From c9536ffa5a549528a4eb517f6c5f50e81b8b509b Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Fri, 14 Mar 2025 15:34:23 +0800 Subject: [PATCH 001/227] drm/hisilicon/hibmc: add dp aux in hibmc drivers maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBSNIA CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=057e77972556aed4a0f1eed7eeb85024d0a22ba1 ---------------------------------------------------------------------- Add dp aux read/write functions. They are basic functions and will be used later. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Reviewed-by: Tian Tao Link: https://patchwork.freedesktop.org/patch/msgid/20250103093824.1963816-2-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/Makefile | 3 +- drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c | 164 +++++++++++++++++++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 42 +++++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 27 +++ 4 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile index d25c75e60d3d4..8770ec6dfffd1 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o +hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \ + dp/dp_aux.o obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c new file mode 100644 index 0000000000000..0a903cce1fa95 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (c) 2024 Hisilicon Limited. + +#include +#include +#include +#include +#include +#include "dp_comm.h" +#include "dp_reg.h" + +#define HIBMC_AUX_CMD_REQ_LEN GENMASK(7, 4) +#define HIBMC_AUX_CMD_ADDR GENMASK(27, 8) +#define HIBMC_AUX_CMD_I2C_ADDR_ONLY BIT(28) +#define HIBMC_BYTES_IN_U32 4 +#define HIBMC_AUX_I2C_WRITE_SUCCESS 0x1 +#define HIBMC_DP_MIN_PULSE_NUM 0x9 +#define BITS_IN_U8 8 + +static inline void hibmc_dp_aux_reset(struct hibmc_dp_dev *dp) +{ + hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_RST_CTRL, HIBMC_DP_CFG_AUX_RST_N, 0x0); + usleep_range(10, 15); + hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_RST_CTRL, HIBMC_DP_CFG_AUX_RST_N, 0x1); +} + +static void hibmc_dp_aux_read_data(struct hibmc_dp_dev *dp, u8 *buf, u8 size) +{ + u32 reg_num; + u32 value; + u32 num; + u8 i, j; + + reg_num = DIV_ROUND_UP(size, HIBMC_BYTES_IN_U32); + for (i = 0; i < reg_num; i++) { + /* number of bytes read from a single register */ + num = min(size - i * HIBMC_BYTES_IN_U32, HIBMC_BYTES_IN_U32); + value = readl(dp->base + HIBMC_DP_AUX_RD_DATA0 + i * HIBMC_BYTES_IN_U32); + /* convert the 32-bit value of the register to the buffer. */ + for (j = 0; j < num; j++) + buf[i * HIBMC_BYTES_IN_U32 + j] = value >> (j * BITS_IN_U8); + } +} + +static void hibmc_dp_aux_write_data(struct hibmc_dp_dev *dp, u8 *buf, u8 size) +{ + u32 reg_num; + u32 value; + u32 num; + u8 i, j; + + reg_num = DIV_ROUND_UP(size, HIBMC_BYTES_IN_U32); + for (i = 0; i < reg_num; i++) { + /* number of bytes written to a single register */ + num = min_t(u8, size - i * HIBMC_BYTES_IN_U32, HIBMC_BYTES_IN_U32); + value = 0; + /* obtain the 32-bit value written to a single register. */ + for (j = 0; j < num; j++) + value |= buf[i * HIBMC_BYTES_IN_U32 + j] << (j * BITS_IN_U8); + /* writing data to a single register */ + writel(value, dp->base + HIBMC_DP_AUX_WR_DATA0 + i * HIBMC_BYTES_IN_U32); + } +} + +static u32 hibmc_dp_aux_build_cmd(const struct drm_dp_aux_msg *msg) +{ + u32 aux_cmd = msg->request; + + if (msg->size) + aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_REQ_LEN, (msg->size - 1)); + else + aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_I2C_ADDR_ONLY, 1); + + aux_cmd |= FIELD_PREP(HIBMC_AUX_CMD_ADDR, msg->address); + + return aux_cmd; +} + +/* ret >= 0, ret is size; ret < 0, ret is err code */ +static int hibmc_dp_aux_parse_xfer(struct hibmc_dp_dev *dp, struct drm_dp_aux_msg *msg) +{ + u32 buf_data_cnt; + u32 aux_status; + + aux_status = readl(dp->base + HIBMC_DP_AUX_STATUS); + msg->reply = FIELD_GET(HIBMC_DP_CFG_AUX_STATUS, aux_status); + + if (aux_status & HIBMC_DP_CFG_AUX_TIMEOUT) + return -ETIMEDOUT; + + /* only address */ + if (!msg->size) + return 0; + + if (msg->reply != DP_AUX_NATIVE_REPLY_ACK) + return -EIO; + + buf_data_cnt = FIELD_GET(HIBMC_DP_CFG_AUX_READY_DATA_BYTE, aux_status); + + switch (msg->request) { + case DP_AUX_NATIVE_WRITE: + return msg->size; + case DP_AUX_I2C_WRITE | DP_AUX_I2C_MOT: + if (buf_data_cnt == HIBMC_AUX_I2C_WRITE_SUCCESS) + return msg->size; + else + return FIELD_GET(HIBMC_DP_CFG_AUX, aux_status); + case DP_AUX_NATIVE_READ: + case DP_AUX_I2C_READ | DP_AUX_I2C_MOT: + buf_data_cnt--; + if (buf_data_cnt != msg->size) { + /* only the successful part of data is read */ + return -EBUSY; + } + + /* all data is successfully read */ + hibmc_dp_aux_read_data(dp, msg->buffer, msg->size); + return msg->size; + default: + return -EINVAL; + } +} + +/* ret >= 0 ,ret is size; ret < 0, ret is err code */ +static ssize_t hibmc_dp_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) +{ + struct hibmc_dp_dev *dp = container_of(aux, struct hibmc_dp_dev, aux); + u32 aux_cmd; + int ret; + u32 val; /* val will be assigned at the beginning of readl_poll_timeout function */ + + writel(0, dp->base + HIBMC_DP_AUX_WR_DATA0); + writel(0, dp->base + HIBMC_DP_AUX_WR_DATA1); + writel(0, dp->base + HIBMC_DP_AUX_WR_DATA2); + writel(0, dp->base + HIBMC_DP_AUX_WR_DATA3); + + hibmc_dp_aux_write_data(dp, msg->buffer, msg->size); + + aux_cmd = hibmc_dp_aux_build_cmd(msg); + writel(aux_cmd, dp->base + HIBMC_DP_AUX_CMD_ADDR); + + /* enable aux transfer */ + hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_REQ, 0x1); + ret = readl_poll_timeout(dp->base + HIBMC_DP_AUX_REQ, val, + !(val & HIBMC_DP_CFG_AUX_REQ), 50, 5000); + if (ret) { + hibmc_dp_aux_reset(dp); + return ret; + } + + return hibmc_dp_aux_parse_xfer(dp, msg); +} + +void hibmc_dp_aux_init(struct hibmc_dp_dev *dp) +{ + hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0); + hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1); + hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_MIN_PULSE_NUM, + HIBMC_DP_MIN_PULSE_NUM); + + dp->aux.transfer = hibmc_dp_aux_xfer; + dp->aux.is_remote = 0; + drm_dp_aux_init(&dp->aux); +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h new file mode 100644 index 0000000000000..97a4d5e2ca332 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) 2024 Hisilicon Limited. */ + +#ifndef DP_COMM_H +#define DP_COMM_H + +#include +#include +#include +#include +#include +#include +#include +#include + +struct hibmc_dp_dev { + struct drm_dp_aux aux; + struct drm_device *dev; + void __iomem *base; + struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */ +}; + +#define dp_field_modify(reg_value, mask, val) \ + do { \ + (reg_value) &= ~(mask); \ + (reg_value) |= FIELD_PREP(mask, val); \ + } while (0) \ + +#define hibmc_dp_reg_write_field(dp, offset, mask, val) \ + do { \ + typeof(dp) _dp = dp; \ + typeof(_dp->base) addr = (_dp->base + (offset)); \ + mutex_lock(&_dp->lock); \ + u32 reg_value = readl(addr); \ + dp_field_modify(reg_value, mask, val); \ + writel(reg_value, addr); \ + mutex_unlock(&_dp->lock); \ + } while (0) + +void hibmc_dp_aux_init(struct hibmc_dp_dev *dp); + +#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h new file mode 100644 index 0000000000000..f3e6781e111a1 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) 2024 Hisilicon Limited. */ + +#ifndef DP_REG_H +#define DP_REG_H + +#define HIBMC_DP_AUX_CMD_ADDR 0x50 +#define HIBMC_DP_AUX_WR_DATA0 0x54 +#define HIBMC_DP_AUX_WR_DATA1 0x58 +#define HIBMC_DP_AUX_WR_DATA2 0x5c +#define HIBMC_DP_AUX_WR_DATA3 0x60 +#define HIBMC_DP_AUX_RD_DATA0 0x64 +#define HIBMC_DP_AUX_REQ 0x74 +#define HIBMC_DP_AUX_STATUS 0x78 +#define HIBMC_DP_DPTX_RST_CTRL 0x700 + +#define HIBMC_DP_CFG_AUX_SYNC_LEN_SEL BIT(1) +#define HIBMC_DP_CFG_AUX_TIMER_TIMEOUT BIT(2) +#define HIBMC_DP_CFG_AUX_MIN_PULSE_NUM GENMASK(13, 9) +#define HIBMC_DP_CFG_AUX_REQ BIT(0) +#define HIBMC_DP_CFG_AUX_RST_N BIT(4) +#define HIBMC_DP_CFG_AUX_TIMEOUT BIT(0) +#define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12) +#define HIBMC_DP_CFG_AUX GENMASK(24, 17) +#define HIBMC_DP_CFG_AUX_STATUS GENMASK(11, 4) + +#endif From 9185d84c76337059607dad6932668fbad5d8fdb9 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Fri, 14 Mar 2025 15:34:24 +0800 Subject: [PATCH 002/227] drm/hisilicon/hibmc: add dp link moduel in hibmc drivers maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBSNIA CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=54063d86e0369f53f180137e5e889bc19cd9015b ---------------------------------------------------------------------- Add link training process functions in this moduel. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Reviewed-by: Tian Tao Link: https://patchwork.freedesktop.org/patch/msgid/20250103093824.1963816-3-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +- drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 21 ++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c | 332 +++++++++++++++++++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 8 + 4 files changed, 362 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile index 8770ec6dfffd1..94d77da88bbfc 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \ - dp/dp_aux.o + dp/dp_aux.o dp/dp_link.o obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h index 97a4d5e2ca332..29d0bdc478506 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h @@ -13,11 +13,31 @@ #include #include +#define HIBMC_DP_LANE_NUM_MAX 2 + +struct hibmc_link_status { + bool clock_recovered; + bool channel_equalized; +}; + +struct hibmc_link_cap { + u8 link_rate; + u8 lanes; +}; + +struct hibmc_dp_link { + struct hibmc_link_status status; + u8 train_set[HIBMC_DP_LANE_NUM_MAX]; + struct hibmc_link_cap cap; +}; + struct hibmc_dp_dev { struct drm_dp_aux aux; struct drm_device *dev; void __iomem *base; struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */ + struct hibmc_dp_link link; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; }; #define dp_field_modify(reg_value, mask, val) \ @@ -38,5 +58,6 @@ struct hibmc_dp_dev { } while (0) void hibmc_dp_aux_init(struct hibmc_dp_dev *dp); +int hibmc_dp_link_training(struct hibmc_dp_dev *dp); #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c new file mode 100644 index 0000000000000..f6355c16cc0ab --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (c) 2024 Hisilicon Limited. + +#include +#include +#include +#include "dp_comm.h" +#include "dp_reg.h" + +#define HIBMC_EQ_MAX_RETRY 5 + +static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp) +{ + u8 buf[2]; + int ret; + + /* DP 2 lane */ + hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_LANE_DATA_EN, + dp->link.cap.lanes == 0x2 ? 0x3 : 0x1); + hibmc_dp_reg_write_field(dp, HIBMC_DP_DPTX_GCTL0, HIBMC_DP_CFG_PHY_LANE_NUM, + dp->link.cap.lanes == 0x2 ? 0x1 : 0); + + /* enhanced frame */ + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_FRAME_MODE, 0x1); + + /* set rate and lane count */ + buf[0] = dp->link.cap.link_rate; + buf[1] = DP_LANE_COUNT_ENHANCED_FRAME_EN | dp->link.cap.lanes; + ret = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, sizeof(buf)); + if (ret != sizeof(buf)) { + drm_dbg_dp(dp->dev, "dp aux write link rate and lanes failed, ret: %d\n", ret); + return ret >= 0 ? -EIO : ret; + } + + /* set 8b/10b and downspread */ + buf[0] = DP_SPREAD_AMP_0_5; + buf[1] = DP_SET_ANSI_8B10B; + ret = drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, buf, sizeof(buf)); + if (ret != sizeof(buf)) { + drm_dbg_dp(dp->dev, "dp aux write 8b/10b and downspread failed, ret: %d\n", ret); + return ret >= 0 ? -EIO : ret; + } + + ret = drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd); + if (ret) + drm_err(dp->dev, "dp aux read dpcd failed, ret: %d\n", ret); + + return ret; +} + +static int hibmc_dp_link_set_pattern(struct hibmc_dp_dev *dp, int pattern) +{ + int ret; + u8 val; + u8 buf; + + buf = (u8)pattern; + if (pattern != DP_TRAINING_PATTERN_DISABLE && pattern != DP_TRAINING_PATTERN_4) { + buf |= DP_LINK_SCRAMBLING_DISABLE; + hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_SCRAMBLE_EN, 0x1); + } else { + hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_SCRAMBLE_EN, 0); + } + + switch (pattern) { + case DP_TRAINING_PATTERN_DISABLE: + val = 0; + break; + case DP_TRAINING_PATTERN_1: + val = 1; + break; + case DP_TRAINING_PATTERN_2: + val = 2; + break; + case DP_TRAINING_PATTERN_3: + val = 3; + break; + case DP_TRAINING_PATTERN_4: + val = 4; + break; + default: + return -EINVAL; + } + + hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_PAT_SEL, val); + + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET, &buf, sizeof(buf)); + if (ret != sizeof(buf)) { + drm_dbg_dp(dp->dev, "dp aux write training pattern set failed\n"); + return ret >= 0 ? -EIO : ret; + } + + return 0; +} + +static int hibmc_dp_link_training_cr_pre(struct hibmc_dp_dev *dp) +{ + u8 *train_set = dp->link.train_set; + int ret; + u8 i; + + ret = hibmc_dp_link_training_configure(dp); + if (ret) + return ret; + + ret = hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_1); + if (ret) + return ret; + + for (i = 0; i < dp->link.cap.lanes; i++) + train_set[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL_2; + + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->link.cap.lanes); + if (ret != dp->link.cap.lanes) { + drm_dbg_dp(dp->dev, "dp aux write training lane set failed\n"); + return ret >= 0 ? -EIO : ret; + } + + return 0; +} + +static bool hibmc_dp_link_get_adjust_train(struct hibmc_dp_dev *dp, + u8 lane_status[DP_LINK_STATUS_SIZE]) +{ + u8 train_set[HIBMC_DP_LANE_NUM_MAX] = {0}; + u8 lane; + + for (lane = 0; lane < dp->link.cap.lanes; lane++) + train_set[lane] = drm_dp_get_adjust_request_voltage(lane_status, lane) | + drm_dp_get_adjust_request_pre_emphasis(lane_status, lane); + + if (memcmp(dp->link.train_set, train_set, HIBMC_DP_LANE_NUM_MAX)) { + memcpy(dp->link.train_set, train_set, HIBMC_DP_LANE_NUM_MAX); + return true; + } + + return false; +} + +static inline int hibmc_dp_link_reduce_rate(struct hibmc_dp_dev *dp) +{ + switch (dp->link.cap.link_rate) { + case DP_LINK_BW_2_7: + dp->link.cap.link_rate = DP_LINK_BW_1_62; + return 0; + case DP_LINK_BW_5_4: + dp->link.cap.link_rate = DP_LINK_BW_2_7; + return 0; + case DP_LINK_BW_8_1: + dp->link.cap.link_rate = DP_LINK_BW_5_4; + return 0; + default: + return -EINVAL; + } +} + +static inline int hibmc_dp_link_reduce_lane(struct hibmc_dp_dev *dp) +{ + switch (dp->link.cap.lanes) { + case 0x2: + dp->link.cap.lanes--; + break; + case 0x1: + drm_err(dp->dev, "dp link training reduce lane failed, already reach minimum\n"); + return -EIO; + default: + return -EINVAL; + } + + return 0; +} + +static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp) +{ + u8 lane_status[DP_LINK_STATUS_SIZE] = {0}; + bool level_changed; + u32 voltage_tries; + u32 cr_tries; + int ret; + + /* + * DP 1.4 spec define 10 for maxtries value, for pre DP 1.4 version set a limit of 80 + * (4 voltage levels x 4 preemphasis levels x 5 identical voltage retries) + */ + + voltage_tries = 1; + for (cr_tries = 0; cr_tries < 80; cr_tries++) { + drm_dp_link_train_clock_recovery_delay(&dp->aux, dp->dpcd); + + ret = drm_dp_dpcd_read_link_status(&dp->aux, lane_status); + if (ret != DP_LINK_STATUS_SIZE) { + drm_err(dp->dev, "Get lane status failed\n"); + return ret; + } + + if (drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) { + drm_dbg_dp(dp->dev, "dp link training cr done\n"); + dp->link.status.clock_recovered = true; + return 0; + } + + if (voltage_tries == 5) { + drm_dbg_dp(dp->dev, "same voltage tries 5 times\n"); + dp->link.status.clock_recovered = false; + return 0; + } + + level_changed = hibmc_dp_link_get_adjust_train(dp, lane_status); + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set, + dp->link.cap.lanes); + if (ret != dp->link.cap.lanes) { + drm_dbg_dp(dp->dev, "Update link training failed\n"); + return ret >= 0 ? -EIO : ret; + } + + voltage_tries = level_changed ? 1 : voltage_tries + 1; + } + + drm_err(dp->dev, "dp link training clock recovery 80 times failed\n"); + dp->link.status.clock_recovered = false; + + return 0; +} + +static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp) +{ + u8 lane_status[DP_LINK_STATUS_SIZE] = {0}; + u8 eq_tries; + int ret; + + ret = hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_2); + if (ret) + return ret; + + for (eq_tries = 0; eq_tries < HIBMC_EQ_MAX_RETRY; eq_tries++) { + drm_dp_link_train_channel_eq_delay(&dp->aux, dp->dpcd); + + ret = drm_dp_dpcd_read_link_status(&dp->aux, lane_status); + if (ret != DP_LINK_STATUS_SIZE) { + drm_err(dp->dev, "get lane status failed\n"); + break; + } + + if (!drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) { + drm_dbg_dp(dp->dev, "clock recovery check failed\n"); + drm_dbg_dp(dp->dev, "cannot continue channel equalization\n"); + dp->link.status.clock_recovered = false; + break; + } + + if (drm_dp_channel_eq_ok(lane_status, dp->link.cap.lanes)) { + dp->link.status.channel_equalized = true; + drm_dbg_dp(dp->dev, "dp link training eq done\n"); + break; + } + + hibmc_dp_link_get_adjust_train(dp, lane_status); + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, + dp->link.train_set, dp->link.cap.lanes); + if (ret != dp->link.cap.lanes) { + drm_dbg_dp(dp->dev, "Update link training failed\n"); + ret = (ret >= 0) ? -EIO : ret; + break; + } + } + + if (eq_tries == HIBMC_EQ_MAX_RETRY) + drm_err(dp->dev, "channel equalization failed %u times\n", eq_tries); + + hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE); + + return ret < 0 ? ret : 0; +} + +static int hibmc_dp_link_downgrade_training_cr(struct hibmc_dp_dev *dp) +{ + if (hibmc_dp_link_reduce_rate(dp)) + return hibmc_dp_link_reduce_lane(dp); + + return 0; +} + +static int hibmc_dp_link_downgrade_training_eq(struct hibmc_dp_dev *dp) +{ + if ((dp->link.status.clock_recovered && !dp->link.status.channel_equalized)) { + if (!hibmc_dp_link_reduce_lane(dp)) + return 0; + } + + return hibmc_dp_link_reduce_rate(dp); +} + +int hibmc_dp_link_training(struct hibmc_dp_dev *dp) +{ + struct hibmc_dp_link *link = &dp->link; + int ret; + + while (true) { + ret = hibmc_dp_link_training_cr_pre(dp); + if (ret) + goto err; + + ret = hibmc_dp_link_training_cr(dp); + if (ret) + goto err; + + if (!link->status.clock_recovered) { + ret = hibmc_dp_link_downgrade_training_cr(dp); + if (ret) + goto err; + continue; + } + + ret = hibmc_dp_link_training_channel_eq(dp); + if (ret) + goto err; + + if (!link->status.channel_equalized) { + ret = hibmc_dp_link_downgrade_training_eq(dp); + if (ret) + goto err; + continue; + } + + return 0; + } + +err: + hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_DISABLE); + + return ret; +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index f3e6781e111a1..0bd308eccdc50 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -12,16 +12,24 @@ #define HIBMC_DP_AUX_RD_DATA0 0x64 #define HIBMC_DP_AUX_REQ 0x74 #define HIBMC_DP_AUX_STATUS 0x78 +#define HIBMC_DP_PHYIF_CTRL0 0xa0 +#define HIBMC_DP_VIDEO_CTRL 0x100 #define HIBMC_DP_DPTX_RST_CTRL 0x700 +#define HIBMC_DP_DPTX_GCTL0 0x708 #define HIBMC_DP_CFG_AUX_SYNC_LEN_SEL BIT(1) #define HIBMC_DP_CFG_AUX_TIMER_TIMEOUT BIT(2) +#define HIBMC_DP_CFG_STREAM_FRAME_MODE BIT(6) #define HIBMC_DP_CFG_AUX_MIN_PULSE_NUM GENMASK(13, 9) +#define HIBMC_DP_CFG_LANE_DATA_EN GENMASK(11, 8) +#define HIBMC_DP_CFG_PHY_LANE_NUM GENMASK(2, 1) #define HIBMC_DP_CFG_AUX_REQ BIT(0) #define HIBMC_DP_CFG_AUX_RST_N BIT(4) #define HIBMC_DP_CFG_AUX_TIMEOUT BIT(0) #define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12) #define HIBMC_DP_CFG_AUX GENMASK(24, 17) #define HIBMC_DP_CFG_AUX_STATUS GENMASK(11, 4) +#define HIBMC_DP_CFG_SCRAMBLE_EN BIT(0) +#define HIBMC_DP_CFG_PAT_SEL GENMASK(7, 4) #endif From 460e28b73306f4f64019adb0e8b47be8fed66746 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Fri, 14 Mar 2025 15:34:25 +0800 Subject: [PATCH 003/227] drm/hisilicon/hibmc: add dp hw moduel in hibmc driver maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBSNIA CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=94ee73ee30208f3d92b2f2a4f7d3346ba56245bf ---------------------------------------------------------------------- Build a dp level that hibmc driver can enable dp by calling their functions. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Reviewed-by: Tian Tao Link: https://patchwork.freedesktop.org/patch/msgid/20250103093824.1963816-4-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +- .../gpu/drm/hisilicon/hibmc/dp/dp_config.h | 19 ++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 220 ++++++++++++++++++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 28 +++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 41 ++++ 5 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile index 94d77da88bbfc..214228052ccf4 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \ - dp/dp_aux.o dp/dp_link.o + dp/dp_aux.o dp/dp_link.o dp/dp_hw.o obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h new file mode 100644 index 0000000000000..74dd9956144e4 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) 2024 Hisilicon Limited. */ + +#ifndef DP_CONFIG_H +#define DP_CONFIG_H + +#define HIBMC_DP_BPP 24 +#define HIBMC_DP_SYMBOL_PER_FCLK 4 +#define HIBMC_DP_MSA1 0x20 +#define HIBMC_DP_MSA2 0x845c00 +#define HIBMC_DP_OFFSET 0x1e0000 +#define HIBMC_DP_HDCP 0x2 +#define HIBMC_DP_INT_RST 0xffff +#define HIBMC_DP_DPTX_RST 0x3ff +#define HIBMC_DP_CLK_EN 0x7 +#define HIBMC_DP_SYNC_EN_MASK 0x3 +#define HIBMC_DP_LINK_RATE_CAL 27 + +#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c new file mode 100644 index 0000000000000..a8d543881c099 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (c) 2024 Hisilicon Limited. + +#include +#include +#include "dp_config.h" +#include "dp_comm.h" +#include "dp_reg.h" +#include "dp_hw.h" + +static void hibmc_dp_set_tu(struct hibmc_dp_dev *dp, struct drm_display_mode *mode) +{ + u32 tu_symbol_frac_size; + u32 tu_symbol_size; + u32 rate_ks; + u8 lane_num; + u32 value; + u32 bpp; + + lane_num = dp->link.cap.lanes; + if (lane_num == 0) { + drm_err(dp->dev, "set tu failed, lane num cannot be 0!\n"); + return; + } + + bpp = HIBMC_DP_BPP; + rate_ks = dp->link.cap.link_rate * HIBMC_DP_LINK_RATE_CAL; + value = (mode->clock * bpp * 5) / (61 * lane_num * rate_ks); + + if (value % 10 == 9) { /* 9 carry */ + tu_symbol_size = value / 10 + 1; + tu_symbol_frac_size = 0; + } else { + tu_symbol_size = value / 10; + tu_symbol_frac_size = value % 10 + 1; + } + + drm_dbg_dp(dp->dev, "tu value: %u.%u value: %u\n", + tu_symbol_size, tu_symbol_frac_size, value); + + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET, + HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE, tu_symbol_size); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET, + HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE, tu_symbol_frac_size); +} + +static void hibmc_dp_set_sst(struct hibmc_dp_dev *dp, struct drm_display_mode *mode) +{ + u32 hblank_size; + u32 htotal_size; + u32 htotal_int; + u32 hblank_int; + u32 fclk; /* flink_clock */ + + fclk = dp->link.cap.link_rate * HIBMC_DP_LINK_RATE_CAL; + + /* Considering the effect of spread spectrum, the value may be deviated. + * The coefficient (0.9947) is used to offset the deviation. + */ + htotal_int = mode->htotal * 9947 / 10000; + htotal_size = htotal_int * fclk / (HIBMC_DP_SYMBOL_PER_FCLK * (mode->clock / 1000)); + + hblank_int = mode->htotal - mode->hdisplay - mode->hdisplay * 53 / 10000; + hblank_size = hblank_int * fclk * 9947 / + (mode->clock * 10 * HIBMC_DP_SYMBOL_PER_FCLK); + + drm_dbg_dp(dp->dev, "h_active %u v_active %u htotal_size %u hblank_size %u", + mode->hdisplay, mode->vdisplay, htotal_size, hblank_size); + drm_dbg_dp(dp->dev, "flink_clock %u pixel_clock %d", fclk, mode->clock / 1000); + + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE, + HIBMC_DP_CFG_STREAM_HTOTAL_SIZE, htotal_size); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE, + HIBMC_DP_CFG_STREAM_HBLANK_SIZE, hblank_size); +} + +static void hibmc_dp_link_cfg(struct hibmc_dp_dev *dp, struct drm_display_mode *mode) +{ + u32 timing_delay; + u32 vblank; + u32 hstart; + u32 vstart; + + vblank = mode->vtotal - mode->vdisplay; + timing_delay = mode->htotal - mode->hsync_start; + hstart = mode->htotal - mode->hsync_start; + vstart = mode->vtotal - mode->vsync_start; + + hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG0, + HIBMC_DP_CFG_TIMING_GEN0_HBLANK, mode->htotal - mode->hdisplay); + hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG0, + HIBMC_DP_CFG_TIMING_GEN0_HACTIVE, mode->hdisplay); + + hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG2, + HIBMC_DP_CFG_TIMING_GEN0_VBLANK, vblank); + hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG2, + HIBMC_DP_CFG_TIMING_GEN0_VACTIVE, mode->vdisplay); + hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_GEN_CONFIG3, + HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH, + mode->vsync_start - mode->vdisplay); + + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG0, + HIBMC_DP_CFG_STREAM_HACTIVE, mode->hdisplay); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG0, + HIBMC_DP_CFG_STREAM_HBLANK, mode->htotal - mode->hdisplay); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG2, + HIBMC_DP_CFG_STREAM_HSYNC_WIDTH, + mode->hsync_end - mode->hsync_start); + + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG1, + HIBMC_DP_CFG_STREAM_VACTIVE, mode->vdisplay); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG1, + HIBMC_DP_CFG_STREAM_VBLANK, vblank); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG3, + HIBMC_DP_CFG_STREAM_VFRONT_PORCH, + mode->vsync_start - mode->vdisplay); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CONFIG3, + HIBMC_DP_CFG_STREAM_VSYNC_WIDTH, + mode->vsync_end - mode->vsync_start); + + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_MSA0, + HIBMC_DP_CFG_STREAM_VSTART, vstart); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_MSA0, + HIBMC_DP_CFG_STREAM_HSTART, hstart); + + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_VSYNC_POLARITY, + mode->flags & DRM_MODE_FLAG_PVSYNC ? 1 : 0); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_HSYNC_POLARITY, + mode->flags & DRM_MODE_FLAG_PHSYNC ? 1 : 0); + + /* MSA mic 0 and 1 */ + writel(HIBMC_DP_MSA1, dp->base + HIBMC_DP_VIDEO_MSA1); + writel(HIBMC_DP_MSA2, dp->base + HIBMC_DP_VIDEO_MSA2); + + hibmc_dp_set_tu(dp, mode); + + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_RGB_ENABLE, 0x1); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_VIDEO_MAPPING, 0); + + /* divide 2: up even */ + if (timing_delay % 2) + timing_delay++; + + hibmc_dp_reg_write_field(dp, HIBMC_DP_TIMING_MODEL_CTRL, + HIBMC_DP_CFG_PIXEL_NUM_TIMING_MODE_SEL1, timing_delay); + + hibmc_dp_set_sst(dp, mode); +} + +int hibmc_dp_hw_init(struct hibmc_dp *dp) +{ + struct drm_device *drm_dev = dp->drm_dev; + struct hibmc_dp_dev *dp_dev; + + dp_dev = devm_kzalloc(drm_dev->dev, sizeof(struct hibmc_dp_dev), GFP_KERNEL); + if (!dp_dev) + return -ENOMEM; + + mutex_init(&dp_dev->lock); + + dp->dp_dev = dp_dev; + + dp_dev->dev = drm_dev; + dp_dev->base = dp->mmio + HIBMC_DP_OFFSET; + + hibmc_dp_aux_init(dp_dev); + + dp_dev->link.cap.lanes = 0x2; + dp_dev->link.cap.link_rate = DP_LINK_BW_2_7; + + /* hdcp data */ + writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG); + /* int init */ + writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE); + writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS); + /* rst */ + writel(HIBMC_DP_DPTX_RST, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL); + /* clock enable */ + writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL); + + return 0; +} + +void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable) +{ + struct hibmc_dp_dev *dp_dev = dp->dp_dev; + + if (enable) { + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, BIT(0), 0x1); + writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, BIT(10), 0x1); + writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); + } else { + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, BIT(10), 0); + writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, BIT(0), 0); + writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); + } + + msleep(50); +} + +int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode) +{ + struct hibmc_dp_dev *dp_dev = dp->dp_dev; + int ret; + + if (!dp_dev->link.status.channel_equalized) { + ret = hibmc_dp_link_training(dp_dev); + if (ret) { + drm_err(dp->drm_dev, "dp link training failed, ret: %d\n", ret); + return ret; + } + } + + hibmc_dp_display_en(dp, false); + hibmc_dp_link_cfg(dp_dev, mode); + + return 0; +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h new file mode 100644 index 0000000000000..4dc13b3d9875f --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) 2024 Hisilicon Limited. */ + +#ifndef DP_KAPI_H +#define DP_KAPI_H + +#include +#include +#include +#include +#include +#include + +struct hibmc_dp_dev; + +struct hibmc_dp { + struct hibmc_dp_dev *dp_dev; + struct drm_device *drm_dev; + struct drm_encoder encoder; + struct drm_connector connector; + void __iomem *mmio; +}; + +int hibmc_dp_hw_init(struct hibmc_dp *dp); +int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode); +void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable); + +#endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index 0bd308eccdc50..4a515c726d524 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -14,8 +14,26 @@ #define HIBMC_DP_AUX_STATUS 0x78 #define HIBMC_DP_PHYIF_CTRL0 0xa0 #define HIBMC_DP_VIDEO_CTRL 0x100 +#define HIBMC_DP_VIDEO_CONFIG0 0x104 +#define HIBMC_DP_VIDEO_CONFIG1 0x108 +#define HIBMC_DP_VIDEO_CONFIG2 0x10c +#define HIBMC_DP_VIDEO_CONFIG3 0x110 +#define HIBMC_DP_VIDEO_PACKET 0x114 +#define HIBMC_DP_VIDEO_MSA0 0x118 +#define HIBMC_DP_VIDEO_MSA1 0x11c +#define HIBMC_DP_VIDEO_MSA2 0x120 +#define HIBMC_DP_VIDEO_HORIZONTAL_SIZE 0X124 +#define HIBMC_DP_TIMING_GEN_CONFIG0 0x26c +#define HIBMC_DP_TIMING_GEN_CONFIG2 0x274 +#define HIBMC_DP_TIMING_GEN_CONFIG3 0x278 +#define HIBMC_DP_HDCP_CFG 0x600 #define HIBMC_DP_DPTX_RST_CTRL 0x700 +#define HIBMC_DP_DPTX_CLK_CTRL 0x704 #define HIBMC_DP_DPTX_GCTL0 0x708 +#define HIBMC_DP_INTR_ENABLE 0x720 +#define HIBMC_DP_INTR_ORIGINAL_STATUS 0x728 +#define HIBMC_DP_TIMING_MODEL_CTRL 0x884 +#define HIBMC_DP_TIMING_SYNC_CTRL 0xFF0 #define HIBMC_DP_CFG_AUX_SYNC_LEN_SEL BIT(1) #define HIBMC_DP_CFG_AUX_TIMER_TIMEOUT BIT(2) @@ -31,5 +49,28 @@ #define HIBMC_DP_CFG_AUX_STATUS GENMASK(11, 4) #define HIBMC_DP_CFG_SCRAMBLE_EN BIT(0) #define HIBMC_DP_CFG_PAT_SEL GENMASK(7, 4) +#define HIBMC_DP_CFG_TIMING_GEN0_HACTIVE GENMASK(31, 16) +#define HIBMC_DP_CFG_TIMING_GEN0_HBLANK GENMASK(15, 0) +#define HIBMC_DP_CFG_TIMING_GEN0_VACTIVE GENMASK(31, 16) +#define HIBMC_DP_CFG_TIMING_GEN0_VBLANK GENMASK(15, 0) +#define HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_HACTIVE GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_HBLANK GENMASK(15, 0) +#define HIBMC_DP_CFG_STREAM_HSYNC_WIDTH GENMASK(15, 0) +#define HIBMC_DP_CFG_STREAM_VACTIVE GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_VBLANK GENMASK(15, 0) +#define HIBMC_DP_CFG_STREAM_VFRONT_PORCH GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_VSYNC_WIDTH GENMASK(15, 0) +#define HIBMC_DP_CFG_STREAM_VSTART GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_HSTART GENMASK(15, 0) +#define HIBMC_DP_CFG_STREAM_VSYNC_POLARITY BIT(8) +#define HIBMC_DP_CFG_STREAM_HSYNC_POLARITY BIT(7) +#define HIBMC_DP_CFG_STREAM_RGB_ENABLE BIT(1) +#define HIBMC_DP_CFG_STREAM_VIDEO_MAPPING GENMASK(5, 2) +#define HIBMC_DP_CFG_PIXEL_NUM_TIMING_MODE_SEL1 GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE GENMASK(5, 0) +#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE GENMASK(9, 6) +#define HIBMC_DP_CFG_STREAM_HTOTAL_SIZE GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_HBLANK_SIZE GENMASK(15, 0) #endif From b0db20fe519e20b5cacaf9ebfb8b65afe1eb98a8 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Fri, 14 Mar 2025 15:34:26 +0800 Subject: [PATCH 004/227] drm/hisilicon/hibmc: refactored struct hibmc_drm_private maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBSNIA CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=587013d72c1a217ced9f42a9a08c8013052cabfc ---------------------------------------------------------------------- Refactored struct hibmc_drm_private to separate VGA module from generic struct. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Reviewed-by: Tian Tao Link: https://patchwork.freedesktop.org/patch/msgid/20250103093824.1963816-5-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 16 +++---- .../gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c | 42 +++++++++---------- .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 20 ++++----- 3 files changed, 38 insertions(+), 40 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index 7d263f4d70784..ad041c7213a93 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -22,9 +22,10 @@ #include #include -struct hibmc_connector { - struct drm_connector base; - +struct hibmc_vdac { + struct drm_device *dev; + struct drm_encoder encoder; + struct drm_connector connector; struct i2c_adapter adapter; struct i2c_algo_bit_data bit_data; }; @@ -40,13 +41,12 @@ struct hibmc_drm_private { struct drm_device dev; struct drm_plane primary_plane; struct drm_crtc crtc; - struct drm_encoder encoder; - struct hibmc_connector connector; + struct hibmc_vdac vdac; }; -static inline struct hibmc_connector *to_hibmc_connector(struct drm_connector *connector) +static inline struct hibmc_vdac *to_hibmc_vdac(struct drm_connector *connector) { - return container_of(connector, struct hibmc_connector, base); + return container_of(connector, struct hibmc_vdac, connector); } static inline struct hibmc_drm_private *to_hibmc_drm_private(struct drm_device *dev) @@ -63,6 +63,6 @@ int hibmc_de_init(struct hibmc_drm_private *priv); int hibmc_vdac_init(struct hibmc_drm_private *priv); int hibmc_mm_init(struct hibmc_drm_private *hibmc); -int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_connector *connector); +int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *connector); #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c index 410bd019bb357..99b3b77b5445f 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c @@ -25,8 +25,8 @@ static void hibmc_set_i2c_signal(void *data, u32 mask, int value) { - struct hibmc_connector *hibmc_connector = data; - struct hibmc_drm_private *priv = to_hibmc_drm_private(hibmc_connector->base.dev); + struct hibmc_vdac *vdac = data; + struct hibmc_drm_private *priv = to_hibmc_drm_private(vdac->connector.dev); u32 tmp_dir = readl(priv->mmio + GPIO_DATA_DIRECTION); if (value) { @@ -45,8 +45,8 @@ static void hibmc_set_i2c_signal(void *data, u32 mask, int value) static int hibmc_get_i2c_signal(void *data, u32 mask) { - struct hibmc_connector *hibmc_connector = data; - struct hibmc_drm_private *priv = to_hibmc_drm_private(hibmc_connector->base.dev); + struct hibmc_vdac *vdac = data; + struct hibmc_drm_private *priv = to_hibmc_drm_private(vdac->connector.dev); u32 tmp_dir = readl(priv->mmio + GPIO_DATA_DIRECTION); if ((tmp_dir & mask) != mask) { @@ -77,23 +77,21 @@ static int hibmc_ddc_getscl(void *data) return hibmc_get_i2c_signal(data, I2C_SCL_MASK); } -int hibmc_ddc_create(struct drm_device *drm_dev, - struct hibmc_connector *connector) +int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *vdac) { - connector->adapter.owner = THIS_MODULE; - connector->adapter.class = I2C_CLASS_DDC; - snprintf(connector->adapter.name, I2C_NAME_SIZE, "HIS i2c bit bus"); - connector->adapter.dev.parent = drm_dev->dev; - i2c_set_adapdata(&connector->adapter, connector); - connector->adapter.algo_data = &connector->bit_data; - - connector->bit_data.udelay = 20; - connector->bit_data.timeout = usecs_to_jiffies(2000); - connector->bit_data.data = connector; - connector->bit_data.setsda = hibmc_ddc_setsda; - connector->bit_data.setscl = hibmc_ddc_setscl; - connector->bit_data.getsda = hibmc_ddc_getsda; - connector->bit_data.getscl = hibmc_ddc_getscl; - - return i2c_bit_add_bus(&connector->adapter); + vdac->adapter.owner = THIS_MODULE; + snprintf(vdac->adapter.name, I2C_NAME_SIZE, "HIS i2c bit bus"); + vdac->adapter.dev.parent = drm_dev->dev; + i2c_set_adapdata(&vdac->adapter, vdac); + vdac->adapter.algo_data = &vdac->bit_data; + + vdac->bit_data.udelay = 20; + vdac->bit_data.timeout = usecs_to_jiffies(2000); + vdac->bit_data.data = vdac; + vdac->bit_data.setsda = hibmc_ddc_setsda; + vdac->bit_data.setscl = hibmc_ddc_setscl; + vdac->bit_data.getsda = hibmc_ddc_getsda; + vdac->bit_data.getscl = hibmc_ddc_getscl; + + return i2c_bit_add_bus(&vdac->adapter); } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index c228091fb0e6a..04a9eac316a17 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -23,9 +23,9 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) { int count; void *edid; - struct hibmc_connector *hibmc_connector = to_hibmc_connector(connector); + struct hibmc_vdac *vdac = to_hibmc_vdac(connector); - edid = drm_get_edid(connector, &hibmc_connector->adapter); + edid = drm_get_edid(connector, &vdac->adapter); if (edid) { drm_connector_update_edid_property(connector, edid); count = drm_add_edid_modes(connector, edid); @@ -45,9 +45,9 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) static void hibmc_connector_destroy(struct drm_connector *connector) { - struct hibmc_connector *hibmc_connector = to_hibmc_connector(connector); + struct hibmc_vdac *vdac = to_hibmc_vdac(connector); - i2c_del_adapter(&hibmc_connector->adapter); + i2c_del_adapter(&vdac->adapter); drm_connector_cleanup(connector); } @@ -87,20 +87,20 @@ static const struct drm_encoder_helper_funcs hibmc_encoder_helper_funcs = { int hibmc_vdac_init(struct hibmc_drm_private *priv) { struct drm_device *dev = &priv->dev; - struct hibmc_connector *hibmc_connector = &priv->connector; - struct drm_encoder *encoder = &priv->encoder; + struct hibmc_vdac *vdac = &priv->vdac; + struct drm_encoder *encoder = &vdac->encoder; struct drm_crtc *crtc = &priv->crtc; - struct drm_connector *connector = &hibmc_connector->base; + struct drm_connector *connector = &vdac->connector; int ret; - ret = hibmc_ddc_create(dev, hibmc_connector); + ret = hibmc_ddc_create(dev, vdac); if (ret) { drm_err(dev, "failed to create ddc: %d\n", ret); return ret; } encoder->possible_crtcs = drm_crtc_mask(crtc); - ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC); + ret = drmm_encoder_init(dev, encoder, NULL, DRM_MODE_ENCODER_DAC, NULL); if (ret) { drm_err(dev, "failed to init encoder: %d\n", ret); return ret; @@ -111,7 +111,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) ret = drm_connector_init_with_ddc(dev, connector, &hibmc_connector_funcs, DRM_MODE_CONNECTOR_VGA, - &hibmc_connector->adapter); + &vdac->adapter); if (ret) { drm_err(dev, "failed to init connector: %d\n", ret); return ret; From bbc4641b1edc2832ac4e51af69bbc2e7edf893c1 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Fri, 14 Mar 2025 15:34:27 +0800 Subject: [PATCH 005/227] drm/hisilicon/hibmc: add dp module in hibmc maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBSNIA CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0ab6ea261c1fe4edbf4d99dfe65d8ebaae905092 ---------------------------------------------------------------------- To support DP interface displaying in hibmc driver. Add a encoder and connector for DP modual. The HPD function and get_edid function will be add in next series, so temporarily using 1024x768 as default in hibmc_dp_connector_get_modes() Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Reviewed-by: Tian Tao Link: https://patchwork.freedesktop.org/patch/msgid/20250103093824.1963816-6-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +- .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 118 ++++++++++++++++++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 14 +++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 3 + 4 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile index 214228052ccf4..95a4ed599d980 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \ - dp/dp_aux.o dp/dp_link.o dp/dp_hw.o + dp/dp_aux.o dp/dp_link.o dp/dp_hw.o hibmc_drm_dp.o obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c new file mode 100644 index 0000000000000..603d6b198a540 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (c) 2024 Hisilicon Limited. + +#include + +#include +#include +#include +#include +#include +#include + +#include "hibmc_drm_drv.h" +#include "dp/dp_hw.h" + +static int hibmc_dp_connector_get_modes(struct drm_connector *connector) +{ + int count; + + count = drm_add_modes_noedid(connector, connector->dev->mode_config.max_width, + connector->dev->mode_config.max_height); + drm_set_preferred_mode(connector, 1024, 768); // temporary implementation + + return count; +} + +static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = { + .get_modes = hibmc_dp_connector_get_modes, +}; + +static const struct drm_connector_funcs hibmc_dp_conn_funcs = { + .reset = drm_atomic_helper_connector_reset, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = drm_connector_cleanup, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static inline int hibmc_dp_prepare(struct hibmc_dp *dp, struct drm_display_mode *mode) +{ + int ret; + + hibmc_dp_display_en(dp, false); + + ret = hibmc_dp_mode_set(dp, mode); + if (ret) + drm_err(dp->drm_dev, "hibmc dp mode set failed: %d\n", ret); + + return ret; +} + +static void hibmc_dp_encoder_enable(struct drm_encoder *drm_encoder, + struct drm_atomic_state *state) +{ + struct hibmc_dp *dp = container_of(drm_encoder, struct hibmc_dp, encoder); + struct drm_display_mode *mode = &drm_encoder->crtc->state->mode; + + if (hibmc_dp_prepare(dp, mode)) + return; + + hibmc_dp_display_en(dp, true); +} + +static void hibmc_dp_encoder_disable(struct drm_encoder *drm_encoder, + struct drm_atomic_state *state) +{ + struct hibmc_dp *dp = container_of(drm_encoder, struct hibmc_dp, encoder); + + hibmc_dp_display_en(dp, false); +} + +static const struct drm_encoder_helper_funcs hibmc_dp_encoder_helper_funcs = { + .atomic_enable = hibmc_dp_encoder_enable, + .atomic_disable = hibmc_dp_encoder_disable, +}; + +int hibmc_dp_init(struct hibmc_drm_private *priv) +{ + struct drm_device *dev = &priv->dev; + struct drm_crtc *crtc = &priv->crtc; + struct hibmc_dp *dp = &priv->dp; + struct drm_connector *connector = &dp->connector; + struct drm_encoder *encoder = &dp->encoder; + int ret; + + dp->mmio = priv->mmio; + dp->drm_dev = dev; + + ret = hibmc_dp_hw_init(&priv->dp); + if (ret) { + drm_err(dev, "hibmc dp hw init failed: %d\n", ret); + return ret; + } + + hibmc_dp_display_en(&priv->dp, false); + + encoder->possible_crtcs = drm_crtc_mask(crtc); + ret = drmm_encoder_init(dev, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL); + if (ret) { + drm_err(dev, "init dp encoder failed: %d\n", ret); + return ret; + } + + drm_encoder_helper_add(encoder, &hibmc_dp_encoder_helper_funcs); + + ret = drm_connector_init(dev, connector, &hibmc_dp_conn_funcs, + DRM_MODE_CONNECTOR_DisplayPort); + if (ret) { + drm_err(dev, "init dp connector failed: %d\n", ret); + return ret; + } + + drm_connector_helper_add(connector, &hibmc_dp_conn_helper_funcs); + + drm_connector_attach_encoder(connector, encoder); + + return 0; +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 610fc8e135f9b..9fa29e542fc4b 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -25,6 +25,10 @@ #include "hibmc_drm_drv.h" #include "hibmc_drm_regs.h" +#define HIBMC_DP_HOST_SERDES_CTRL 0x1f001c +#define HIBMC_DP_HOST_SERDES_CTRL_VAL 0x8a00 +#define HIBMC_DP_HOST_SERDES_CTRL_MASK 0x7ffff + DEFINE_DRM_GEM_FOPS(hibmc_fops); static irqreturn_t hibmc_interrupt(int irq, void *arg) @@ -116,6 +120,14 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) return ret; } + /* if DP existed, init DP */ + if ((readl(priv->mmio + HIBMC_DP_HOST_SERDES_CTRL) & + HIBMC_DP_HOST_SERDES_CTRL_MASK) == HIBMC_DP_HOST_SERDES_CTRL_VAL) { + ret = hibmc_dp_init(priv); + if (ret) + drm_err(dev, "failed to init dp: %d\n", ret); + } + ret = hibmc_vdac_init(priv); if (ret) { drm_err(dev, "failed to init vdac: %d\n", ret); @@ -335,6 +347,8 @@ static int hibmc_pci_probe(struct pci_dev *pdev, goto err_return; } + pci_set_master(pdev); + ret = hibmc_load(dev); if (ret) { drm_err(dev, "failed to load hibmc: %d\n", ret); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index ad041c7213a93..232057676ec50 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -21,6 +21,7 @@ #include #include #include +#include "dp/dp_hw.h" struct hibmc_vdac { struct drm_device *dev; @@ -42,6 +43,7 @@ struct hibmc_drm_private { struct drm_plane primary_plane; struct drm_crtc crtc; struct hibmc_vdac vdac; + struct hibmc_dp dp; }; static inline struct hibmc_vdac *to_hibmc_vdac(struct drm_connector *connector) @@ -64,5 +66,6 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv); int hibmc_mm_init(struct hibmc_drm_private *hibmc); int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *connector); +int hibmc_dp_init(struct hibmc_drm_private *priv); #endif From c386f2f2a19fddad699532adc5fe6aeb1cc646d9 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Mon, 11 Jul 2022 19:38:33 +0200 Subject: [PATCH 006/227] drm/encoder: Introduce drmm_encoder_init The DRM-managed function to register an encoder is drmm_encoder_alloc() and its variants, which will allocate the underlying structure and initialisation the encoder. However, we might want to separate the structure creation and the encoder initialisation, for example if the structure is shared across multiple DRM entities, for example an encoder and a connector. Let's create an helper to only initialise an encoder that would be passed as an argument. Acked-by: Thomas Zimmermann Signed-off-by: Maxime Ripard Link: https://lore.kernel.org/r/20220711173939.1132294-4-maxime@cerno.tech Signed-off-by: chenyi --- drivers/gpu/drm/drm_encoder.c | 75 ++++++++++++++++++++++++++++++----- include/drm/drm_encoder.h | 6 +++ 2 files changed, 70 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/drm_encoder.c b/drivers/gpu/drm/drm_encoder.c index 72e982323a5e5..2118f6616c380 100644 --- a/drivers/gpu/drm/drm_encoder.c +++ b/drivers/gpu/drm/drm_encoder.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "drm_crtc_internal.h" @@ -148,9 +149,9 @@ static int __drm_encoder_init(struct drm_device *dev, * the encoder structure. The encoder structure should not be allocated with * devm_kzalloc(). * - * Note: consider using drmm_encoder_alloc() instead of drm_encoder_init() to - * let the DRM managed resource infrastructure take care of cleanup and - * deallocation. + * Note: consider using drmm_encoder_alloc() or drmm_encoder_init() + * instead of drm_encoder_init() to let the DRM managed resource + * infrastructure take care of cleanup and deallocation. * * Returns: * Zero on success, error code on failure. @@ -212,6 +213,30 @@ static void drmm_encoder_alloc_release(struct drm_device *dev, void *ptr) drm_encoder_cleanup(encoder); } +__printf(5, 0) +static int __drmm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, + const char *name, + va_list args) +{ + int ret; + + if (drm_WARN_ON(dev, funcs && funcs->destroy)) + return -EINVAL; + + ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, args); + if (ret) + return ret; + + ret = drmm_add_action_or_reset(dev, drmm_encoder_alloc_release, encoder); + if (ret) + return ret; + + return 0; +} + void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, const struct drm_encoder_funcs *funcs, int encoder_type, const char *name, ...) @@ -221,9 +246,6 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, va_list ap; int ret; - if (WARN_ON(funcs && funcs->destroy)) - return ERR_PTR(-EINVAL); - container = drmm_kzalloc(dev, size, GFP_KERNEL); if (!container) return ERR_PTR(-EINVAL); @@ -231,19 +253,50 @@ void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, encoder = container + offset; va_start(ap, name); - ret = __drm_encoder_init(dev, encoder, funcs, encoder_type, name, ap); + ret = __drmm_encoder_init(dev, encoder, funcs, encoder_type, name, ap); va_end(ap); if (ret) return ERR_PTR(ret); - ret = drmm_add_action_or_reset(dev, drmm_encoder_alloc_release, encoder); - if (ret) - return ERR_PTR(ret); - return container; } EXPORT_SYMBOL(__drmm_encoder_alloc); +/** + * drmm_encoder_init - Initialize a preallocated encoder + * @dev: drm device + * @encoder: the encoder to init + * @funcs: callbacks for this encoder (optional) + * @encoder_type: user visible type of the encoder + * @name: printf style format string for the encoder name, or NULL for default name + * + * Initializes a preallocated encoder. Encoder should be subclassed as + * part of driver encoder objects. Cleanup is automatically handled + * through registering drm_encoder_cleanup() with drmm_add_action(). The + * encoder structure should be allocated with drmm_kzalloc(). + * + * The @drm_encoder_funcs.destroy hook must be NULL. + * + * Returns: + * Zero on success, error code on failure. + */ +int drmm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...) +{ + va_list ap; + int ret; + + va_start(ap, name); + ret = __drmm_encoder_init(dev, encoder, funcs, encoder_type, name, ap); + va_end(ap); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(drmm_encoder_init); + static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) { struct drm_connector *connector; diff --git a/include/drm/drm_encoder.h b/include/drm/drm_encoder.h index 6e91a0280f31b..3a09682af685a 100644 --- a/include/drm/drm_encoder.h +++ b/include/drm/drm_encoder.h @@ -194,6 +194,12 @@ int drm_encoder_init(struct drm_device *dev, const struct drm_encoder_funcs *funcs, int encoder_type, const char *name, ...); +__printf(5, 6) +int drmm_encoder_init(struct drm_device *dev, + struct drm_encoder *encoder, + const struct drm_encoder_funcs *funcs, + int encoder_type, const char *name, ...); + __printf(6, 7) void *__drmm_encoder_alloc(struct drm_device *dev, size_t size, size_t offset, From 4d7808b2dddf09c029dec7439b1f473077827ef8 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 17 Apr 2024 17:48:00 +0800 Subject: [PATCH 007/227] soc: hisilicon: kunpeng_hccs: Add the check for obtaining complete port attribute mainline inclusion from mainline-v6.9-rc1 commit 192f97fe164dd946d0da54bf6e17ff6f9ab9ba43 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9UY3Z CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?id=192f97fe164dd946d0da54bf6e17ff6f9ab9ba43 -------------------------------- The hccs_get_all_port_attr() is used to obtain the attribute of all the ports on a specified DIE from the firmware. However this interface doesn't ensure whether the firmware reports the complete attribute of all the ports or not. So this patch adds the check for this. Signed-off-by: Huisong Li Signed-off-by: Wei Xu Signed-off-by: huwentao --- drivers/soc/hisilicon/kunpeng_hccs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index 9b11efe3e131b..d1dadedf10917 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -556,6 +556,12 @@ static int hccs_get_all_port_attr(struct hccs_dev *hdev, start_id = rsp_head.next_id; } + if (left_buf_len != 0) { + dev_err(hdev->dev, "failed to get the expected port number(%u) attribute.\n", + size); + return -EINVAL; + } + return 0; } From 7a1017eb1505fd830246a0e2a60011cb40430101 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 28 Aug 2024 18:49:51 +0800 Subject: [PATCH 008/227] soc: hisilicon: kunpeng_hccs: Fix a PCC typo mainline inclusion from mainline-v6.13-rc1 commit 1cbe974f816cf40bc0437ebabe26b453331fb0d9 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB75RW CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1cbe974f816cf40bc0437ebabe26b453331fb0d9 -------------------------------- Fix a PCC typo. Signed-off-by: Huisong Li Reviewed-by: Jonathan Cameron Signed-off-by: Wei Xu Signed-off-by: huwentao --- drivers/soc/hisilicon/kunpeng_hccs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index d1dadedf10917..e16620c1a5817 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -144,7 +144,7 @@ static int hccs_register_pcc_channel(struct hccs_dev *hdev) pcc_chan = pcc_mbox_request_channel(cl, hdev->chan_id); if (IS_ERR(pcc_chan)) { - dev_err(dev, "PPC channel request failed.\n"); + dev_err(dev, "PCC channel request failed.\n"); rc = -ENODEV; goto out; } From 14b5358acf5d6ba6ca460e69a7001fdb96c565f0 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 28 Aug 2024 18:49:52 +0800 Subject: [PATCH 009/227] soc: hisilicon: kunpeng_hccs: Return failure on having not die or port information mainline inclusion from mainline-v6.13-rc1 commit 0c811bd2721d219bd104e0dc1fa76deee5e04ae4 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB75RW CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0c811bd2721d219bd104e0dc1fa76deee5e04ae4 -------------------------------- Driver is unavailable if all die number or all port number obtained from firmware are zero. Although this is unlikely, just to harden the code. Signed-off-by: Huisong Li Reviewed-by: Jonathan Cameron Signed-off-by: Wei Xu Signed-off-by: huwentao --- drivers/soc/hisilicon/kunpeng_hccs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index e16620c1a5817..3fbbc22982fde 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -451,6 +451,7 @@ static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev) struct device *dev = hdev->dev; struct hccs_chip_info *chip; struct hccs_die_info *die; + bool has_die_info = false; u8 i, j; int ret; @@ -459,6 +460,7 @@ static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev) if (!chip->die_num) continue; + has_die_info = true; chip->dies = devm_kzalloc(hdev->dev, chip->die_num * sizeof(struct hccs_die_info), GFP_KERNEL); @@ -480,7 +482,7 @@ static int hccs_query_all_die_info_on_platform(struct hccs_dev *hdev) } } - return 0; + return has_die_info ? 0 : -EINVAL; } static int hccs_get_bd_info(struct hccs_dev *hdev, u8 opcode, @@ -601,6 +603,7 @@ static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev) struct device *dev = hdev->dev; struct hccs_chip_info *chip; struct hccs_die_info *die; + bool has_port_info = false; u8 i, j; int ret; @@ -611,6 +614,7 @@ static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev) if (!die->port_num) continue; + has_port_info = true; die->ports = devm_kzalloc(dev, die->port_num * sizeof(struct hccs_port_info), GFP_KERNEL); @@ -629,7 +633,7 @@ static int hccs_query_all_port_info_on_platform(struct hccs_dev *hdev) } } - return 0; + return has_port_info ? 0 : -EINVAL; } static int hccs_get_hw_info(struct hccs_dev *hdev) From 1462a409fdb23ed804654dd198a32310da006edd Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 28 Aug 2024 18:49:53 +0800 Subject: [PATCH 010/227] soc: hisilicon: kunpeng_hccs: Add the check for base address and size of shared memory mainline inclusion from mainline-v6.13-rc1 commit 3400c85f6400dc907d686d319faad25956ac4db1 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB75RW CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=3400c85f6400dc907d686d319faad25956ac4db1 -------------------------------- If the shmem_base_addr from PCCT is zero, hccs_register_pcc_channel will return success. And then driver will access to illegal address when send PCC command. In addition, the size of shared memory used for communication between driver and platform is fixed, namely 64 Bytes which is unchangeable. So add the verification for hardening code. Signed-off-by: Huisong Li Reviewed-by: Jonathan Cameron Signed-off-by: Wei Xu Signed-off-by: huwentao --- drivers/soc/hisilicon/kunpeng_hccs.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index 3fbbc22982fde..d5d9f47525393 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -170,15 +170,21 @@ static int hccs_register_pcc_channel(struct hccs_dev *hdev) goto err_mbx_channel_free; } - if (pcc_chan->shmem_base_addr) { - cl_info->pcc_comm_addr = ioremap(pcc_chan->shmem_base_addr, - pcc_chan->shmem_size); - if (!cl_info->pcc_comm_addr) { - dev_err(dev, "Failed to ioremap PCC communication region for channel-%u.\n", - hdev->chan_id); - rc = -ENOMEM; - goto err_mbx_channel_free; - } + if (!pcc_chan->shmem_base_addr || + pcc_chan->shmem_size != HCCS_PCC_SHARE_MEM_BYTES) { + dev_err(dev, "The base address or size (%llu) of PCC communication region is invalid.\n", + pcc_chan->shmem_size); + rc = -EINVAL; + goto err_mbx_channel_free; + } + + cl_info->pcc_comm_addr = ioremap(pcc_chan->shmem_base_addr, + pcc_chan->shmem_size); + if (!cl_info->pcc_comm_addr) { + dev_err(dev, "Failed to ioremap PCC communication region for channel-%u.\n", + hdev->chan_id); + rc = -ENOMEM; + goto err_mbx_channel_free; } return 0; From c332d3f4d677f26658afca9c16c4f760ebcc5365 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 28 Aug 2024 18:49:54 +0800 Subject: [PATCH 011/227] soc: hisilicon: kunpeng_hccs: Fix the 'lane_mode' field name in port info structure to 'max_lane_num' mainline inclusion from mainline-v6.13-rc1 commit b51878373a81bda3a790fd936507fd0643ef98b3 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB75RW CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b51878373a81bda3a790fd936507fd0643ef98b3 -------------------------------- The lane mode of HCCS port is an information to user, and actually comes from the maximum lane number. But it is good and easy for driver to use the maximum lane number. So fix the 'lane_mode' field name in port info structure to 'max_lane_num'. Signed-off-by: Huisong Li Reviewed-by: Jonathan Cameron Signed-off-by: Wei Xu Signed-off-by: huwentao --- drivers/soc/hisilicon/kunpeng_hccs.c | 4 ++-- drivers/soc/hisilicon/kunpeng_hccs.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index d5d9f47525393..c65d8cf86d674 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -594,7 +594,7 @@ static int hccs_get_all_port_info_on_die(struct hccs_dev *hdev, port = &die->ports[i]; port->port_id = attrs[i].port_id; port->port_type = attrs[i].port_type; - port->lane_mode = attrs[i].lane_mode; + port->max_lane_num = attrs[i].max_lane_num; port->enable = attrs[i].enable; port->die = die; } @@ -839,7 +839,7 @@ static ssize_t lane_mode_show(struct kobject *kobj, struct kobj_attribute *attr, { const struct hccs_port_info *port = kobj_to_port_info(kobj); - return sysfs_emit(buf, "x%u\n", port->lane_mode); + return sysfs_emit(buf, "x%u\n", port->max_lane_num); } static struct kobj_attribute lane_mode_attr = __ATTR_RO(lane_mode); diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h index c3adbc01b4719..5e12a1e1474e1 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.h +++ b/drivers/soc/hisilicon/kunpeng_hccs.h @@ -19,7 +19,7 @@ struct hccs_port_info { u8 port_id; u8 port_type; - u8 lane_mode; + u8 max_lane_num; bool enable; /* if the port is enabled */ struct kobject kobj; bool dir_created; @@ -113,7 +113,7 @@ struct hccs_die_info_rsp_data { struct hccs_port_attr { u8 port_id; u8 port_type; - u8 lane_mode; + u8 max_lane_num; u8 enable : 1; /* if the port is enabled */ u16 rsv[2]; }; From e3a3b64998a497f32b0f51749260336d40e4f15b Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 8 Aug 2023 10:36:40 +0800 Subject: [PATCH 012/227] doc: soc: hisilicon: Add Kunpeng HCCS driver documentation Document the sysfs attributes description provided by HCCS driver on Kunpeng SoC. Signed-off-by: Huisong Li Signed-off-by: Wei Xu Signed-off-by: huwentao --- .../sysfs-devices-platform-kunpeng_hccs | 81 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 82 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs new file mode 100644 index 0000000000000..fdb4e36310fb5 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs @@ -0,0 +1,81 @@ +What: /sys/devices/platform/HISI04Bx:00/chipX/all_linked +What: /sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane +What: /sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt +Date: November 2023 +KernelVersion: 6.6 +Contact: Huisong Li +Description: + The /sys/devices/platform/HISI04Bx:00/chipX/ directory + contains read-only attributes exposing some summarization + information of all HCCS ports under a specified chip. + The X in 'chipX' indicates the Xth chip on platform. + + There are following attributes in this directory: + + ================= ==== ========================================= + all_linked: (RO) if all enabled ports on this chip are + linked (bool). + linked_full_lane: (RO) if all linked ports on this chip are full + lane (bool). + crc_err_cnt: (RO) total CRC err count for all ports on this + chip. + ================= ==== ========================================= + +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/all_linked +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt +Date: November 2023 +KernelVersion: 6.6 +Contact: Huisong Li +Description: + The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory + contains read-only attributes exposing some summarization + information of all HCCS ports under a specified die. + The Y in 'dieY' indicates the hardware id of the die on chip who + has chip id X. + + There are following attributes in this directory: + + ================= ==== ========================================= + all_linked: (RO) if all enabled ports on this die are + linked (bool). + linked_full_lane: (RO) if all linked ports on this die are full + lane (bool). + crc_err_cnt: (RO) total CRC err count for all ports on this + die. + ================= ==== ========================================= + +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/type +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mode +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/enable +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/cur_lane_num +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/link_fsm +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask +What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt +Date: November 2023 +KernelVersion: 6.6 +Contact: Huisong Li +Description: + The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory + contains read-only attributes exposing information about + a HCCS port. The N value in 'hccsN' indicates this port id. + The X in 'chipX' indicates the ID of the chip to which the + HCCS port belongs. For example, X ranges from to 'n - 1' if the + chip number on platform is n. + The Y in 'dieY' indicates the hardware id of the die to which + the hccs port belongs. + Note: type, lane_mode and enable are fixed attributes on running + platform. + + The HCCS port have the following attributes: + + ============= ==== ============================================= + type: (RO) port type (string), e.g. HCCS-v1 -> H32 + lane_mode: (RO) the lane mode of this port (string), e.g. x8 + enable: (RO) indicate if this port is enabled (bool). + cur_lane_num: (RO) current lane number of this port. + link_fsm: (RO) link finite state machine of this port. + lane_mask: (RO) current lane mask of this port, every bit + indicates a lane. + crc_err_cnt: (RO) CRC err count on this port. + ============= ==== ============================================= diff --git a/MAINTAINERS b/MAINTAINERS index dca0f45f5161e..ab1ff9b4195ef 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8474,6 +8474,7 @@ F: drivers/i2c/busses/i2c-hisi.c HISILICON KUNPENG SOC HCCS DRIVER M: Huisong Li S: Maintained +F: Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs F: drivers/soc/hisilicon/kunpeng_hccs.c F: drivers/soc/hisilicon/kunpeng_hccs.h From b6011c2eb01105d73e1b5f7017640670062f58c0 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Fri, 1 Dec 2023 11:45:33 +0800 Subject: [PATCH 013/227] doc: kunpeng_hccs: Fix incorrect email domain name mainline inclusion from mainline-v6.8-rc1 commit a07d8fc358af5f297b41b2182a5fce710e3c9bd7 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8Y3PY CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v6.6-rc1&id=a07d8fc358af5f297b41b2182a5fce710e3c9bd7 -------------------------------- The email domain name in Contact is wrong. So this patch has to modify it. Signed-off-by: Huisong Li Signed-off-by: Wei Xu Signed-off-by: huwentao --- .../ABI/testing/sysfs-devices-platform-kunpeng_hccs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs index fdb4e36310fb5..1666340820f75 100644 --- a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs +++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs @@ -3,7 +3,7 @@ What: /sys/devices/platform/HISI04Bx:00/chipX/linked_full_lane What: /sys/devices/platform/HISI04Bx:00/chipX/crc_err_cnt Date: November 2023 KernelVersion: 6.6 -Contact: Huisong Li +Contact: Huisong Li Description: The /sys/devices/platform/HISI04Bx:00/chipX/ directory contains read-only attributes exposing some summarization @@ -26,7 +26,7 @@ What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/linked_full_lane What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/crc_err_cnt Date: November 2023 KernelVersion: 6.6 -Contact: Huisong Li +Contact: Huisong Li Description: The /sys/devices/platform/HISI04Bx:00/chipX/dieY/ directory contains read-only attributes exposing some summarization @@ -54,7 +54,7 @@ What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/lane_mask What: /sys/devices/platform/HISI04Bx:00/chipX/dieY/hccsN/crc_err_cnt Date: November 2023 KernelVersion: 6.6 -Contact: Huisong Li +Contact: Huisong Li Description: The /sys/devices/platform/HISI04Bx/chipX/dieX/hccsN/ directory contains read-only attributes exposing information about From 48bfa9f15377016fc9f31144064dfb724eb1cc0c Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 28 Aug 2024 18:49:55 +0800 Subject: [PATCH 014/227] soc: hisilicon: kunpeng_hccs: Add used HCCS types sysfs mainline inclusion from mainline-v6.13-rc1 commit 23fe8112a23106e7dfc2b73fc52a60ea3eb64c20 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB75RW CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=23fe8112a23106e7dfc2b73fc52a60ea3eb64c20 -------------------------------- Kunpeng_hccs driver supports multiple HCCS types used on one platform at the same time. In this case, to find which HCCS types are used on the platform the user needs to scan the type attribute of all ports, which is unfriendly to the user. In addition, the aggregated information is also useful for global control like the low power feature. So add the sysfs to show all HCCS types used on the platform. Signed-off-by: Huisong Li Reviewed-by: Jonathan Cameron Signed-off-by: Wei Xu Signed-off-by: huwentao --- .../sysfs-devices-platform-kunpeng_hccs | 8 ++ drivers/soc/hisilicon/kunpeng_hccs.c | 102 +++++++++++++++++- drivers/soc/hisilicon/kunpeng_hccs.h | 15 +++ 3 files changed, 124 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs index 1666340820f75..d4c355e0e0bbd 100644 --- a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs +++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs @@ -79,3 +79,11 @@ Description: indicates a lane. crc_err_cnt: (RO) CRC err count on this port. ============= ==== ============================================= + +What: /sys/devices/platform/HISI04Bx:00/used_types +Date: August 2024 +KernelVersion: 6.12 +Contact: Huisong Li +Description: + This interface is used to show all HCCS types used on the + platform, like, HCCS-v1, HCCS-v2 and so on. diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index c65d8cf86d674..155ba072a3575 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -21,11 +21,14 @@ * - if all enabled ports are in linked * - if all linked ports are in full lane * - CRC error count sum + * + * - Retrieve all HCCS types used on the platform. */ #include #include #include #include +#include #include @@ -53,6 +56,15 @@ static struct hccs_chip_info *kobj_to_chip_info(struct kobject *k) return container_of(k, struct hccs_chip_info, kobj); } +static struct hccs_dev *device_kobj_to_hccs_dev(struct kobject *k) +{ + struct device *dev = container_of(k, struct device, kobj); + struct platform_device *pdev = + container_of(dev, struct platform_device, dev); + + return platform_get_drvdata(pdev); +} + struct hccs_register_ctx { struct device *dev; u8 chan_id; @@ -670,6 +682,55 @@ static int hccs_get_hw_info(struct hccs_dev *hdev) return 0; } +static u16 hccs_calc_used_type_num(struct hccs_dev *hdev, + unsigned long *hccs_ver) +{ + struct hccs_chip_info *chip; + struct hccs_port_info *port; + struct hccs_die_info *die; + u16 used_type_num = 0; + u16 i, j, k; + + for (i = 0; i < hdev->chip_num; i++) { + chip = &hdev->chips[i]; + for (j = 0; j < chip->die_num; j++) { + die = &chip->dies[j]; + for (k = 0; k < die->port_num; k++) { + port = &die->ports[k]; + set_bit(port->port_type, hccs_ver); + } + } + } + + for_each_set_bit(i, hccs_ver, HCCS_IP_MAX + 1) + used_type_num++; + + return used_type_num; +} + +static int hccs_init_type_name_maps(struct hccs_dev *hdev) +{ + DECLARE_BITMAP(hccs_ver, HCCS_IP_MAX + 1) = {}; + unsigned int i; + u16 idx = 0; + + hdev->used_type_num = hccs_calc_used_type_num(hdev, hccs_ver); + hdev->type_name_maps = devm_kcalloc(hdev->dev, hdev->used_type_num, + sizeof(struct hccs_type_name_map), + GFP_KERNEL); + if (!hdev->type_name_maps) + return -ENOMEM; + + for_each_set_bit(i, hccs_ver, HCCS_IP_MAX + 1) { + hdev->type_name_maps[idx].type = i; + sprintf(hdev->type_name_maps[idx].name, + "%s%u", HCCS_IP_PREFIX, i); + idx++; + } + + return 0; +} + static int hccs_query_port_link_status(struct hccs_dev *hdev, const struct hccs_port_info *port, struct hccs_link_status *link_status) @@ -830,7 +891,7 @@ static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, { const struct hccs_port_info *port = kobj_to_port_info(kobj); - return sysfs_emit(buf, "HCCS-v%u\n", port->port_type); + return sysfs_emit(buf, "%s%u\n", HCCS_IP_PREFIX, port->port_type); } static struct kobj_attribute hccs_type_attr = __ATTR_RO(type); @@ -1134,6 +1195,33 @@ static struct kobj_type hccs_chip_type = { .default_groups = hccs_chip_default_groups, }; + +static ssize_t used_types_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct hccs_dev *hdev = device_kobj_to_hccs_dev(kobj); + int len = 0; + u16 i; + + for (i = 0; i < hdev->used_type_num - 1; i++) + len += sysfs_emit(&buf[len], "%s ", hdev->type_name_maps[i].name); + len += sysfs_emit(&buf[len], "%s\n", hdev->type_name_maps[i].name); + + return len; +} +static struct kobj_attribute used_types_attr = + __ATTR(used_types, 0444, used_types_show, NULL); + +static void hccs_remove_misc_sysfs(struct hccs_dev *hdev) +{ + sysfs_remove_file(&hdev->dev->kobj, &used_types_attr.attr); +} + +static int hccs_add_misc_sysfs(struct hccs_dev *hdev) +{ + return sysfs_create_file(&hdev->dev->kobj, &used_types_attr.attr); +} + static void hccs_remove_die_dir(struct hccs_die_info *die) { struct hccs_port_info *port; @@ -1168,6 +1256,8 @@ static void hccs_remove_topo_dirs(struct hccs_dev *hdev) for (i = 0; i < hdev->chip_num; i++) hccs_remove_chip_dir(&hdev->chips[i]); + + hccs_remove_misc_sysfs(hdev); } static int hccs_create_hccs_dir(struct hccs_dev *hdev, @@ -1263,6 +1353,12 @@ static int hccs_create_topo_dirs(struct hccs_dev *hdev) } } + ret = hccs_add_misc_sysfs(hdev); + if (ret) { + dev_err(hdev->dev, "create misc sysfs interface failed, ret = %d\n", ret); + goto err; + } + return 0; err: for (k = 0; k < id; k++) @@ -1313,6 +1409,10 @@ static int hccs_probe(struct platform_device *pdev) if (rc) goto unregister_pcc_chan; + rc = hccs_init_type_name_maps(hdev); + if (rc) + goto unregister_pcc_chan; + rc = hccs_create_topo_dirs(hdev); if (rc) goto unregister_pcc_chan; diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h index 5e12a1e1474e1..401df4694aec0 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.h +++ b/drivers/soc/hisilicon/kunpeng_hccs.h @@ -10,6 +10,19 @@ * | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 | P0 | P1 | P2 | P3 |P0 | P1 | P2 | P3 | */ +enum hccs_port_type { + HCCS_V1 = 1, + HCCS_V2, +}; + +#define HCCS_IP_PREFIX "HCCS-v" +#define HCCS_IP_MAX 255 +#define HCCS_NAME_MAX_LEN 9 +struct hccs_type_name_map { + u8 type; + char name[HCCS_NAME_MAX_LEN + 1]; +}; + /* * This value cannot be 255, otherwise the loop of the multi-BD communication * case cannot end. @@ -74,6 +87,8 @@ struct hccs_dev { u64 caps; u8 chip_num; struct hccs_chip_info *chips; + u16 used_type_num; + struct hccs_type_name_map *type_name_maps; u8 chan_id; struct mutex lock; struct hccs_mbox_client_info cl_info; From aab4d7a94aa571bece586c8c4b88ec789c943a3e Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 28 Aug 2024 18:49:56 +0800 Subject: [PATCH 015/227] soc: hisilicon: kunpeng_hccs: Support low power feature for the specified HCCS type mainline inclusion from mainline-v6.13-rc1 commit e3c289c0916e2a3d7859a0cf851ea242fa23ec1e category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB75RW CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e3c289c0916e2a3d7859a0cf851ea242fa23ec1e -------------------------------- Add the low power feature for the specified HCCS type by increasing and decreasing the used lane number of these HCCS ports on platform. Signed-off-by: Huisong Li Reviewed-by: Jonathan Cameron Signed-off-by: Wei Xu Signed-off-by: huwentao --- .../sysfs-devices-platform-kunpeng_hccs | 37 ++ drivers/soc/hisilicon/Kconfig | 7 +- drivers/soc/hisilicon/kunpeng_hccs.c | 376 +++++++++++++++++- drivers/soc/hisilicon/kunpeng_hccs.h | 14 + 4 files changed, 431 insertions(+), 3 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs index d4c355e0e0bbd..d1b3a95a5518a 100644 --- a/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs +++ b/Documentation/ABI/testing/sysfs-devices-platform-kunpeng_hccs @@ -87,3 +87,40 @@ Contact: Huisong Li Description: This interface is used to show all HCCS types used on the platform, like, HCCS-v1, HCCS-v2 and so on. + +What: /sys/devices/platform/HISI04Bx:00/available_inc_dec_lane_types +What: /sys/devices/platform/HISI04Bx:00/dec_lane_of_type +What: /sys/devices/platform/HISI04Bx:00/inc_lane_of_type +Date: August 2024 +KernelVersion: 6.12 +Contact: Huisong Li +Description: + These interfaces under /sys/devices/platform/HISI04Bx/ are + used to support the low power consumption feature of some + HCCS types by changing the number of lanes used. The interfaces + changing the number of lanes used are 'dec_lane_of_type' and + 'inc_lane_of_type' which require root privileges. These + interfaces aren't exposed if no HCCS type on platform support + this feature. Please note that decreasing lane number is only + allowed if all the specified HCCS ports are not busy. + + The low power consumption interfaces are as follows: + + ============================= ==== ================================ + available_inc_dec_lane_types: (RO) available HCCS types (string) to + increase and decrease the number + of lane used, e.g. HCCS-v2. + dec_lane_of_type: (WO) input HCCS type supported + decreasing lane to decrease the + used lane number of all specified + HCCS type ports on platform to + the minimum. + You can query the 'cur_lane_num' + to get the minimum lane number + after executing successfully. + inc_lane_of_type: (WO) input HCCS type supported + increasing lane to increase the + used lane number of all specified + HCCS type ports on platform to + the full lane state. + ============================= ==== ================================ diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig index 0ab688af308fe..ba83651368402 100644 --- a/drivers/soc/hisilicon/Kconfig +++ b/drivers/soc/hisilicon/Kconfig @@ -13,9 +13,12 @@ config KUNPENG_HCCS interconnection bus protocol. The performance of application may be affected if some HCCS ports are not in full lane status, have a large number of CRC - errors and so on. + errors and so on. This may support for reducing system power + consumption if there are HCCS ports supported low power feature + on platform. Say M here if you want to include support for querying the - health status and port information of HCCS on Kunpeng SoC. + health status and port information of HCCS, or reducing system + power consumption on Kunpeng SoC. endmenu diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index 155ba072a3575..59dff2e8a0878 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -23,10 +23,18 @@ * - CRC error count sum * * - Retrieve all HCCS types used on the platform. + * + * - Support low power feature for all specified HCCS type ports, and + * provide the following interface: + * - query HCCS types supported increasing and decreasing lane number. + * - decrease lane number of all specified HCCS type ports on idle state. + * - increase lane number of all specified HCCS type ports. */ #include +#include #include #include +#include #include #include @@ -65,6 +73,33 @@ static struct hccs_dev *device_kobj_to_hccs_dev(struct kobject *k) return platform_get_drvdata(pdev); } +static char *hccs_port_type_to_name(struct hccs_dev *hdev, u8 type) +{ + u16 i; + + for (i = 0; i < hdev->used_type_num; i++) { + if (hdev->type_name_maps[i].type == type) + return hdev->type_name_maps[i].name; + } + + return NULL; +} + +static int hccs_name_to_port_type(struct hccs_dev *hdev, + const char *name, u8 *type) +{ + u16 i; + + for (i = 0; i < hdev->used_type_num; i++) { + if (strcmp(hdev->type_name_maps[i].name, name) == 0) { + *type = hdev->type_name_maps[i].type; + return 0; + } + } + + return -EINVAL; +} + struct hccs_register_ctx { struct device *dev; u8 chan_id; @@ -1195,6 +1230,306 @@ static struct kobj_type hccs_chip_type = { .default_groups = hccs_chip_default_groups, }; +static int hccs_parse_pm_port_type(struct hccs_dev *hdev, const char *buf, + u8 *port_type) +{ + char hccs_name[HCCS_NAME_MAX_LEN + 1] = ""; + u8 type; + int ret; + + ret = sscanf(buf, "%" __stringify(HCCS_NAME_MAX_LEN) "s", hccs_name); + if (ret != 1) + return -EINVAL; + + ret = hccs_name_to_port_type(hdev, hccs_name, &type); + if (ret) { + dev_dbg(hdev->dev, "input invalid, please get the available types from 'used_types'.\n"); + return ret; + } + + if (type == HCCS_V2 && hdev->caps & HCCS_CAPS_HCCS_V2_PM) { + *port_type = type; + return 0; + } + + dev_dbg(hdev->dev, "%s doesn't support for increasing and decreasing lane.\n", + hccs_name); + + return -EOPNOTSUPP; +} + +static int hccs_query_port_idle_status(struct hccs_dev *hdev, + struct hccs_port_info *port, u8 *idle) +{ + const struct hccs_die_info *die = port->die; + const struct hccs_chip_info *chip = die->chip; + struct hccs_port_comm_req_param *req_param; + struct hccs_desc desc; + int ret; + + hccs_init_req_desc(&desc); + req_param = (struct hccs_port_comm_req_param *)desc.req.data; + req_param->chip_id = chip->chip_id; + req_param->die_id = die->die_id; + req_param->port_id = port->port_id; + ret = hccs_pcc_cmd_send(hdev, HCCS_GET_PORT_IDLE_STATUS, &desc); + if (ret) { + dev_err(hdev->dev, + "get port idle status failed, ret = %d.\n", ret); + return ret; + } + + *idle = *((u8 *)desc.rsp.data); + return 0; +} + +static int hccs_get_all_spec_port_idle_sta(struct hccs_dev *hdev, u8 port_type, + bool *all_idle) +{ + struct hccs_chip_info *chip; + struct hccs_port_info *port; + struct hccs_die_info *die; + int ret = 0; + u8 i, j, k; + u8 idle; + + *all_idle = false; + for (i = 0; i < hdev->chip_num; i++) { + chip = &hdev->chips[i]; + for (j = 0; j < chip->die_num; j++) { + die = &chip->dies[j]; + for (k = 0; k < die->port_num; k++) { + port = &die->ports[k]; + if (port->port_type != port_type) + continue; + ret = hccs_query_port_idle_status(hdev, port, + &idle); + if (ret) { + dev_err(hdev->dev, + "hccs%u on chip%u/die%u get idle status failed, ret = %d.\n", + k, i, j, ret); + return ret; + } else if (idle == 0) { + dev_info(hdev->dev, "hccs%u on chip%u/die%u is busy.\n", + k, i, j); + return 0; + } + } + } + } + *all_idle = true; + + return 0; +} + +static int hccs_get_all_spec_port_full_lane_sta(struct hccs_dev *hdev, + u8 port_type, bool *full_lane) +{ + struct hccs_link_status status = {0}; + struct hccs_chip_info *chip; + struct hccs_port_info *port; + struct hccs_die_info *die; + u8 i, j, k; + int ret; + + *full_lane = false; + for (i = 0; i < hdev->chip_num; i++) { + chip = &hdev->chips[i]; + for (j = 0; j < chip->die_num; j++) { + die = &chip->dies[j]; + for (k = 0; k < die->port_num; k++) { + port = &die->ports[k]; + if (port->port_type != port_type) + continue; + ret = hccs_query_port_link_status(hdev, port, + &status); + if (ret) + return ret; + if (status.lane_num != port->max_lane_num) + return 0; + } + } + } + *full_lane = true; + + return 0; +} + +static int hccs_prepare_inc_lane(struct hccs_dev *hdev, u8 type) +{ + struct hccs_inc_lane_req_param *req_param; + struct hccs_desc desc; + int ret; + + hccs_init_req_desc(&desc); + req_param = (struct hccs_inc_lane_req_param *)desc.req.data; + req_param->port_type = type; + req_param->opt_type = HCCS_PREPARE_INC_LANE; + ret = hccs_pcc_cmd_send(hdev, HCCS_PM_INC_LANE, &desc); + if (ret) + dev_err(hdev->dev, "prepare for increasing lane failed, ret = %d.\n", + ret); + + return ret; +} + +static int hccs_wait_serdes_adapt_completed(struct hccs_dev *hdev, u8 type) +{ +#define HCCS_MAX_WAIT_CNT_FOR_ADAPT 10 +#define HCCS_QUERY_ADAPT_RES_DELAY_MS 100 +#define HCCS_SERDES_ADAPT_OK 0 + + struct hccs_inc_lane_req_param *req_param; + u8 wait_cnt = HCCS_MAX_WAIT_CNT_FOR_ADAPT; + struct hccs_desc desc; + u8 adapt_res; + int ret; + + do { + hccs_init_req_desc(&desc); + req_param = (struct hccs_inc_lane_req_param *)desc.req.data; + req_param->port_type = type; + req_param->opt_type = HCCS_GET_ADAPT_RES; + ret = hccs_pcc_cmd_send(hdev, HCCS_PM_INC_LANE, &desc); + if (ret) { + dev_err(hdev->dev, "query adapting result failed, ret = %d.\n", + ret); + return ret; + } + adapt_res = *((u8 *)&desc.rsp.data); + if (adapt_res == HCCS_SERDES_ADAPT_OK) + return 0; + + msleep(HCCS_QUERY_ADAPT_RES_DELAY_MS); + } while (--wait_cnt); + + dev_err(hdev->dev, "wait for adapting completed timeout.\n"); + + return -ETIMEDOUT; +} + +static int hccs_start_hpcs_retraining(struct hccs_dev *hdev, u8 type) +{ + struct hccs_inc_lane_req_param *req_param; + struct hccs_desc desc; + int ret; + + hccs_init_req_desc(&desc); + req_param = (struct hccs_inc_lane_req_param *)desc.req.data; + req_param->port_type = type; + req_param->opt_type = HCCS_START_RETRAINING; + ret = hccs_pcc_cmd_send(hdev, HCCS_PM_INC_LANE, &desc); + if (ret) + dev_err(hdev->dev, "start hpcs retraining failed, ret = %d.\n", + ret); + + return ret; +} + +static int hccs_start_inc_lane(struct hccs_dev *hdev, u8 type) +{ + int ret; + + ret = hccs_prepare_inc_lane(hdev, type); + if (ret) + return ret; + + ret = hccs_wait_serdes_adapt_completed(hdev, type); + if (ret) + return ret; + + return hccs_start_hpcs_retraining(hdev, type); +} + +static int hccs_start_dec_lane(struct hccs_dev *hdev, u8 type) +{ + struct hccs_desc desc; + u8 *port_type; + int ret; + + hccs_init_req_desc(&desc); + port_type = (u8 *)desc.req.data; + *port_type = type; + ret = hccs_pcc_cmd_send(hdev, HCCS_PM_DEC_LANE, &desc); + if (ret) + dev_err(hdev->dev, "start to decrease lane failed, ret = %d.\n", + ret); + + return ret; +} + +static ssize_t dec_lane_of_type_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct hccs_dev *hdev = device_kobj_to_hccs_dev(kobj); + bool all_in_idle; + u8 port_type; + int ret; + + ret = hccs_parse_pm_port_type(hdev, buf, &port_type); + if (ret) + return ret; + + mutex_lock(&hdev->lock); + ret = hccs_get_all_spec_port_idle_sta(hdev, port_type, &all_in_idle); + if (ret) + goto out; + if (!all_in_idle) { + ret = -EBUSY; + dev_err(hdev->dev, "please don't decrese lanes on high load with %s, ret = %d.\n", + hccs_port_type_to_name(hdev, port_type), ret); + goto out; + } + + ret = hccs_start_dec_lane(hdev, port_type); +out: + mutex_unlock(&hdev->lock); + + return ret == 0 ? count : ret; +} +static struct kobj_attribute dec_lane_of_type_attr = + __ATTR(dec_lane_of_type, 0200, NULL, dec_lane_of_type_store); + +static ssize_t inc_lane_of_type_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct hccs_dev *hdev = device_kobj_to_hccs_dev(kobj); + bool full_lane; + u8 port_type; + int ret; + + ret = hccs_parse_pm_port_type(hdev, buf, &port_type); + if (ret) + return ret; + + mutex_lock(&hdev->lock); + ret = hccs_get_all_spec_port_full_lane_sta(hdev, port_type, &full_lane); + if (ret || full_lane) + goto out; + + ret = hccs_start_inc_lane(hdev, port_type); +out: + mutex_unlock(&hdev->lock); + return ret == 0 ? count : ret; +} +static struct kobj_attribute inc_lane_of_type_attr = + __ATTR(inc_lane_of_type, 0200, NULL, inc_lane_of_type_store); + +static ssize_t available_inc_dec_lane_types_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + struct hccs_dev *hdev = device_kobj_to_hccs_dev(kobj); + + if (hdev->caps & HCCS_CAPS_HCCS_V2_PM) + return sysfs_emit(buf, "%s\n", + hccs_port_type_to_name(hdev, HCCS_V2)); + + return -EINVAL; +} +static struct kobj_attribute available_inc_dec_lane_types_attr = + __ATTR(available_inc_dec_lane_types, 0444, + available_inc_dec_lane_types_show, NULL); static ssize_t used_types_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -1215,11 +1550,50 @@ static struct kobj_attribute used_types_attr = static void hccs_remove_misc_sysfs(struct hccs_dev *hdev) { sysfs_remove_file(&hdev->dev->kobj, &used_types_attr.attr); + + if (!(hdev->caps & HCCS_CAPS_HCCS_V2_PM)) + return; + + sysfs_remove_file(&hdev->dev->kobj, + &available_inc_dec_lane_types_attr.attr); + sysfs_remove_file(&hdev->dev->kobj, &dec_lane_of_type_attr.attr); + sysfs_remove_file(&hdev->dev->kobj, &inc_lane_of_type_attr.attr); } static int hccs_add_misc_sysfs(struct hccs_dev *hdev) { - return sysfs_create_file(&hdev->dev->kobj, &used_types_attr.attr); + int ret; + + ret = sysfs_create_file(&hdev->dev->kobj, &used_types_attr.attr); + if (ret) + return ret; + + if (!(hdev->caps & HCCS_CAPS_HCCS_V2_PM)) + return 0; + + ret = sysfs_create_file(&hdev->dev->kobj, + &available_inc_dec_lane_types_attr.attr); + if (ret) + goto used_types_remove; + + ret = sysfs_create_file(&hdev->dev->kobj, &dec_lane_of_type_attr.attr); + if (ret) + goto inc_dec_lane_types_remove; + + ret = sysfs_create_file(&hdev->dev->kobj, &inc_lane_of_type_attr.attr); + if (ret) + goto dec_lane_of_type_remove; + + return 0; + +dec_lane_of_type_remove: + sysfs_remove_file(&hdev->dev->kobj, &dec_lane_of_type_attr.attr); +inc_dec_lane_types_remove: + sysfs_remove_file(&hdev->dev->kobj, + &available_inc_dec_lane_types_attr.attr); +used_types_remove: + sysfs_remove_file(&hdev->dev->kobj, &used_types_attr.attr); + return ret; } static void hccs_remove_die_dir(struct hccs_die_info *die) diff --git a/drivers/soc/hisilicon/kunpeng_hccs.h b/drivers/soc/hisilicon/kunpeng_hccs.h index 401df4694aec0..dc267136919b7 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.h +++ b/drivers/soc/hisilicon/kunpeng_hccs.h @@ -80,10 +80,13 @@ struct hccs_verspecific_data { bool has_txdone_irq; }; +#define HCCS_CAPS_HCCS_V2_PM BIT_ULL(0) + struct hccs_dev { struct device *dev; struct acpi_device *acpi_dev; const struct hccs_verspecific_data *verspec_data; + /* device capabilities from firmware, like HCCS_CAPS_xxx. */ u64 caps; u8 chip_num; struct hccs_chip_info *chips; @@ -106,6 +109,9 @@ enum hccs_subcmd_type { HCCS_GET_DIE_PORTS_LANE_STA, HCCS_GET_DIE_PORTS_LINK_STA, HCCS_GET_DIE_PORTS_CRC_ERR_CNT, + HCCS_GET_PORT_IDLE_STATUS, + HCCS_PM_DEC_LANE, + HCCS_PM_INC_LANE, HCCS_SUB_CMD_MAX = 255, }; @@ -149,6 +155,14 @@ struct hccs_port_comm_req_param { u8 port_id; }; +#define HCCS_PREPARE_INC_LANE 1 +#define HCCS_GET_ADAPT_RES 2 +#define HCCS_START_RETRAINING 3 +struct hccs_inc_lane_req_param { + u8 port_type; + u8 opt_type; +}; + #define HCCS_PORT_RESET 1 #define HCCS_PORT_SETUP 2 #define HCCS_PORT_CONFIG 3 From 3c471fb373aa410f4c66ae529051d08f5ad126eb Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Fri, 14 Mar 2025 18:01:43 +0800 Subject: [PATCH 016/227] soc: hisilicon: kunpeng_hccs: Fix incorrect string assembly mainline inclusion from mainline-v6.14 commit 262666c04be6afa8f15b6c318596b54c37499cda category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC4N9D CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=262666c04be6afa8f15b6c318596b54c37499cda -------------------------------- String assembly should use sysfs_emit_at() instead of sysfs_emit(). Fixes: 23fe8112a231 ("soc: hisilicon: kunpeng_hccs: Add used HCCS types sysfs") Reviewed-by: Jonathan Cameron Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250314100143.3377268-1-lihuisong@huawei.com Signed-off-by: Arnd Bergmann Signed-off-by: Dengdui Huang Signed-off-by: huwentao --- drivers/soc/hisilicon/kunpeng_hccs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/soc/hisilicon/kunpeng_hccs.c b/drivers/soc/hisilicon/kunpeng_hccs.c index 59dff2e8a0878..eb679395f7d19 100644 --- a/drivers/soc/hisilicon/kunpeng_hccs.c +++ b/drivers/soc/hisilicon/kunpeng_hccs.c @@ -1539,8 +1539,8 @@ static ssize_t used_types_show(struct kobject *kobj, u16 i; for (i = 0; i < hdev->used_type_num - 1; i++) - len += sysfs_emit(&buf[len], "%s ", hdev->type_name_maps[i].name); - len += sysfs_emit(&buf[len], "%s\n", hdev->type_name_maps[i].name); + len += sysfs_emit_at(buf, len, "%s ", hdev->type_name_maps[i].name); + len += sysfs_emit_at(buf, len, "%s\n", hdev->type_name_maps[i].name); return len; } From 3853a63f836b6373092dfe233c6a57ea3f94f2f6 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Fri, 26 May 2023 12:23:48 +0200 Subject: [PATCH 017/227] locking: Introduce __cleanup() based infrastructure Use __attribute__((__cleanup__(func))) to build: - simple auto-release pointers using __free() - 'classes' with constructor and destructor semantics for scope-based resource management. - lock guards based on the above classes. Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20230612093537.614161713%40infradead.org Signed-off-by: huwentao Conflicts: include/linux/preempt.h include/linux/slab.h include/linux/spinlock.h --- include/linux/cleanup.h | 171 ++++++++++++++++++++++++++++ include/linux/compiler-clang.h | 9 ++ include/linux/compiler_attributes.h | 6 + include/linux/device.h | 7 ++ include/linux/file.h | 6 + include/linux/irqflags.h | 7 ++ include/linux/mutex.h | 4 + include/linux/percpu.h | 4 + include/linux/preempt.h | 5 + include/linux/rcupdate.h | 3 + include/linux/rwsem.h | 8 ++ include/linux/sched/task.h | 2 + include/linux/slab.h | 4 + include/linux/spinlock.h | 31 +++++ include/linux/srcu.h | 5 + scripts/checkpatch.pl | 2 +- 16 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 include/linux/cleanup.h diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h new file mode 100644 index 0000000000000..53f1a7a932b08 --- /dev/null +++ b/include/linux/cleanup.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __LINUX_GUARDS_H +#define __LINUX_GUARDS_H + +#include + +/* + * DEFINE_FREE(name, type, free): + * simple helper macro that defines the required wrapper for a __free() + * based cleanup function. @free is an expression using '_T' to access + * the variable. + * + * __free(name): + * variable attribute to add a scoped based cleanup to the variable. + * + * no_free_ptr(var): + * like a non-atomic xchg(var, NULL), such that the cleanup function will + * be inhibited -- provided it sanely deals with a NULL value. + * + * return_ptr(p): + * returns p while inhibiting the __free(). + * + * Ex. + * + * DEFINE_FREE(kfree, void *, if (_T) kfree(_T)) + * + * struct obj *p __free(kfree) = kmalloc(...); + * if (!p) + * return NULL; + * + * if (!init_obj(p)) + * return NULL; + * + * return_ptr(p); + */ + +#define DEFINE_FREE(_name, _type, _free) \ + static inline void __free_##_name(void *p) { _type _T = *(_type *)p; _free; } + +#define __free(_name) __cleanup(__free_##_name) + +#define no_free_ptr(p) \ + ({ __auto_type __ptr = (p); (p) = NULL; __ptr; }) + +#define return_ptr(p) return no_free_ptr(p) + + +/* + * DEFINE_CLASS(name, type, exit, init, init_args...): + * helper to define the destructor and constructor for a type. + * @exit is an expression using '_T' -- similar to FREE above. + * @init is an expression in @init_args resulting in @type + * + * EXTEND_CLASS(name, ext, init, init_args...): + * extends class @name to @name@ext with the new constructor + * + * CLASS(name, var)(args...): + * declare the variable @var as an instance of the named class + * + * Ex. + * + * DEFINE_CLASS(fdget, struct fd, fdput(_T), fdget(fd), int fd) + * + * CLASS(fdget, f)(fd); + * if (!f.file) + * return -EBADF; + * + * // use 'f' without concern + */ + +#define DEFINE_CLASS(_name, _type, _exit, _init, _init_args...) \ +typedef _type class_##_name##_t; \ +static inline void class_##_name##_destructor(_type *p) \ +{ _type _T = *p; _exit; } \ +static inline _type class_##_name##_constructor(_init_args) \ +{ _type t = _init; return t; } + +#define EXTEND_CLASS(_name, ext, _init, _init_args...) \ +typedef class_##_name##_t class_##_name##ext##_t; \ +static inline void class_##_name##ext##_destructor(class_##_name##_t *p)\ +{ class_##_name##_destructor(p); } \ +static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ +{ class_##_name##_t t = _init; return t; } + +#define CLASS(_name, var) \ + class_##_name##_t var __cleanup(class_##_name##_destructor) = \ + class_##_name##_constructor + + +/* + * DEFINE_GUARD(name, type, lock, unlock): + * trivial wrapper around DEFINE_CLASS() above specifically + * for locks. + * + * guard(name): + * an anonymous instance of the (guard) class + * + * scoped_guard (name, args...) { }: + * similar to CLASS(name, scope)(args), except the variable (with the + * explicit name 'scope') is declard in a for-loop such that its scope is + * bound to the next (compound) statement. + * + */ + +#define DEFINE_GUARD(_name, _type, _lock, _unlock) \ + DEFINE_CLASS(_name, _type, _unlock, ({ _lock; _T; }), _type _T) + +#define guard(_name) \ + CLASS(_name, __UNIQUE_ID(guard)) + +#define scoped_guard(_name, args...) \ + for (CLASS(_name, scope)(args), \ + *done = NULL; !done; done = (void *)1) + +/* + * Additional helper macros for generating lock guards with types, either for + * locks that don't have a native type (eg. RCU, preempt) or those that need a + * 'fat' pointer (eg. spin_lock_irqsave). + * + * DEFINE_LOCK_GUARD_0(name, lock, unlock, ...) + * DEFINE_LOCK_GUARD_1(name, type, lock, unlock, ...) + * + * will result in the following type: + * + * typedef struct { + * type *lock; // 'type := void' for the _0 variant + * __VA_ARGS__; + * } class_##name##_t; + * + * As above, both _lock and _unlock are statements, except this time '_T' will + * be a pointer to the above struct. + */ + +#define __DEFINE_UNLOCK_GUARD(_name, _type, _unlock, ...) \ +typedef struct { \ + _type *lock; \ + __VA_ARGS__; \ +} class_##_name##_t; \ + \ +static inline void class_##_name##_destructor(class_##_name##_t *_T) \ +{ \ + if (_T->lock) { _unlock; } \ +} + + +#define __DEFINE_LOCK_GUARD_1(_name, _type, _lock) \ +static inline class_##_name##_t class_##_name##_constructor(_type *l) \ +{ \ + class_##_name##_t _t = { .lock = l }, *_T = &_t; \ + _lock; \ + return _t; \ +} + +#define __DEFINE_LOCK_GUARD_0(_name, _lock) \ +static inline class_##_name##_t class_##_name##_constructor(void) \ +{ \ + class_##_name##_t _t = { .lock = (void*)1 }, \ + *_T __maybe_unused = &_t; \ + _lock; \ + return _t; \ +} + +#define DEFINE_LOCK_GUARD_1(_name, _type, _lock, _unlock, ...) \ +__DEFINE_UNLOCK_GUARD(_name, _type, _unlock, __VA_ARGS__) \ +__DEFINE_LOCK_GUARD_1(_name, _type, _lock) + +#define DEFINE_LOCK_GUARD_0(_name, _lock, _unlock, ...) \ +__DEFINE_UNLOCK_GUARD(_name, void, _unlock, __VA_ARGS__) \ +__DEFINE_LOCK_GUARD_0(_name, _lock) + +#endif /* __LINUX_GUARDS_H */ diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h index 3c4de9b6c6e3e..e75768ca8c186 100644 --- a/include/linux/compiler-clang.h +++ b/include/linux/compiler-clang.h @@ -5,6 +5,15 @@ /* Compiler specific definitions for Clang compiler */ +/* + * Clang prior to 17 is being silly and considers many __cleanup() variables + * as unused (because they are, their sole purpose is to go out of scope). + * + * https://reviews.llvm.org/D152180 + */ +#undef __cleanup +#define __cleanup(func) __maybe_unused __attribute__((__cleanup__(func))) + /* same as gcc, this was present in clang-2.6 so we can assume it works * with any version that can compile the kernel */ diff --git a/include/linux/compiler_attributes.h b/include/linux/compiler_attributes.h index 3de06a8fae73b..bcad8bdaf3d36 100644 --- a/include/linux/compiler_attributes.h +++ b/include/linux/compiler_attributes.h @@ -80,6 +80,12 @@ */ #define __cold __attribute__((__cold__)) +/* + * gcc: https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html#index-cleanup-variable-attribute + * clang: https://clang.llvm.org/docs/AttributeReference.html#cleanup + */ +#define __cleanup(func) __attribute__((__cleanup__(func))) + /* * Note the long name. * diff --git a/include/linux/device.h b/include/linux/device.h index e270cb740b9e7..6dc14af3790db 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -30,6 +30,7 @@ #include #include #include +#include #include struct device; @@ -822,6 +823,9 @@ void device_unregister(struct device *dev); void device_initialize(struct device *dev); int __must_check device_add(struct device *dev); void device_del(struct device *dev); + +DEFINE_FREE(device_del, struct device *, if (_T) device_del(_T)) + int device_for_each_child(struct device *dev, void *data, int (*fn)(struct device *dev, void *data)); int device_for_each_child_reverse(struct device *dev, void *data, @@ -950,6 +954,9 @@ extern int (*platform_notify_remove)(struct device *dev); */ struct device *get_device(struct device *dev); void put_device(struct device *dev); + +DEFINE_FREE(put_device, struct device *, if (_T) put_device(_T)) + bool kill_device(struct device *dev); #ifdef CONFIG_DEVTMPFS diff --git a/include/linux/file.h b/include/linux/file.h index 39704eae83e27..6e9099d293436 100644 --- a/include/linux/file.h +++ b/include/linux/file.h @@ -10,6 +10,7 @@ #include #include #include +#include struct file; @@ -80,6 +81,8 @@ static inline void fdput_pos(struct fd f) fdput(f); } +DEFINE_CLASS(fd, struct fd, fdput(_T), fdget(fd), int fd) + extern int f_dupfd(unsigned int from, struct file *file, unsigned flags); extern int replace_fd(unsigned fd, struct file *file, unsigned flags); extern void set_close_on_exec(unsigned int fd, int flag); @@ -88,6 +91,9 @@ extern int __get_unused_fd_flags(unsigned flags, unsigned long nofile); extern int get_unused_fd_flags(unsigned flags); extern void put_unused_fd(unsigned int fd); +DEFINE_CLASS(get_unused_fd, int, if (_T >= 0) put_unused_fd(_T), + get_unused_fd_flags(flags), unsigned flags) + extern void fd_install(unsigned int fd, struct file *file); extern int __receive_fd(struct file *file, int __user *ufd, diff --git a/include/linux/irqflags.h b/include/linux/irqflags.h index 747f40e0c3260..49d472686d4cb 100644 --- a/include/linux/irqflags.h +++ b/include/linux/irqflags.h @@ -13,6 +13,7 @@ #define _LINUX_TRACE_IRQFLAGS_H #include +#include #include #include @@ -260,4 +261,10 @@ extern void warn_bogus_irq_restore(void); #define irqs_disabled_flags(flags) raw_irqs_disabled_flags(flags) +DEFINE_LOCK_GUARD_0(irq, local_irq_disable(), local_irq_enable()) +DEFINE_LOCK_GUARD_0(irqsave, + local_irq_save(_T->flags), + local_irq_restore(_T->flags), + unsigned long flags) + #endif diff --git a/include/linux/mutex.h b/include/linux/mutex.h index 8f226d460f51c..a33aa9eb9fc3b 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef CONFIG_DEBUG_LOCK_ALLOC # define __DEP_MAP_MUTEX_INITIALIZER(lockname) \ @@ -219,4 +220,7 @@ extern void mutex_unlock(struct mutex *lock); extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); +DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T)) +DEFINE_FREE(mutex, struct mutex *, if (_T) mutex_unlock(_T)) + #endif /* __LINUX_MUTEX_H */ diff --git a/include/linux/percpu.h b/include/linux/percpu.h index 5e76af742c807..c9a84532bb793 100644 --- a/include/linux/percpu.h +++ b/include/linux/percpu.h @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -134,6 +135,9 @@ extern void __init setup_per_cpu_areas(void); extern void __percpu *__alloc_percpu_gfp(size_t size, size_t align, gfp_t gfp); extern void __percpu *__alloc_percpu(size_t size, size_t align); extern void free_percpu(void __percpu *__pdata); + +DEFINE_FREE(free_percpu, void __percpu *, free_percpu(_T)) + extern phys_addr_t per_cpu_ptr_to_phys(void *addr); #define alloc_percpu_gfp(type, gfp) \ diff --git a/include/linux/preempt.h b/include/linux/preempt.h index 9c4534a69a8f7..36c4233742a15 100644 --- a/include/linux/preempt.h +++ b/include/linux/preempt.h @@ -8,6 +8,7 @@ */ #include +#include #include /* @@ -431,4 +432,8 @@ static inline void migrate_enable(void) { } #endif /* CONFIG_SMP */ +DEFINE_LOCK_GUARD_0(preempt, preempt_disable(), preempt_enable()) +DEFINE_LOCK_GUARD_0(preempt_notrace, preempt_disable_notrace(), preempt_enable_notrace()) +DEFINE_LOCK_GUARD_0(migrate, migrate_disable(), migrate_enable()) + #endif /* __LINUX_PREEMPT_H */ diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index e3b12de36e924..06d8689069f39 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -1026,4 +1027,6 @@ rcu_head_after_call_rcu(struct rcu_head *rhp, rcu_callback_t f) extern int rcu_expedited; extern int rcu_normal; +DEFINE_LOCK_GUARD_0(rcu, rcu_read_lock(), rcu_read_unlock()) + #endif /* __LINUX_RCUPDATE_H */ diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 352c6127cb90f..458a0c92cc689 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -16,6 +16,7 @@ #include #include #include +#include #ifdef CONFIG_DEBUG_LOCK_ALLOC # define __RWSEM_DEP_MAP_INIT(lockname) \ @@ -202,6 +203,13 @@ extern void up_read(struct rw_semaphore *sem); */ extern void up_write(struct rw_semaphore *sem); +DEFINE_GUARD(rwsem_read, struct rw_semaphore *, down_read(_T), up_read(_T)) +DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T)) + +DEFINE_FREE(up_read, struct rw_semaphore *, if (_T) up_read(_T)) +DEFINE_FREE(up_write, struct rw_semaphore *, if (_T) up_write(_T)) + + /* * downgrade write lock to read lock */ diff --git a/include/linux/sched/task.h b/include/linux/sched/task.h index 09edb4e61bac9..7f64bcee024a5 100644 --- a/include/linux/sched/task.h +++ b/include/linux/sched/task.h @@ -143,6 +143,8 @@ static inline void put_task_struct(struct task_struct *t) __put_task_struct(t); } +DEFINE_FREE(put_task, struct task_struct *, if (_T) put_task_struct(_T)) + static inline void put_task_struct_many(struct task_struct *t, int nr) { if (refcount_sub_and_test(nr, &t->usage)) diff --git a/include/linux/slab.h b/include/linux/slab.h index e6148a9419ae3..e969a5b872fb8 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -17,6 +17,7 @@ #include #include #include +#include /* @@ -192,6 +193,9 @@ void * __must_check krealloc(const void *objp, size_t new_size, gfp_t flags); void kfree(const void *objp); void kfree_sensitive(const void *objp); size_t __ksize(const void *objp); + +DEFINE_FREE(kfree, void *, if (_T) kfree(_T)) + size_t ksize(const void *objp); #ifdef CONFIG_PRINTK bool kmem_valid_obj(void *object); diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index d30caca0a074a..18c692162f573 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -61,6 +61,7 @@ #include #include #include +#include #include #include @@ -515,4 +516,34 @@ int __alloc_bucket_spinlocks(spinlock_t **locks, unsigned int *lock_mask, void free_bucket_spinlocks(spinlock_t *locks); +DEFINE_LOCK_GUARD_1(raw_spinlock, raw_spinlock_t, + raw_spin_lock(_T->lock), + raw_spin_unlock(_T->lock)) + +DEFINE_LOCK_GUARD_1(raw_spinlock_nested, raw_spinlock_t, + raw_spin_lock_nested(_T->lock, SINGLE_DEPTH_NESTING), + raw_spin_unlock(_T->lock)) + +DEFINE_LOCK_GUARD_1(raw_spinlock_irq, raw_spinlock_t, + raw_spin_lock_irq(_T->lock), + raw_spin_unlock_irq(_T->lock)) + +DEFINE_LOCK_GUARD_1(raw_spinlock_irqsave, raw_spinlock_t, + raw_spin_lock_irqsave(_T->lock, _T->flags), + raw_spin_unlock_irqrestore(_T->lock, _T->flags), + unsigned long flags) + +DEFINE_LOCK_GUARD_1(spinlock, spinlock_t, + spin_lock(_T->lock), + spin_unlock(_T->lock)) + +DEFINE_LOCK_GUARD_1(spinlock_irq, spinlock_t, + spin_lock_irq(_T->lock), + spin_unlock_irq(_T->lock)) + +DEFINE_LOCK_GUARD_1(spinlock_irqsave, spinlock_t, + spin_lock_irqsave(_T->lock, _T->flags), + spin_unlock_irqrestore(_T->lock, _T->flags), + unsigned long flags) + #endif /* __LINUX_SPINLOCK_H */ diff --git a/include/linux/srcu.h b/include/linux/srcu.h index e6011a9975af2..e94687215fbe1 100644 --- a/include/linux/srcu.h +++ b/include/linux/srcu.h @@ -211,4 +211,9 @@ static inline void smp_mb__after_srcu_read_unlock(void) /* __srcu_read_unlock has smp_mb() internally so nothing to do here. */ } +DEFINE_LOCK_GUARD_1(srcu, struct srcu_struct, + _T->idx = srcu_read_lock(_T->lock), + srcu_read_unlock(_T->lock, _T->idx), + int idx) + #endif diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 63f311770a86e..ac5b378bbe052 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -4897,7 +4897,7 @@ sub process { if|for|while|switch|return|case| volatile|__volatile__| __attribute__|format|__extension__| - asm|__asm__)$/x) + asm|__asm__|scoped_guard)$/x) { # cpp #define statements have non-optional spaces, ie # if there is a space between the name and the open From c97aa2c33ff79cdfbd68860c7b737fb9e962eac1 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Sun, 17 Sep 2023 13:22:17 +0200 Subject: [PATCH 018/227] cleanup: Add conditional guard support stable inclusion from stable-v6.6.70 commit 873df38bdf42c076842b0e55e2f410e8a4662347 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBOHV1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=873df38bdf42c076842b0e55e2f410e8a4662347 -------------------------------- [ Upstream commit e4ab322fbaaaf84b23d6cb0e3317a7f68baf36dc ] Adds: - DEFINE_GUARD_COND() / DEFINE_LOCK_GUARD_1_COND() to extend existing guards with conditional lock primitives, eg. mutex_trylock(), mutex_lock_interruptible(). nb. both primitives allow NULL 'locks', which cause the lock to fail (obviously). - extends scoped_guard() to not take the body when the the conditional guard 'fails'. eg. scoped_guard (mutex_intr, &task->signal_cred_guard_mutex) { ... } will only execute the body when the mutex is held. - provides scoped_cond_guard(name, fail, args...); which extends scoped_guard() to do fail when the lock-acquire fails. Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20231102110706.460851167%40infradead.org Stable-dep-of: fcc22ac5baf0 ("cleanup: Adjust scoped_guard() macros to avoid potential warning") Signed-off-by: Sasha Levin Signed-off-by: Wen Zhiwei Signed-off-by: huwentao Conflicts: include/linux/spinlock.h --- include/linux/cleanup.h | 52 +++++++++++++++++++++++++++++++++++++--- include/linux/mutex.h | 3 ++- include/linux/rwsem.h | 8 +++---- include/linux/spinlock.h | 15 ++++++++++++ 4 files changed, 70 insertions(+), 8 deletions(-) diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index 53f1a7a932b08..6d7bfa899df02 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -92,25 +92,55 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ * trivial wrapper around DEFINE_CLASS() above specifically * for locks. * + * DEFINE_GUARD_COND(name, ext, condlock) + * wrapper around EXTEND_CLASS above to add conditional lock + * variants to a base class, eg. mutex_trylock() or + * mutex_lock_interruptible(). + * * guard(name): - * an anonymous instance of the (guard) class + * an anonymous instance of the (guard) class, not recommended for + * conditional locks. * * scoped_guard (name, args...) { }: * similar to CLASS(name, scope)(args), except the variable (with the * explicit name 'scope') is declard in a for-loop such that its scope is * bound to the next (compound) statement. * + * for conditional locks the loop body is skipped when the lock is not + * acquired. + * + * scoped_cond_guard (name, fail, args...) { }: + * similar to scoped_guard(), except it does fail when the lock + * acquire fails. + * */ #define DEFINE_GUARD(_name, _type, _lock, _unlock) \ - DEFINE_CLASS(_name, _type, _unlock, ({ _lock; _T; }), _type _T) + DEFINE_CLASS(_name, _type, if (_T) { _unlock; }, ({ _lock; _T; }), _type _T); \ + static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \ + { return *_T; } + +#define DEFINE_GUARD_COND(_name, _ext, _condlock) \ + EXTEND_CLASS(_name, _ext, \ + ({ void *_t = _T; if (_T && !(_condlock)) _t = NULL; _t; }), \ + class_##_name##_t _T) \ + static inline void * class_##_name##_ext##_lock_ptr(class_##_name##_t *_T) \ + { return class_##_name##_lock_ptr(_T); } #define guard(_name) \ CLASS(_name, __UNIQUE_ID(guard)) +#define __guard_ptr(_name) class_##_name##_lock_ptr + #define scoped_guard(_name, args...) \ for (CLASS(_name, scope)(args), \ - *done = NULL; !done; done = (void *)1) + *done = NULL; __guard_ptr(_name)(&scope) && !done; done = (void *)1) + +#define scoped_cond_guard(_name, _fail, args...) \ + for (CLASS(_name, scope)(args), \ + *done = NULL; !done; done = (void *)1) \ + if (!__guard_ptr(_name)(&scope)) _fail; \ + else /* * Additional helper macros for generating lock guards with types, either for @@ -119,6 +149,7 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ * * DEFINE_LOCK_GUARD_0(name, lock, unlock, ...) * DEFINE_LOCK_GUARD_1(name, type, lock, unlock, ...) + * DEFINE_LOCK_GUARD_1_COND(name, ext, condlock) * * will result in the following type: * @@ -140,6 +171,11 @@ typedef struct { \ static inline void class_##_name##_destructor(class_##_name##_t *_T) \ { \ if (_T->lock) { _unlock; } \ +} \ + \ +static inline void *class_##_name##_lock_ptr(class_##_name##_t *_T) \ +{ \ + return _T->lock; \ } @@ -168,4 +204,14 @@ __DEFINE_LOCK_GUARD_1(_name, _type, _lock) __DEFINE_UNLOCK_GUARD(_name, void, _unlock, __VA_ARGS__) \ __DEFINE_LOCK_GUARD_0(_name, _lock) +#define DEFINE_LOCK_GUARD_1_COND(_name, _ext, _condlock) \ + EXTEND_CLASS(_name, _ext, \ + ({ class_##_name##_t _t = { .lock = l }, *_T = &_t;\ + if (_T->lock && !(_condlock)) _T->lock = NULL; \ + _t; }), \ + typeof_member(class_##_name##_t, lock) l) \ + static inline void * class_##_name##_ext##_lock_ptr(class_##_name##_t *_T) \ + { return class_##_name##_lock_ptr(_T); } + + #endif /* __LINUX_GUARDS_H */ diff --git a/include/linux/mutex.h b/include/linux/mutex.h index a33aa9eb9fc3b..95d11308f995d 100644 --- a/include/linux/mutex.h +++ b/include/linux/mutex.h @@ -221,6 +221,7 @@ extern void mutex_unlock(struct mutex *lock); extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock); DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T)) -DEFINE_FREE(mutex, struct mutex *, if (_T) mutex_unlock(_T)) +DEFINE_GUARD_COND(mutex, _try, mutex_trylock(_T)) +DEFINE_GUARD_COND(mutex, _intr, mutex_lock_interruptible(_T) == 0) #endif /* __LINUX_MUTEX_H */ diff --git a/include/linux/rwsem.h b/include/linux/rwsem.h index 458a0c92cc689..c5d9d06a1f3c9 100644 --- a/include/linux/rwsem.h +++ b/include/linux/rwsem.h @@ -204,11 +204,11 @@ extern void up_read(struct rw_semaphore *sem); extern void up_write(struct rw_semaphore *sem); DEFINE_GUARD(rwsem_read, struct rw_semaphore *, down_read(_T), up_read(_T)) -DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T)) - -DEFINE_FREE(up_read, struct rw_semaphore *, if (_T) up_read(_T)) -DEFINE_FREE(up_write, struct rw_semaphore *, if (_T) up_write(_T)) +DEFINE_GUARD_COND(rwsem_read, _try, down_read_trylock(_T)) +DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T) == 0) +DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T)) +DEFINE_GUARD_COND(rwsem_write, _try, down_write_trylock(_T)) /* * downgrade write lock to read lock diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h index 18c692162f573..23aa3e023ab1d 100644 --- a/include/linux/spinlock.h +++ b/include/linux/spinlock.h @@ -520,6 +520,8 @@ DEFINE_LOCK_GUARD_1(raw_spinlock, raw_spinlock_t, raw_spin_lock(_T->lock), raw_spin_unlock(_T->lock)) +DEFINE_LOCK_GUARD_1_COND(raw_spinlock, _try, raw_spin_trylock(_T->lock)) + DEFINE_LOCK_GUARD_1(raw_spinlock_nested, raw_spinlock_t, raw_spin_lock_nested(_T->lock, SINGLE_DEPTH_NESTING), raw_spin_unlock(_T->lock)) @@ -528,22 +530,35 @@ DEFINE_LOCK_GUARD_1(raw_spinlock_irq, raw_spinlock_t, raw_spin_lock_irq(_T->lock), raw_spin_unlock_irq(_T->lock)) +DEFINE_LOCK_GUARD_1_COND(raw_spinlock_irq, _try, raw_spin_trylock_irq(_T->lock)) + DEFINE_LOCK_GUARD_1(raw_spinlock_irqsave, raw_spinlock_t, raw_spin_lock_irqsave(_T->lock, _T->flags), raw_spin_unlock_irqrestore(_T->lock, _T->flags), unsigned long flags) +DEFINE_LOCK_GUARD_1_COND(raw_spinlock_irqsave, _try, + raw_spin_trylock_irqsave(_T->lock, _T->flags)) + DEFINE_LOCK_GUARD_1(spinlock, spinlock_t, spin_lock(_T->lock), spin_unlock(_T->lock)) +DEFINE_LOCK_GUARD_1_COND(spinlock, _try, spin_trylock(_T->lock)) + DEFINE_LOCK_GUARD_1(spinlock_irq, spinlock_t, spin_lock_irq(_T->lock), spin_unlock_irq(_T->lock)) +DEFINE_LOCK_GUARD_1_COND(spinlock_irq, _try, + spin_trylock_irq(_T->lock)) + DEFINE_LOCK_GUARD_1(spinlock_irqsave, spinlock_t, spin_lock_irqsave(_T->lock, _T->flags), spin_unlock_irqrestore(_T->lock, _T->flags), unsigned long flags) +DEFINE_LOCK_GUARD_1_COND(spinlock_irqsave, _try, + spin_trylock_irqsave(_T->lock, _T->flags)) + #endif /* __LINUX_SPINLOCK_H */ From 72b9385090915b02f2378d37d34d387d03e6ffc0 Mon Sep 17 00:00:00 2001 From: Przemek Kitszel Date: Fri, 18 Oct 2024 13:38:14 +0200 Subject: [PATCH 019/227] cleanup: Adjust scoped_guard() macros to avoid potential warning stable inclusion from stable-v6.6.70 commit 8fa6f680b5aad8f0aceacb1404fc8464269167ed category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBOHV1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=8fa6f680b5aad8f0aceacb1404fc8464269167ed -------------------------------- [ Upstream commit fcc22ac5baf06dd17193de44b60dbceea6461983 ] Change scoped_guard() and scoped_cond_guard() macros to make reasoning about them easier for static analysis tools (smatch, compiler diagnostics), especially to enable them to tell if the given usage of scoped_guard() is with a conditional lock class (interruptible-locks, try-locks) or not (like simple mutex_lock()). Add compile-time error if scoped_cond_guard() is used for non-conditional lock class. Beyond easier tooling and a little shrink reported by bloat-o-meter this patch enables developer to write code like: int foo(struct my_drv *adapter) { scoped_guard(spinlock, &adapter->some_spinlock) return adapter->spinlock_protected_var; } Current scoped_guard() implementation does not support that, due to compiler complaining: error: control reaches end of non-void function [-Werror=return-type] Technical stuff about the change: scoped_guard() macro uses common idiom of using "for" statement to declare a scoped variable. Unfortunately, current logic is too hard for compiler diagnostics to be sure that there is exactly one loop step; fix that. To make any loop so trivial that there is no above warning, it must not depend on any non-const variable to tell if there are more steps. There is no obvious solution for that in C, but one could use the compound statement expression with "goto" jumping past the "loop", effectively leaving only the subscope part of the loop semantics. More impl details: one more level of macro indirection is now needed to avoid duplicating label names; I didn't spot any other place that is using the "for (...; goto label) if (0) label: break;" idiom, so it's not packed for reuse beyond scoped_guard() family, what makes actual macros code cleaner. There was also a need to introduce const true/false variable per lock class, it is used to aid compiler diagnostics reasoning about "exactly 1 step" loops (note that converting that to function would undo the whole benefit). Big thanks to Andy Shevchenko for help on this patch, both internal and public, ranging from whitespace/formatting, through commit message clarifications, general improvements, ending with presenting alternative approaches - all despite not even liking the idea. Big thanks to Dmitry Torokhov for the idea of compile-time check for scoped_cond_guard() (to use it only with conditional locsk), and general improvements for the patch. Big thanks to David Lechner for idea to cover also scoped_cond_guard(). Signed-off-by: Przemek Kitszel Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Dmitry Torokhov Link: https://lkml.kernel.org/r/20241018113823.171256-1-przemyslaw.kitszel@intel.com Signed-off-by: Sasha Levin Signed-off-by: Wen Zhiwei Signed-off-by: huwentao --- include/linux/cleanup.h | 52 +++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index 6d7bfa899df02..f0c6d1d45e679 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -113,14 +113,20 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ * similar to scoped_guard(), except it does fail when the lock * acquire fails. * + * Only for conditional locks. */ +#define __DEFINE_CLASS_IS_CONDITIONAL(_name, _is_cond) \ +static __maybe_unused const bool class_##_name##_is_conditional = _is_cond + #define DEFINE_GUARD(_name, _type, _lock, _unlock) \ + __DEFINE_CLASS_IS_CONDITIONAL(_name, false); \ DEFINE_CLASS(_name, _type, if (_T) { _unlock; }, ({ _lock; _T; }), _type _T); \ static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \ { return *_T; } #define DEFINE_GUARD_COND(_name, _ext, _condlock) \ + __DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true); \ EXTEND_CLASS(_name, _ext, \ ({ void *_t = _T; if (_T && !(_condlock)) _t = NULL; _t; }), \ class_##_name##_t _T) \ @@ -131,17 +137,40 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ CLASS(_name, __UNIQUE_ID(guard)) #define __guard_ptr(_name) class_##_name##_lock_ptr +#define __is_cond_ptr(_name) class_##_name##_is_conditional -#define scoped_guard(_name, args...) \ - for (CLASS(_name, scope)(args), \ - *done = NULL; __guard_ptr(_name)(&scope) && !done; done = (void *)1) - -#define scoped_cond_guard(_name, _fail, args...) \ - for (CLASS(_name, scope)(args), \ - *done = NULL; !done; done = (void *)1) \ - if (!__guard_ptr(_name)(&scope)) _fail; \ - else - +/* + * Helper macro for scoped_guard(). + * + * Note that the "!__is_cond_ptr(_name)" part of the condition ensures that + * compiler would be sure that for the unconditional locks the body of the + * loop (caller-provided code glued to the else clause) could not be skipped. + * It is needed because the other part - "__guard_ptr(_name)(&scope)" - is too + * hard to deduce (even if could be proven true for unconditional locks). + */ +#define __scoped_guard(_name, _label, args...) \ + for (CLASS(_name, scope)(args); \ + __guard_ptr(_name)(&scope) || !__is_cond_ptr(_name); \ + ({ goto _label; })) \ + if (0) { \ +_label: \ + break; \ + } else + +#define scoped_guard(_name, args...) \ + __scoped_guard(_name, __UNIQUE_ID(label), args) + +#define __scoped_cond_guard(_name, _fail, _label, args...) \ + for (CLASS(_name, scope)(args); true; ({ goto _label; })) \ + if (!__guard_ptr(_name)(&scope)) { \ + BUILD_BUG_ON(!__is_cond_ptr(_name)); \ + _fail; \ +_label: \ + break; \ + } else + +#define scoped_cond_guard(_name, _fail, args...) \ + __scoped_cond_guard(_name, _fail, __UNIQUE_ID(label), args) /* * Additional helper macros for generating lock guards with types, either for * locks that don't have a native type (eg. RCU, preempt) or those that need a @@ -197,14 +226,17 @@ static inline class_##_name##_t class_##_name##_constructor(void) \ } #define DEFINE_LOCK_GUARD_1(_name, _type, _lock, _unlock, ...) \ +__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \ __DEFINE_UNLOCK_GUARD(_name, _type, _unlock, __VA_ARGS__) \ __DEFINE_LOCK_GUARD_1(_name, _type, _lock) #define DEFINE_LOCK_GUARD_0(_name, _lock, _unlock, ...) \ +__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \ __DEFINE_UNLOCK_GUARD(_name, void, _unlock, __VA_ARGS__) \ __DEFINE_LOCK_GUARD_0(_name, _lock) #define DEFINE_LOCK_GUARD_1_COND(_name, _ext, _condlock) \ + __DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true); \ EXTEND_CLASS(_name, _ext, \ ({ class_##_name##_t _t = { .lock = l }, *_T = &_t;\ if (_T->lock && !(_condlock)) _T->lock = NULL; \ From de813f7a20f2f64ac171cdf6a3ad3f818a8cb8d4 Mon Sep 17 00:00:00 2001 From: Uros Bizjak Date: Mon, 19 Aug 2024 09:41:15 +0200 Subject: [PATCH 020/227] cleanup: Remove address space of returned pointer stable inclusion from stable-v6.6.70 commit 8c5ad189e90f5f1470c7c93b22320976dc1dc777 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBOHV1 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=8c5ad189e90f5f1470c7c93b22320976dc1dc777 -------------------------------- [ Upstream commit f730fd535fc51573f982fad629f2fc6b4a0cde2f ] Guard functions in local_lock.h are defined using DEFINE_GUARD() and DEFINE_LOCK_GUARD_1() macros having lock type defined as pointer in the percpu address space. The functions, defined by these macros return value in generic address space, causing: cleanup.h:157:18: error: return from pointer to non-enclosed address space and cleanup.h:214:18: error: return from pointer to non-enclosed address space when strict percpu checks are enabled. Add explicit casts to remove address space of the returned pointer. Found by GCC's named address space checks. Fixes: e4ab322fbaaa ("cleanup: Add conditional guard support") Signed-off-by: Uros Bizjak Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20240819074124.143565-1-ubizjak@gmail.com Signed-off-by: Sasha Levin Signed-off-by: Wen Zhiwei Signed-off-by: huwentao --- include/linux/cleanup.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h index f0c6d1d45e679..64b8600eb8c0e 100644 --- a/include/linux/cleanup.h +++ b/include/linux/cleanup.h @@ -123,7 +123,7 @@ static __maybe_unused const bool class_##_name##_is_conditional = _is_cond __DEFINE_CLASS_IS_CONDITIONAL(_name, false); \ DEFINE_CLASS(_name, _type, if (_T) { _unlock; }, ({ _lock; _T; }), _type _T); \ static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \ - { return *_T; } + { return (void *)(__force unsigned long)*_T; } #define DEFINE_GUARD_COND(_name, _ext, _condlock) \ __DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true); \ @@ -204,7 +204,7 @@ static inline void class_##_name##_destructor(class_##_name##_t *_T) \ \ static inline void *class_##_name##_lock_ptr(class_##_name##_t *_T) \ { \ - return _T->lock; \ + return (void *)(__force unsigned long)_T->lock; \ } From 82100102d9d36fdea3d7d97af9c384eace7c4da5 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 12 Sep 2022 10:03:59 -0600 Subject: [PATCH 021/227] hwtracing: hisi_ptt: Fix up for "iommu/dma: Make header private" drivers/hwtracing/ptt/hisi_ptt.c:13:10: fatal error: linux/dma-iommu.h: No such file or directory 13 | #include | ^~~~~~~~~~~~~~~~~~~ Caused by: commit ff0de066b463 ("hwtracing: hisi_ptt: Add trace function support for HiSilicon PCIe Tune and Trace device") interacting with: commit f2042ed21da7 ("iommu/dma: Make header private") from the iommu tree. Signed-off-by: Stephen Rothwell Acked-by: Robin Murphy Acked-by: Yicong Yang [Fixed subject line and added changelog text] Signed-off-by: Mathieu Poirier Signed-off-by: huwentao --- drivers/hwtracing/ptt/hisi_ptt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 0e77bf38f6588..97d244f48ca2b 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include From d84293085eb53df1aafd03cf08d9aa885be6af1d Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Tue, 10 Oct 2023 16:47:29 +0800 Subject: [PATCH 022/227] hwtracing: hisi_ptt: Optimize the trace data committing maillist inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I944FB CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7a527d4d9273bdd4f355b728fd46d703f33c1d1d ---------------------------------------------------------------------- In the current implementation, there're 4*4MiB trace buffer and hardware will fill the buffer one by one. The driver will get notified if one buffer is full and then copy data to the AUX buffer. If there's no enough room for the next trace buffer, we'll commit the AUX buffer to the perf core and try to apply a new one. In a typical configuration the AUX buffer will be 16MiB, so we'll commit the data after the whole AUX buffer is occupied. Then the driver cannot apply a new AUX buffer immediately until the committed data is consumed by userspace and then there's room in the AUX buffer again. This patch tries to optimize this by commit the data after one single trace buffer is filled. Since there's still room in the AUX buffer, driver can apply a new one without failure and don't need to wait for the userspace to consume the data. Signed-off-by: Yicong Yang Acked-by: Jonathan Cameron Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20231010084731.30450-4-yangyicong@huawei.com Signed-off-by: shiyongbang Signed-off-by: huwentao --- drivers/hwtracing/ptt/hisi_ptt.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 97d244f48ca2b..ffc4a8436a2f3 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -274,15 +274,14 @@ static int hisi_ptt_update_aux(struct hisi_ptt *hisi_ptt, int index, bool stop) buf->pos += size; /* - * Just commit the traced data if we're going to stop. Otherwise if the - * resident AUX buffer cannot contain the data of next trace buffer, - * apply a new one. + * Always commit the data to the AUX buffer in time to make sure + * userspace got enough time to consume the data. + * + * If we're not going to stop, apply a new one and check whether + * there's enough room for the next trace. */ - if (stop) { - perf_aux_output_end(handle, buf->pos); - } else if (buf->length - buf->pos < HISI_PTT_TRACE_BUF_SIZE) { - perf_aux_output_end(handle, buf->pos); - + perf_aux_output_end(handle, size); + if (!stop) { buf = perf_aux_output_begin(handle, event); if (!buf) return -EINVAL; From 82ff15a4ef3fb76278abe184519d27c70d5de6d8 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 20 Mar 2024 08:51:21 +0800 Subject: [PATCH 023/227] perf header: Fix one memory leakage in perf_event__fprintf_event_update() stable inclusion from stable-v6.6.14 commit f8d47ca6785ce0d5998b24ec3857649fb8e6f9f5 bugzilla: https://gitee.com/openeuler/kernel/issues/I99TJK Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=f8d47ca6785ce0d5998b24ec3857649fb8e6f9f5 -------------------------------- [ Upstream commit 813900d19b923fc1b241c1ce292472f68066092b ] When dump the raw trace by `perf report -D` ASan reports a memory leakage in perf_event__fprintf_event_update(). It shows that we allocated a temporary cpumap for dumping the CPUs but doesn't release it and it's not used elsewhere. Fix this by free the cpumap after the dumping. Fixes: c853f9394b7bc189 ("perf tools: Add perf_event__fprintf_event_update function") Reviewed-by: Ian Rogers Signed-off-by: Yicong Yang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Jonathan Cameron Cc: Junhao He Cc: Mark Rutland Cc: Peter Zijlstra Cc: linuxarm@huawei.com Link: https://lore.kernel.org/r/20231207081635.8427-2-yangyicong@huawei.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin Signed-off-by: ZhangPeng Signed-off-by: huwentao Conflicts: tools/perf/util/header.c --- tools/perf/util/header.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 1d68d556f5939..de5ba6b93efad 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -4246,9 +4246,10 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) ret += fprintf(fp, "... "); map = cpu_map__new_data(&ev_cpus->cpus); - if (map) + if (map) { ret += cpu_map__fprintf(map, fp); - else + perf_cpu_map__put(map); + } else ret += fprintf(fp, "failed to get cpus\n"); break; default: From 39f8d8d7ab94354f230f810885cdb823914e84c6 Mon Sep 17 00:00:00 2001 From: Jay Fang Date: Tue, 30 Jul 2024 09:16:39 +0800 Subject: [PATCH 024/227] PCI/ASPM: Update ASPM sysfs on MFD function removal to avoid use-after-free driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IB2LVJ CVE: NA -------------------------------- From 'commit 456d8aa37d0f ("PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free")' we know that PCIe spec r6.0, sec 7.5.3.7, recommends that software program the same ASPM Control(pcie_link_state) value in all functions of multi-function devices, and free the pcie_link_state when any child function is removed. However, ASPM Control sysfs is still visible to other children even if it has been removed by any child function, and careless use it will trigger use-after-free error, e.g.: # lspci -tv -[0000:16]---00.0-[17]--+-00.0 Device 19e5:0222 \-00.1 Device 19e5:0222 # echo 1 > /sys/bus/pci/devices/0000:17:00.0/remove //pcie_link_state will be released # echo 1 > /sys/bus/pci/devices/0000:17:00.1/link/l1_aspm //will trigger error Unable to handle kernel NULL pointer dereference at virtual address 0000000000000030 Call trace: aspm_attr_store_common.constprop.0+0x10c/0x154 l1_aspm_store+0x24/0x30 dev_attr_store+0x20/0x34 sysfs_kf_write+0x4c/0x5c We can solve this problem by updating the ASPM Control sysfs of all children immediately after ASPM Control have been freed. Extra note: The Linux community plans to optimize to the pcie_link_state management code.For details,see the following link: https://patchwork.kernel.org/project/linux-pci/patch/ 20240730011639.2590846-1-f.fangjian@huawei.com/ Fixes: 456d8aa37d0f ("PCI/ASPM: Disable ASPM on MFD function removal to avoid use-after-free") Signed-off-by: Jay Fang Signed-off-by: lujunhua Signed-off-by: huwentao --- drivers/pci/pcie/aspm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index e3f81948ce724..9f41e26b3abe1 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -1034,6 +1034,8 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) pcie_config_aspm_path(parent_link); } + pcie_aspm_update_sysfs_visibility(parent); + mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); } From abea11f024096b8fd13136e1a069b6565e897035 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Sat, 15 Feb 2025 17:26:12 +0800 Subject: [PATCH 025/227] hwtracing: hisi_ptt: Initialize the filter sysfs attribute when allocation driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBNZ64 CVE: NA -------------------------------- The filter sysfs attribute is initialized when register to the sysfs. This is unnecessary and could be done when allocation without distinguish the filter type. After the changes above, we don't need a wrapper for initializing and registering the filter's sysfs attributes. Remove them and call the sysfs creating/removing functions in place. Fixes: 6373c463ac89 ("hwtracing: hisi_ptt: Export available filters through sysfs") Signed-off-by: Yicong Yang Signed-off-by: lujunhua Signed-off-by: huwentao --- drivers/hwtracing/ptt/hisi_ptt.c | 92 ++++++++++---------------------- 1 file changed, 29 insertions(+), 63 deletions(-) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index ffc4a8436a2f3..1b9ec18f0b4a3 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -368,6 +368,19 @@ static void hisi_ptt_del_free_filter(struct hisi_ptt *hisi_ptt, kfree(filter); } +static ssize_t hisi_ptt_filter_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct hisi_ptt_filter_desc *filter; + unsigned long filter_val; + + filter = container_of(attr, struct hisi_ptt_filter_desc, attr); + filter_val = hisi_ptt_get_filter_val(filter->devid, filter->is_port) | + (filter->is_port ? HISI_PTT_PMU_FILTER_IS_PORT : 0); + + return sysfs_emit(buf, "0x%05lx\n", filter_val); +} + static struct hisi_ptt_filter_desc * hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port) { @@ -396,6 +409,11 @@ hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port) filter->is_port = is_port; filter->devid = devid; + sysfs_attr_init(&filter->attr.attr); + filter->attr.attr.name = filter->name; + filter->attr.attr.mode = 0400; /* DEVICE_ATTR_ADMIN_RO */ + filter->attr.show = hisi_ptt_filter_show; + if (filter->is_port) { list_add_tail(&filter->list, &hisi_ptt->port_filters); @@ -408,74 +426,18 @@ hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port) return filter; } -static ssize_t hisi_ptt_filter_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct hisi_ptt_filter_desc *filter; - unsigned long filter_val; - - filter = container_of(attr, struct hisi_ptt_filter_desc, attr); - filter_val = hisi_ptt_get_filter_val(filter->devid, filter->is_port) | - (filter->is_port ? HISI_PTT_PMU_FILTER_IS_PORT : 0); - - return sysfs_emit(buf, "0x%05lx\n", filter_val); -} - -static int hisi_ptt_create_rp_filter_attr(struct hisi_ptt *hisi_ptt, - struct hisi_ptt_filter_desc *filter) -{ - struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; - - sysfs_attr_init(&filter->attr.attr); - filter->attr.attr.name = filter->name; - filter->attr.attr.mode = 0400; /* DEVICE_ATTR_ADMIN_RO */ - filter->attr.show = hisi_ptt_filter_show; - - return sysfs_add_file_to_group(kobj, &filter->attr.attr, - HISI_PTT_RP_FILTERS_GRP_NAME); -} - -static void hisi_ptt_remove_rp_filter_attr(struct hisi_ptt *hisi_ptt, - struct hisi_ptt_filter_desc *filter) -{ - struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; - - sysfs_remove_file_from_group(kobj, &filter->attr.attr, - HISI_PTT_RP_FILTERS_GRP_NAME); -} - -static int hisi_ptt_create_req_filter_attr(struct hisi_ptt *hisi_ptt, - struct hisi_ptt_filter_desc *filter) -{ - struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; - - sysfs_attr_init(&filter->attr.attr); - filter->attr.attr.name = filter->name; - filter->attr.attr.mode = 0400; /* DEVICE_ATTR_ADMIN_RO */ - filter->attr.show = hisi_ptt_filter_show; - - return sysfs_add_file_to_group(kobj, &filter->attr.attr, - HISI_PTT_REQ_FILTERS_GRP_NAME); -} - -static void hisi_ptt_remove_req_filter_attr(struct hisi_ptt *hisi_ptt, - struct hisi_ptt_filter_desc *filter) -{ - struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; - - sysfs_remove_file_from_group(kobj, &filter->attr.attr, - HISI_PTT_REQ_FILTERS_GRP_NAME); -} - static int hisi_ptt_create_filter_attr(struct hisi_ptt *hisi_ptt, struct hisi_ptt_filter_desc *filter) { + struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; int ret; if (filter->is_port) - ret = hisi_ptt_create_rp_filter_attr(hisi_ptt, filter); + ret = sysfs_add_file_to_group(kobj, &filter->attr.attr, + HISI_PTT_RP_FILTERS_GRP_NAME); else - ret = hisi_ptt_create_req_filter_attr(hisi_ptt, filter); + ret = sysfs_add_file_to_group(kobj, &filter->attr.attr, + HISI_PTT_REQ_FILTERS_GRP_NAME); if (ret) pci_err(hisi_ptt->pdev, "failed to create sysfs attribute for filter %s\n", @@ -487,10 +449,14 @@ static int hisi_ptt_create_filter_attr(struct hisi_ptt *hisi_ptt, static void hisi_ptt_remove_filter_attr(struct hisi_ptt *hisi_ptt, struct hisi_ptt_filter_desc *filter) { + struct kobject *kobj = &hisi_ptt->hisi_ptt_pmu.dev->kobj; + if (filter->is_port) - hisi_ptt_remove_rp_filter_attr(hisi_ptt, filter); + sysfs_remove_file_from_group(kobj, &filter->attr.attr, + HISI_PTT_RP_FILTERS_GRP_NAME); else - hisi_ptt_remove_req_filter_attr(hisi_ptt, filter); + sysfs_remove_file_from_group(kobj, &filter->attr.attr, + HISI_PTT_REQ_FILTERS_GRP_NAME); } static void hisi_ptt_remove_all_filter_attributes(void *data) From 64d505b7cd81b5fed1340186d91bc0d30318f105 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 24 Feb 2025 22:03:12 +0800 Subject: [PATCH 026/227] hwtracing: hisi_ptt: Check duplicate filters before allocation driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBNZ64 CVE: NA -------------------------------- Abnormally it's reported duplicated filters added in the filter list, though the filter we created should be unique maintained by the PCIe framework. Add a duplicate filters check before allocation to prevent this case. Fixes: 6373c463ac89 ("hwtracing: hisi_ptt: Export available filters through sysfs") Signed-off-by: Yicong Yang Signed-off-by: lujunhua Signed-off-by: huwentao --- drivers/hwtracing/ptt/hisi_ptt.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/hwtracing/ptt/hisi_ptt.c b/drivers/hwtracing/ptt/hisi_ptt.c index 1b9ec18f0b4a3..00053987d7c01 100644 --- a/drivers/hwtracing/ptt/hisi_ptt.c +++ b/drivers/hwtracing/ptt/hisi_ptt.c @@ -381,6 +381,32 @@ static ssize_t hisi_ptt_filter_show(struct device *dev, struct device_attribute return sysfs_emit(buf, "0x%05lx\n", filter_val); } +static int hisi_ptt_check_duplicated_filters(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port) +{ + struct hisi_ptt_filter_desc *filter; + struct list_head *target_list; + u8 devfn = devid & 0xff; + + if (is_port) + target_list = &hisi_ptt->port_filters; + else + target_list = &hisi_ptt->req_filters; + + list_for_each_entry(filter, target_list, list) { + if (filter->devid == devid) { + pci_warn(hisi_ptt->pdev, + "ignore duplicated filter %04x:%02x:%02x.%d\n", + pci_domain_nr(hisi_ptt->pdev->bus), + PCI_BUS_NUM(devid), PCI_SLOT(devfn), + PCI_FUNC(devfn)); + + return -EEXIST; + } + } + + return 0; +} + static struct hisi_ptt_filter_desc * hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port) { @@ -388,6 +414,9 @@ hisi_ptt_alloc_add_filter(struct hisi_ptt *hisi_ptt, u16 devid, bool is_port) u8 devfn = devid & 0xff; char *filter_name; + if (hisi_ptt_check_duplicated_filters(hisi_ptt, devid, is_port)) + return NULL; + filter_name = kasprintf(GFP_KERNEL, "%04x:%02x:%02x.%d", pci_domain_nr(hisi_ptt->pdev->bus), PCI_BUS_NUM(devid), PCI_SLOT(devfn), PCI_FUNC(devfn)); if (!filter_name) { From 4d1aaabebb79080e09386a87b82c21a59bd05106 Mon Sep 17 00:00:00 2001 From: Smita Koralahalli Date: Wed, 7 Feb 2024 18:18:54 +0000 Subject: [PATCH 027/227] PCI/DPC: Ignore Surprise Down error on hot removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mainline inclusion from mainline-v6.9-rc1 commit 2ae8fbbe1cd42a6624d345151859982cba89bbd3 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBWFCP CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2ae8fbbe1cd42a6624d345151859982cba89bbd3 ---------------------------------------------------------------------- MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to PCIe r6.0 sec 6.7.6 [1], async removal with DPC may result in surprise down error. This error is expected and is just a side-effect of async remove. Ignore surprise down error generated as a side-effect of async remove. Typically, this error is benign as the pciehp handler invoked by PDC or/and DLLSC alongside DPC, de-enumerates and brings down the device appropriately, but the error messages might confuse users. Get rid of these irritating log messages with a 1s delay while pciehp waits for DPC recovery. The implementation is as follows: On an async remove a DPC is triggered along with a Presence Detect State change and/or DLL State Change. Determine it's an async remove by checking for DPC Trigger Status in DPC Status Register and Surprise Down Error Status in AER Uncorrected Error Status to be non-zero. If true, treat the DPC event as a side-effect of async remove, clear the error status registers and continue with hot-plug tear down routines. If not, follow the existing routine to handle AER and DPC errors. Masking Surprise Down Errors was explored as an alternative approach, but discarded due to the odd behavior that masking only avoids the interrupt, but still records an error per PCIe r6.0, sec 6.2.3.2.2. That stale error would be reported the next time some error other than Surprise Down is handled. Dmesg before: pcieport 0000:00:01.4: DPC: containment event, status:0x1f01 source:0x0000 pcieport 0000:00:01.4: DPC: unmasked uncorrectable error detected pcieport 0000:00:01.4: PCIe Bus Error: severity=Uncorrected (Fatal), type=Transaction Layer, (Receiver ID) pcieport 0000:00:01.4: device [1022:14ab] error status/mask=00000020/04004000 pcieport 0000:00:01.4: [ 5] SDES (First) nvme nvme2: frozen state error detected, reset controller pcieport 0000:00:01.4: DPC: Data Link Layer Link Active not set in 1000 msec pcieport 0000:00:01.4: AER: subordinate device reset failed pcieport 0000:00:01.4: AER: device recovery failed pcieport 0000:00:01.4: pciehp: Slot(16): Link Down nvme2n1: detected capacity change from 1953525168 to 0 pci 0000:04:00.0: Removing from iommu group 49 Dmesg after: pcieport 0000:00:01.4: pciehp: Slot(16): Link Down nvme1n1: detected capacity change from 1953525168 to 0 pci 0000:04:00.0: Removing from iommu group 37 [1] PCI Express Base Specification Revision 6.0, Dec 16 2021. https://members.pcisig.com/wg/PCI-SIG/document/16609 Fixes: aea47413e7ce ("PCI/DPC: Expose dpc_process_error(), dpc_reset_link() for use by EDR") Link: https://lore.kernel.org/r/20240207181854.121335-1-Smita.KoralahalliChannabasappa@amd.com Signed-off-by: Smita Koralahalli Signed-off-by: Bjorn Helgaas Signed-off-by: lujunhua Reviewed-by: Lukas Wunner Reviewed-by: Kuppuswamy Sathyanarayanan Reviewed-by: Ilpo Järvinen Signed-off-by: xia-bing1 <2328702590@qq.com> Signed-off-by: lujunhua Signed-off-by: huwentao --- drivers/pci/pcie/dpc.c | 60 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/drivers/pci/pcie/dpc.c b/drivers/pci/pcie/dpc.c index f21d64ae4ffcc..6aaf5e2da4897 100644 --- a/drivers/pci/pcie/dpc.c +++ b/drivers/pci/pcie/dpc.c @@ -293,10 +293,70 @@ void dpc_process_error(struct pci_dev *pdev) } } +static void pci_clear_surpdn_errors(struct pci_dev *pdev) +{ + if (pdev->dpc_rp_extensions) + pci_write_config_dword(pdev, pdev->dpc_cap + + PCI_EXP_DPC_RP_PIO_STATUS, ~0); + + /* + * In practice, Surprise Down errors have been observed to also set + * error bits in the Status Register as well as the Fatal Error + * Detected bit in the Device Status Register. + */ + pci_write_config_word(pdev, PCI_STATUS, 0xffff); + + pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, PCI_EXP_DEVSTA_FED); +} + +static void dpc_handle_surprise_removal(struct pci_dev *pdev) +{ + if (!pcie_wait_for_link(pdev, false)) { + pci_info(pdev, "Data Link Layer Link Active not cleared in 1000 msec\n"); + goto out; + } + + if (pdev->dpc_rp_extensions && dpc_wait_rp_inactive(pdev)) + goto out; + + pci_aer_raw_clear_status(pdev); + pci_clear_surpdn_errors(pdev); + + pci_write_config_word(pdev, pdev->dpc_cap + PCI_EXP_DPC_STATUS, + PCI_EXP_DPC_STATUS_TRIGGER); + +out: + clear_bit(PCI_DPC_RECOVERED, &pdev->priv_flags); + wake_up_all(&dpc_completed_waitqueue); +} + +static bool dpc_is_surprise_removal(struct pci_dev *pdev) +{ + u16 status; + + if (!pdev->is_hotplug_bridge) + return false; + + if (pci_read_config_word(pdev, pdev->aer_cap + PCI_ERR_UNCOR_STATUS, + &status)) + return false; + + return status & PCI_ERR_UNC_SURPDN; +} + static irqreturn_t dpc_handler(int irq, void *context) { struct pci_dev *pdev = context; + /* + * According to PCIe r6.0 sec 6.7.6, errors are an expected side effect + * of async removal and should be ignored by software. + */ + if (dpc_is_surprise_removal(pdev)) { + dpc_handle_surprise_removal(pdev); + return IRQ_HANDLED; + } + dpc_process_error(pdev); /* We configure DPC so it only triggers on ERR_FATAL */ From dc06d96598a1893a2d9ec7d967fa8ed3a445fa84 Mon Sep 17 00:00:00 2001 From: Kuan-Wei Chiu Date: Thu, 7 Dec 2023 19:56:23 +0800 Subject: [PATCH 028/227] perf hisi-ptt: Fix memory leak in lseek failure handling mainline inclusion from mainline-v6.7-rc1 commit be7a4caa7c45bd4b0a39cdb260905b52a87c8688 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8MEWF CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=be7a4caa7c45bd4b0a39cdb260905b52a87c8688 ------------------------------------------- In the previous code, there was a memory leak issue where the previously allocated memory was not freed upon a failed lseek operation. This patch addresses the problem by releasing the old memory before returning -errno in case of a lseek failure. This ensures that memory is properly managed and avoids potential memory leaks. Signed-off-by: Kuan-Wei Chiu Acked-by: Namhyung Kim Cc: yangyicong@hisilicon.com Cc: jonathan.cameron@huawei.com Link: https://lore.kernel.org/r/20230930072719.1267784-1-visitorckw@gmail.com Signed-off-by: Namhyung Kim Signed-off-by: Junhao He Signed-off-by: huwentao --- tools/perf/util/hisi-ptt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/hisi-ptt.c b/tools/perf/util/hisi-ptt.c index 764d660d30e2f..52d0ce302ca04 100644 --- a/tools/perf/util/hisi-ptt.c +++ b/tools/perf/util/hisi-ptt.c @@ -108,8 +108,10 @@ static int hisi_ptt_process_auxtrace_event(struct perf_session *session, data_offset = 0; } else { data_offset = lseek(fd, 0, SEEK_CUR); - if (data_offset == -1) + if (data_offset == -1) { + free(data); return -errno; + } } err = readn(fd, data, size); From 4ac70e3703bf166150533807bb8517c5e2eee4e6 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 8 May 2025 15:42:21 +0800 Subject: [PATCH 029/227] Add gpio-hisi softdepend driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC6B79 CVE: NA -------------------------------- Add gpio-hisi for module soft depend to ensure gpio driver is loaded fist and I2C driver can obtain bus recovery gpio pin resources. Fixes: bdc031294c4d ("i2c: hisi: Add gpio bus recovery support") Signed-off-by: Devyn Liu Signed-off-by: lujunhua --- drivers/i2c/busses/i2c-hisi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index e13e4b3f59f91..2d5892066a704 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -613,3 +613,4 @@ module_platform_driver(hisi_i2c_driver); MODULE_AUTHOR("Yicong Yang "); MODULE_DESCRIPTION("HiSilicon I2C Controller Driver"); MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: gpio-hisi"); From 990c6c9efb4606b66db3c1672993a0bc6bf61986 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 28 Mar 2024 18:55:52 +0800 Subject: [PATCH 030/227] i2c: hisi: Add I2C controller reset and initialization proccess in bus recovery action driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9KYFI CVE: NA ----------------------------------------------- The I2C controller hardware manual specifies the bus recovery operation for I2C. It is recommended to reset the I2C controller and reinitialize the I2C bus configuration in bus recovery operation, which can more effectively ensure the recovery of the I2C bus. So we add I2C controller reset and initialization proccess in bus recovery action. Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/i2c/busses/i2c-hisi.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index 2d5892066a704..24672f425e13c 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -461,6 +461,26 @@ static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr) #ifdef CONFIG_ACPI #define HISI_I2C_PIN_MUX_METHOD "PMUX" +#define HISI_I2C_SOFT_RESET_METHOD "SRST" + +/** + * i2c_hisi_soft_reset - Do I2C master soft reset method through ACPI + * @dev: device need to be reset + * + * The function invokes the specific ACPI method "SRST" for trigger a soft + * reset of I2C controller in order to help on I2C controller recover from + * the abnormal state after bus recovery process. + */ +static void i2c_hisi_soft_reset(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long data; + + status = acpi_evaluate_integer(handle, HISI_I2C_SOFT_RESET_METHOD, NULL, &data); + dev_info(dev, "I2C controller reset %s", ACPI_FAILURE(status) ? "failed" : + "succeed"); +} /** * i2c_dw_acpi_pin_mux_change - Change the I2C controller's pin mux through ACPI @@ -498,6 +518,13 @@ static void i2c_hisi_unprepare_recovery(struct i2c_adapter *adap) struct hisi_i2c_controller *ctlr = i2c_get_adapdata(adap); i2c_hisi_pin_mux_change(ctlr->dev, false); + i2c_hisi_soft_reset(ctlr->dev); + + /* + * After a soft reset, the device configuration return to default + * values and require reinitialization. + */ + hisi_i2c_configure_bus(ctlr); } static void hisi_i2c_init_recovery_info(struct hisi_i2c_controller *ctlr) @@ -509,7 +536,8 @@ static void hisi_i2c_init_recovery_info(struct hisi_i2c_controller *ctlr) if (acpi_disabled) return; - if (!adev || !acpi_has_method(adev->handle, HISI_I2C_PIN_MUX_METHOD)) + if (!adev || !acpi_has_method(adev->handle, HISI_I2C_PIN_MUX_METHOD) || + !acpi_has_method(adev->handle, HISI_I2C_SOFT_RESET_METHOD)) return; gpio = devm_gpiod_get_optional(ctlr->dev, "scl", GPIOD_OUT_HIGH); From 6284f652e27448e9ece8a14d9f197a7bba7dbda8 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Sun, 28 Apr 2024 18:35:06 +0800 Subject: [PATCH 031/227] i2c: hisi: Correct the description comment for PIN_MUX METHOD driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9KYFI CVE: NA ----------------------------------------------- Correct the description comment for PIN_MUX METHOD and rename it to hisi instead of dw. Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/i2c/busses/i2c-hisi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index 24672f425e13c..b5a659b8b0a12 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -483,7 +483,7 @@ static void i2c_hisi_soft_reset(struct device *dev) } /** - * i2c_dw_acpi_pin_mux_change - Change the I2C controller's pin mux through ACPI + * i2c_hisi_pin_mux_change - Change the I2C controller's pin mux through ACPI * @dev: device owns the SCL/SDA pin * @to_gpio: true to switch to GPIO, false to switch to SCL/SDA * From 0913305e3c30614093035f4a9a6fe6f66aa8387b Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 1 Feb 2024 12:36:45 +0100 Subject: [PATCH 032/227] tpm,tpm_tis: Avoid warning splat at shutdown mainline inclusion from mainline-v6.9-rc1 commit b7ab4bbd0188f3985b821fa09456b11105a8dedf category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9G5IU CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b7ab4bbd0188f3985b821fa09456b11105a8dedf ---------------------------------------------------------------------- If interrupts are not activated the work struct 'free_irq_work' is not initialized. This results in a warning splat at module shutdown. Fix this by always initializing the work regardless of whether interrupts are activated or not. cc: stable@vger.kernel.org Fixes: 481c2d14627d ("tpm,tpm_tis: Disable interrupts after 1000 unhandled IRQs") Reported-by: Jarkko Sakkinen Closes: https://lore.kernel.org/all/CX32RFOMJUQ0.3R4YCL9MDCB96@kernel.org/ Signed-off-by: Lino Sanfilippo Signed-off-by: Jarkko Sakkinen Signed-off-by: lujunhua --- drivers/char/tpm/tpm_tis_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index b3452259d6e0b..6dde52c4f1d67 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -779,7 +779,6 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int rc; u32 int_status; - rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL, tis_int_handler, IRQF_ONESHOT | flags, dev_name(&chip->dev), chip); @@ -989,6 +988,7 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, priv->phy_ops = phy_ops; priv->locality_count = 0; mutex_init(&priv->locality_count_mutex); + INIT_WORK(&priv->free_irq_work, tpm_tis_free_irq_func); dev_set_drvdata(&chip->dev, priv); From 2d8bc0eace8f5e76f7cd0138f0c05c6420431dc6 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Wed, 10 Apr 2024 10:02:03 +0800 Subject: [PATCH 033/227] spi: hisi-kunpeng: Delete the dump interface of data registers in debugfs maillist inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9KP0Z CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git/commit/?h=for-6.9&id=7430764f5a85d30314aeef2d5438dff1fb0b1d68 ------------------------------------------ Due to the reading of FIFO during the dump of data registers in debugfs, if SPI transmission is in progress, it will be affected and may result in transmission failure. Therefore, the dump interface of data registers in debugfs is removed. Fixes: 2b2142f247eb ("spi: hisi-kunpeng: Add debugfs support") Signed-off-by: Devyn Liu Reviewed-by: Jay Fang Signed-off-by: Junhua Lu --- drivers/spi/spi-hisi-kunpeng.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 525cc0143a305..54730e93fba45 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -151,8 +151,6 @@ static const struct debugfs_reg32 hisi_spi_regs[] = { HISI_SPI_DBGFS_REG("ENR", HISI_SPI_ENR), HISI_SPI_DBGFS_REG("FIFOC", HISI_SPI_FIFOC), HISI_SPI_DBGFS_REG("IMR", HISI_SPI_IMR), - HISI_SPI_DBGFS_REG("DIN", HISI_SPI_DIN), - HISI_SPI_DBGFS_REG("DOUT", HISI_SPI_DOUT), HISI_SPI_DBGFS_REG("SR", HISI_SPI_SR), HISI_SPI_DBGFS_REG("RISR", HISI_SPI_RISR), HISI_SPI_DBGFS_REG("ISR", HISI_SPI_ISR), From b10c26c664c92f76840c74552ae10e93d4d59090 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 9 May 2024 19:15:13 +0800 Subject: [PATCH 034/227] gpiolib: acpi: Fix failed in acpi_gpiochip_find() by adding parent node match driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9KYEZ CVE: NA -------------------------------- Previous patch modified the standard used by acpi_gpiochip_find() to match device nodes. Using the device node set in gc->gpiodev->d- ev instead of gc->parent. However, there is a situation in gpio-dwapb where the GPIO device driver will set gc->fwnode for each port corresponding to a child node under a GPIO device, so gc->gpiodev->dev will be assigned the value of each child node in gpiochip_add_data(). When other drivers request GPIO pin resources through the GPIO device node provided by ACPI (corresponding to the parent node), the change of the matching object to gc->gpiodev->dev in acpi_gpiochip_find() only allows finding the value of each port (child node), resulting in a failed request. Reapply the condition of using gc->parent for match in acpi_gpio- chip_find() in the code can compatible with the problem of gpio-dwapb, and will not affect the two cases mentioned in the patch: 1. There is no setting for gc->fwnode. 2. The case that depends on using gc->fwnode for match. Fixes: 5062e4c14b75 ("gpiolib: acpi: use the fwnode in acpi_gpiochip_find()") Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/gpio/gpiolib-acpi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 27e3fb9938049..44e8438206da2 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -95,10 +95,8 @@ static bool acpi_gpio_deferred_req_irqs_done; static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) { - if (!gc->parent) - return false; - - return ACPI_HANDLE(gc->parent) == data; + return device_match_acpi_handle(&gc->gpiodev->dev, data) || + (gc->parent && device_match_acpi_handle(gc->parent, data)); } /** From b02c1bc485ef386d584bf303be66a41f6a89d18c Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 11 Apr 2024 20:54:59 +0800 Subject: [PATCH 035/227] spi: hisi-kunpeng: Add validation for the minimum value of speed_hz driver inclusion category: cleanup bugzilla: https://gitee.com/openeuler/kernel/issues/I9O7EH CVE: NA -------------------------------- We get the configuration of max_frequency from the firmware, which is used to calculate the clk_div together with the set speed. If the speed set by the user is too small, it may cause the clk_div to be too large to exceed the variable range. Therefore, we limit the minimum value of the speed configuration to the value obtained by (master->max_speed_hz/CLK_DIV_MAX). Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/spi/spi-hisi-kunpeng.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 54730e93fba45..5dbf71ee8281b 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -495,6 +495,7 @@ static int hisi_spi_probe(struct platform_device *pdev) master->transfer_one = hisi_spi_transfer_one; master->handle_err = hisi_spi_handle_err; master->dev.fwnode = dev->fwnode; + master->min_speed_hz = DIV_ROUND_UP(host->max_speed_hz, CLK_DIV_MAX); hisi_spi_hw_init(hs); From 8346483cf80b631755a5aa57961e696110ed4978 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 11 Apr 2024 21:27:44 +0800 Subject: [PATCH 036/227] spi: Add verification for the max_frequency provided by the firmware driver inclusion category: cleanup bugzilla: https://gitee.com/openeuler/kernel/issues/I9O7EH CVE: NA -------------------------------- Add verification for the max_frequency provided by the firmware. If the value is 0, the parameter is regarded as invalid and an error code is returned. Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/spi/spi-hisi-kunpeng.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 5dbf71ee8281b..6d862b10328ba 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -481,6 +481,12 @@ static int hisi_spi_probe(struct platform_device *pdev) return -EINVAL; } + if (host->max_speed_hz == 0) { + dev_err(dev, "invalid max SPI clocking speed, max-freq=%u\n", + host->max_speed_hz); + return -EINVAL; + } + ret = device_property_read_u16(dev, "num-cs", &master->num_chipselect); if (ret) From 81e1c8000ecb7090e7d29e472414d2a946de2b83 Mon Sep 17 00:00:00 2001 From: hbmm Date: Wed, 2 Jul 2025 15:55:13 +0800 Subject: [PATCH 037/227] Revert "spi: Add verification for the max_frequency provided by the firmware" This reverts commit 8c9aeaf8e93de629208374d05813546a9b3a2e44. --- drivers/spi/spi-hisi-kunpeng.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 6d862b10328ba..5dbf71ee8281b 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -481,12 +481,6 @@ static int hisi_spi_probe(struct platform_device *pdev) return -EINVAL; } - if (host->max_speed_hz == 0) { - dev_err(dev, "invalid max SPI clocking speed, max-freq=%u\n", - host->max_speed_hz); - return -EINVAL; - } - ret = device_property_read_u16(dev, "num-cs", &master->num_chipselect); if (ret) From 37095ece0a235fdc596e58334619eacc65c2a604 Mon Sep 17 00:00:00 2001 From: hbmm Date: Wed, 2 Jul 2025 16:06:12 +0800 Subject: [PATCH 038/227] spi: Add verification for the max_frequency provided by the firmware driver inclusion category: cleanup bugzilla: https://gitee.com/openeuler/kernel/issues/I9O7EH CVE: NA -------------------------------- Add verification for the max_frequency provided by the firmware. If the value is 0, the parameter is regarded as invalid and an error code is returned. Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/spi/spi-hisi-kunpeng.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 5dbf71ee8281b..87b162db475e3 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -481,6 +481,12 @@ static int hisi_spi_probe(struct platform_device *pdev) return -EINVAL; } + if (master->max_speed_hz == 0) { + dev_err(dev, "invalid max SPI clocking speed, max-freq=%u\n", + master->max_speed_hz); + return -EINVAL; + } + ret = device_property_read_u16(dev, "num-cs", &master->num_chipselect); if (ret) @@ -495,7 +501,7 @@ static int hisi_spi_probe(struct platform_device *pdev) master->transfer_one = hisi_spi_transfer_one; master->handle_err = hisi_spi_handle_err; master->dev.fwnode = dev->fwnode; - master->min_speed_hz = DIV_ROUND_UP(host->max_speed_hz, CLK_DIV_MAX); + master->min_speed_hz = DIV_ROUND_UP(master->max_speed_hz, CLK_DIV_MAX); hisi_spi_hw_init(hs); From e9897b65a72dd007cb522d788df1550018e8e88d Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 19 Apr 2023 10:33:42 +0200 Subject: [PATCH 039/227] ACPI: thermal: Move to dedicated function sysfs extra attr creation The ACPI thermal driver creates extra sysfs attributes in its own directory pointing to the thermal zone it is related to and add a pointer to the sysfs ACPI thermal device from the thermal zone sysfs entry. This is very specific to this ACPI thermal driver, let's encapsulate the related creation/deletion code to group it inside a function we can identify later for removal if needed. Signed-off-by: Daniel Lezcano [ rjw: Subject adjustment, removal of trailing white space ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal.c | 45 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 809e12b941235..7e24554fe00f3 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -783,6 +783,32 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = { .critical = acpi_thermal_zone_device_critical, }; +static int acpi_thermal_zone_sysfs_add(struct acpi_thermal *tz) +{ + struct device *tzdev = thermal_zone_device(tz->thermal_zone); + int ret; + + ret = sysfs_create_link(&tz->device->dev.kobj, + &tzdev->kobj, "thermal_zone"); + if (ret) + return ret; + + ret = sysfs_create_link(&tzdev->kobj, + &tz->device->dev.kobj, "device"); + if (ret) + sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); + + return ret; +} + +static void acpi_thermal_zone_sysfs_remove(struct acpi_thermal *tz) +{ + struct device *tzdev = thermal_zone_device(tz->thermal_zone); + + sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); + sysfs_remove_link(&tzdev->kobj, "device"); +} + static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) { int trips = 0; @@ -816,21 +842,15 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (IS_ERR(tz->thermal_zone)) return -ENODEV; - result = sysfs_create_link(&tz->device->dev.kobj, - &tz->thermal_zone->device.kobj, "thermal_zone"); + result = acpi_thermal_zone_sysfs_add(tz); if (result) goto unregister_tzd; - result = sysfs_create_link(&tz->thermal_zone->device.kobj, - &tz->device->dev.kobj, "device"); - if (result) - goto remove_tz_link; - status = acpi_bus_attach_private_data(tz->device->handle, tz->thermal_zone); if (ACPI_FAILURE(status)) { result = -ENODEV; - goto remove_dev_link; + goto remove_links; } result = thermal_zone_device_enable(tz->thermal_zone); @@ -844,10 +864,15 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) acpi_bus_detach: acpi_bus_detach_private_data(tz->device->handle); +<<<<<<< HEAD remove_dev_link: sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); remove_tz_link: sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); +======= +remove_links: + acpi_thermal_zone_sysfs_remove(tz); +>>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) unregister_tzd: thermal_zone_device_unregister(tz->thermal_zone); @@ -856,8 +881,12 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) { +<<<<<<< HEAD sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); +======= + acpi_thermal_zone_sysfs_remove(tz); +>>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) thermal_zone_device_unregister(tz->thermal_zone); tz->thermal_zone = NULL; acpi_bus_detach_private_data(tz->device->handle); From d2e87d928187e7b1e450e323ea1decbddb6d58fc Mon Sep 17 00:00:00 2001 From: Junhao He Date: Thu, 15 Jun 2023 20:59:24 +0800 Subject: [PATCH 040/227] drivers/perf: hisi: Add support for HiSilicon H60PA and PAv3 PMU driver Compared to the original PA device, H60PA offers higher bandwidth. The H60PA is a new device and we use HID to differentiate them. The events supported by PAv3 and PAv2 are different. The PAv3 PMU removed some events which are supported by PAv2 PMU. The older PA PMU driver will probe v3 as v2. Therefore PA events displayed by "perf list" cannot work properly. We add the HISI0275 HID for PAv3 PMU to distinguish different. For each H60PA PMU, except for the overflow interrupt register, other functions of the H60PA PMU are the same as the original PA PMU module. It has 8-programable counters and each counter is free-running. Interrupt is supported to handle counter (64-bits) overflow. Signed-off-by: Junhao He Reviewed-by: Jonathan Cameron Reviewed-by: Yicong Yang Acked-by: Mark Rutland Link: https://lore.kernel.org/r/20230615125926.29832-2-hejunhao3@huawei.com Signed-off-by: Will Deacon Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 127 +++++++++++++++++--- drivers/perf/hisilicon/hisi_uncore_pmu.h | 8 ++ 2 files changed, 120 insertions(+), 15 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c index e88a43b3bffb8..797cf201996a9 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -22,9 +22,15 @@ #define PA_TT_CTRL 0x1c08 #define PA_TGTID_CTRL 0x1c14 #define PA_SRCID_CTRL 0x1c18 + +/* H32 PA interrupt registers */ #define PA_INT_MASK 0x1c70 #define PA_INT_STATUS 0x1c78 #define PA_INT_CLEAR 0x1c7c + +#define H60PA_INT_STATUS 0x1c70 +#define H60PA_INT_MASK 0x1c74 + #define PA_EVENT_TYPE0 0x1c80 #define PA_PMU_VERSION 0x1cf0 #define PA_EVENT_CNT0_L 0x1d00 @@ -46,6 +52,12 @@ HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22); HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33); HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44); +struct hisi_pa_pmu_int_regs { + u32 mask_offset; + u32 clear_offset; + u32 status_offset; +}; + static void hisi_pa_pmu_enable_tracetag(struct perf_event *event) { struct hisi_pmu *pa_pmu = to_hisi_pmu(event->pmu); @@ -219,40 +231,40 @@ static void hisi_pa_pmu_disable_counter(struct hisi_pmu *pa_pmu, static void hisi_pa_pmu_enable_counter_int(struct hisi_pmu *pa_pmu, struct hw_perf_event *hwc) { + struct hisi_pa_pmu_int_regs *regs = pa_pmu->dev_info->private; u32 val; /* Write 0 to enable interrupt */ - val = readl(pa_pmu->base + PA_INT_MASK); + val = readl(pa_pmu->base + regs->mask_offset); val &= ~(1 << hwc->idx); - writel(val, pa_pmu->base + PA_INT_MASK); + writel(val, pa_pmu->base + regs->mask_offset); } static void hisi_pa_pmu_disable_counter_int(struct hisi_pmu *pa_pmu, struct hw_perf_event *hwc) { + struct hisi_pa_pmu_int_regs *regs = pa_pmu->dev_info->private; u32 val; /* Write 1 to mask interrupt */ - val = readl(pa_pmu->base + PA_INT_MASK); + val = readl(pa_pmu->base + regs->mask_offset); val |= 1 << hwc->idx; - writel(val, pa_pmu->base + PA_INT_MASK); + writel(val, pa_pmu->base + regs->mask_offset); } static u32 hisi_pa_pmu_get_int_status(struct hisi_pmu *pa_pmu) { - return readl(pa_pmu->base + PA_INT_STATUS); + struct hisi_pa_pmu_int_regs *regs = pa_pmu->dev_info->private; + + return readl(pa_pmu->base + regs->status_offset); } static void hisi_pa_pmu_clear_int_status(struct hisi_pmu *pa_pmu, int idx) { - writel(1 << idx, pa_pmu->base + PA_INT_CLEAR); -} + struct hisi_pa_pmu_int_regs *regs = pa_pmu->dev_info->private; -static const struct acpi_device_id hisi_pa_pmu_acpi_match[] = { - { "HISI0273", }, - {} -}; -MODULE_DEVICE_TABLE(acpi, hisi_pa_pmu_acpi_match); + writel(1 << idx, pa_pmu->base + regs->clear_offset); +} static int hisi_pa_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *pa_pmu) @@ -276,6 +288,10 @@ static int hisi_pa_pmu_init_data(struct platform_device *pdev, pa_pmu->ccl_id = -1; pa_pmu->sccl_id = -1; + pa_pmu->dev_info = device_get_match_data(&pdev->dev); + if (!pa_pmu->dev_info) + return -ENODEV; + pa_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pa_pmu->base)) { dev_err(&pdev->dev, "ioremap failed for pa_pmu resource.\n"); @@ -314,6 +330,32 @@ static const struct attribute_group hisi_pa_pmu_v2_events_group = { .attrs = hisi_pa_pmu_v2_events_attr, }; +static struct attribute *hisi_pa_pmu_v3_events_attr[] = { + HISI_PMU_EVENT_ATTR(tx_req, 0x0), + HISI_PMU_EVENT_ATTR(tx_dat, 0x1), + HISI_PMU_EVENT_ATTR(tx_snp, 0x2), + HISI_PMU_EVENT_ATTR(rx_req, 0x7), + HISI_PMU_EVENT_ATTR(rx_dat, 0x8), + HISI_PMU_EVENT_ATTR(rx_snp, 0x9), + NULL +}; + +static const struct attribute_group hisi_pa_pmu_v3_events_group = { + .name = "events", + .attrs = hisi_pa_pmu_v3_events_attr, +}; + +static struct attribute *hisi_h60pa_pmu_events_attr[] = { + HISI_PMU_EVENT_ATTR(rx_flit, 0x50), + HISI_PMU_EVENT_ATTR(tx_flit, 0x65), + NULL +}; + +static const struct attribute_group hisi_h60pa_pmu_events_group = { + .name = "events", + .attrs = hisi_h60pa_pmu_events_attr, +}; + static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); static struct attribute *hisi_pa_pmu_cpumask_attrs[] = { @@ -337,6 +379,12 @@ static const struct attribute_group hisi_pa_pmu_identifier_group = { .attrs = hisi_pa_pmu_identifier_attrs, }; +static struct hisi_pa_pmu_int_regs hisi_pa_pmu_regs = { + .mask_offset = PA_INT_MASK, + .clear_offset = PA_INT_CLEAR, + .status_offset = PA_INT_STATUS, +}; + static const struct attribute_group *hisi_pa_pmu_v2_attr_groups[] = { &hisi_pa_pmu_v2_format_group, &hisi_pa_pmu_v2_events_group, @@ -345,6 +393,46 @@ static const struct attribute_group *hisi_pa_pmu_v2_attr_groups[] = { NULL }; +static const struct hisi_pmu_dev_info hisi_h32pa_v2 = { + .name = "pa", + .attr_groups = hisi_pa_pmu_v2_attr_groups, + .private = &hisi_pa_pmu_regs, +}; + +static const struct attribute_group *hisi_pa_pmu_v3_attr_groups[] = { + &hisi_pa_pmu_v2_format_group, + &hisi_pa_pmu_v3_events_group, + &hisi_pa_pmu_cpumask_attr_group, + &hisi_pa_pmu_identifier_group, + NULL +}; + +static const struct hisi_pmu_dev_info hisi_h32pa_v3 = { + .name = "pa", + .attr_groups = hisi_pa_pmu_v3_attr_groups, + .private = &hisi_pa_pmu_regs, +}; + +static struct hisi_pa_pmu_int_regs hisi_h60pa_pmu_regs = { + .mask_offset = H60PA_INT_MASK, + .clear_offset = H60PA_INT_STATUS, /* Clear on write */ + .status_offset = H60PA_INT_STATUS, +}; + +static const struct attribute_group *hisi_h60pa_pmu_attr_groups[] = { + &hisi_pa_pmu_v2_format_group, + &hisi_h60pa_pmu_events_group, + &hisi_pa_pmu_cpumask_attr_group, + &hisi_pa_pmu_identifier_group, + NULL +}; + +static const struct hisi_pmu_dev_info hisi_h60pa = { + .name = "h60pa", + .attr_groups = hisi_h60pa_pmu_attr_groups, + .private = &hisi_h60pa_pmu_regs, +}; + static const struct hisi_uncore_ops hisi_uncore_pa_ops = { .write_evtype = hisi_pa_pmu_write_evtype, .get_event_idx = hisi_uncore_pmu_get_event_idx, @@ -375,7 +463,7 @@ static int hisi_pa_pmu_dev_probe(struct platform_device *pdev, if (ret) return ret; - pa_pmu->pmu_events.attr_groups = hisi_pa_pmu_v2_attr_groups; + pa_pmu->pmu_events.attr_groups = pa_pmu->dev_info->attr_groups; pa_pmu->num_counters = PA_NR_COUNTERS; pa_pmu->ops = &hisi_uncore_pa_ops; pa_pmu->check_event = 0xB0; @@ -400,8 +488,9 @@ static int hisi_pa_pmu_probe(struct platform_device *pdev) if (ret) return ret; - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%u_pa%u", - pa_pmu->sicl_id, pa_pmu->index_id); + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%d_%s%u", + pa_pmu->sicl_id, pa_pmu->dev_info->name, + pa_pmu->index_id); if (!name) return -ENOMEM; @@ -435,6 +524,14 @@ static int hisi_pa_pmu_remove(struct platform_device *pdev) return 0; } +static const struct acpi_device_id hisi_pa_pmu_acpi_match[] = { + { "HISI0273", (kernel_ulong_t)&hisi_h32pa_v2 }, + { "HISI0275", (kernel_ulong_t)&hisi_h32pa_v3 }, + { "HISI0274", (kernel_ulong_t)&hisi_h60pa }, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_pa_pmu_acpi_match); + static struct platform_driver hisi_pa_pmu_driver = { .driver = { .name = "hisi_pa_pmu", diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 07890a8e96ca7..772857b99dc5e 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -62,6 +62,13 @@ struct hisi_uncore_ops { void (*disable_filter)(struct perf_event *event); }; +/* Describes the HISI PMU chip features information */ +struct hisi_pmu_dev_info { + const char *name; + const struct attribute_group **attr_groups; + void *private; +}; + struct hisi_pmu_hwevents { struct perf_event *hw_events[HISI_MAX_COUNTERS]; DECLARE_BITMAP(used_mask, HISI_MAX_COUNTERS); @@ -72,6 +79,7 @@ struct hisi_pmu_hwevents { struct hisi_pmu { struct pmu pmu; const struct hisi_uncore_ops *ops; + const struct hisi_pmu_dev_info *dev_info; struct hisi_pmu_hwevents pmu_events; /* associated_cpus: All CPUs associated with the PMU */ cpumask_t associated_cpus; From e50af4547e519e5272bb12ef669fd4f3f667ff94 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Thu, 15 Jun 2023 20:59:25 +0800 Subject: [PATCH 041/227] drivers/perf: hisi: Add support for HiSilicon UC PMU driver On HiSilicon Hip09 platform, there are 4 UC (unified cache) modules on each chip CCL (CPU Cluster). UC is a cache that provides coherence between NUMA and UMA domains. It is located between L2 and Memory System. Many PMU events are supported. Let's support the UC PMU driver using the HiSilicon uncore PMU framework. * rd_req_en : rd_req_en is the abbreviation of read request tracetag enable and allows user to count only read operations. Details are listed in the hisi-pmu document at Documentation/admin-guide/perf/hisi-pmu.rst * srcid_en & srcid: Allows users to filter statistical information based on specific CPU/ICL by srcid. srcid_en depends on rd_req_en being enabled. * uring_channel: Allows users to filter statistical information based on the specified tx request uring channel. uring_channel only supported events: [0x47 ~ 0x59]. Signed-off-by: Junhao He Reviewed-by: Yicong Yang Reviewed-by: Jonathan Cameron Acked-by: Mark Rutland Link: https://lore.kernel.org/r/20230615125926.29832-3-hejunhao3@huawei.com Signed-off-by: Will Deacon Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/Makefile | 2 +- drivers/perf/hisilicon/hisi_uncore_pmu.c | 4 +- drivers/perf/hisilicon/hisi_uncore_pmu.h | 6 + drivers/perf/hisilicon/hisi_uncore_uc_pmu.c | 578 ++++++++++++++++++++ 4 files changed, 588 insertions(+), 2 deletions(-) create mode 100644 drivers/perf/hisilicon/hisi_uncore_uc_pmu.c diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index 4d2c9abe3372f..48dcc8381ea75 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o \ hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o hisi_uncore_sllc_pmu.o \ - hisi_uncore_pa_pmu.o hisi_uncore_cpa_pmu.o + hisi_uncore_pa_pmu.o hisi_uncore_cpa_pmu.o hisi_uncore_uc_pmu.o obj-$(CONFIG_HISI_PCIE_PMU) += hisi_pcie_pmu.o obj-$(CONFIG_HNS3_PMU) += hns3_pmu.o diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index 5c7234b7a8361..b356d9b363ec1 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -20,7 +20,6 @@ #include "hisi_uncore_pmu.h" -#define HISI_GET_EVENTID(ev) (ev->hw.config_base & 0xff) #define HISI_MAX_PERIOD(nr) (GENMASK_ULL((nr) - 1, 0)) /* @@ -226,6 +225,9 @@ int hisi_uncore_pmu_event_init(struct perf_event *event) hwc->idx = -1; hwc->config_base = event->attr.config; + if (hisi_pmu->ops->check_filter && hisi_pmu->ops->check_filter(event)) + return -EINVAL; + /* Enforce to use the same CPU for all events in this PMU */ event->cpu = hisi_pmu->on_cpu; diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 772857b99dc5e..92402aa69d70f 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -43,9 +43,15 @@ return FIELD_GET(GENMASK_ULL(hi, lo), event->attr.config); \ } +#define HISI_GET_EVENTID(ev) (ev->hw.config_base & 0xff) + +#define HISI_PMU_EVTYPE_BITS 8 +#define HISI_PMU_EVTYPE_SHIFT(idx) ((idx) % 4 * HISI_PMU_EVTYPE_BITS) + struct hisi_pmu; struct hisi_uncore_ops { + int (*check_filter)(struct perf_event *event); void (*write_evtype)(struct hisi_pmu *, int, u32); int (*get_event_idx)(struct perf_event *); u64 (*read_counter)(struct hisi_pmu *, struct hw_perf_event *); diff --git a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c new file mode 100644 index 0000000000000..63da05e5831c1 --- /dev/null +++ b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c @@ -0,0 +1,578 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HiSilicon SoC UC (unified cache) uncore Hardware event counters support + * + * Copyright (C) 2023 HiSilicon Limited + * + * This code is based on the uncore PMUs like hisi_uncore_l3c_pmu. + */ +#include +#include +#include +#include +#include +#include + +#include "hisi_uncore_pmu.h" + +/* Dynamic CPU hotplug state used by UC PMU */ +static enum cpuhp_state hisi_uc_pmu_online; + +/* UC register definition */ +#define HISI_UC_INT_MASK_REG 0x0800 +#define HISI_UC_INT_STS_REG 0x0808 +#define HISI_UC_INT_CLEAR_REG 0x080c +#define HISI_UC_TRACETAG_CTRL_REG 0x1b2c +#define HISI_UC_TRACETAG_REQ_MSK GENMASK(9, 7) +#define HISI_UC_TRACETAG_MARK_EN BIT(0) +#define HISI_UC_TRACETAG_REQ_EN (HISI_UC_TRACETAG_MARK_EN | BIT(2)) +#define HISI_UC_TRACETAG_SRCID_EN BIT(3) +#define HISI_UC_SRCID_CTRL_REG 0x1b40 +#define HISI_UC_SRCID_MSK GENMASK(14, 1) +#define HISI_UC_EVENT_CTRL_REG 0x1c00 +#define HISI_UC_EVENT_TRACETAG_EN BIT(29) +#define HISI_UC_EVENT_URING_MSK GENMASK(28, 27) +#define HISI_UC_EVENT_GLB_EN BIT(26) +#define HISI_UC_VERSION_REG 0x1cf0 +#define HISI_UC_EVTYPE_REGn(n) (0x1d00 + (n) * 4) +#define HISI_UC_EVTYPE_MASK GENMASK(7, 0) +#define HISI_UC_CNTR_REGn(n) (0x1e00 + (n) * 8) + +#define HISI_UC_NR_COUNTERS 0x8 +#define HISI_UC_V2_NR_EVENTS 0xFF +#define HISI_UC_CNTR_REG_BITS 64 + +#define HISI_UC_RD_REQ_TRACETAG 0x4 +#define HISI_UC_URING_EVENT_MIN 0x47 +#define HISI_UC_URING_EVENT_MAX 0x59 + +HISI_PMU_EVENT_ATTR_EXTRACTOR(rd_req_en, config1, 0, 0); +HISI_PMU_EVENT_ATTR_EXTRACTOR(uring_channel, config1, 5, 4); +HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid, config1, 19, 6); +HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_en, config1, 20, 20); + +static int hisi_uc_pmu_check_filter(struct perf_event *event) +{ + struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); + + if (hisi_get_srcid_en(event) && !hisi_get_rd_req_en(event)) { + dev_err(uc_pmu->dev, + "rcid_en depends on rd_req_en being enabled!\n"); + return -EINVAL; + } + + if (!hisi_get_uring_channel(event)) + return 0; + + if ((HISI_GET_EVENTID(event) < HISI_UC_URING_EVENT_MIN) || + (HISI_GET_EVENTID(event) > HISI_UC_URING_EVENT_MAX)) + dev_warn(uc_pmu->dev, + "Only events: [%#x ~ %#x] support channel filtering!", + HISI_UC_URING_EVENT_MIN, HISI_UC_URING_EVENT_MAX); + + return 0; +} + +static void hisi_uc_pmu_config_req_tracetag(struct perf_event *event) +{ + struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); + u32 val; + + if (!hisi_get_rd_req_en(event)) + return; + + val = readl(uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); + + /* The request-type has been configured */ + if (FIELD_GET(HISI_UC_TRACETAG_REQ_MSK, val) == HISI_UC_RD_REQ_TRACETAG) + return; + + /* Set request-type for tracetag, only read request is supported! */ + val &= ~HISI_UC_TRACETAG_REQ_MSK; + val |= FIELD_PREP(HISI_UC_TRACETAG_REQ_MSK, HISI_UC_RD_REQ_TRACETAG); + val |= HISI_UC_TRACETAG_REQ_EN; + writel(val, uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); +} + +static void hisi_uc_pmu_clear_req_tracetag(struct perf_event *event) +{ + struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); + u32 val; + + if (!hisi_get_rd_req_en(event)) + return; + + val = readl(uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); + + /* Do nothing, the request-type tracetag has been cleaned up */ + if (FIELD_GET(HISI_UC_TRACETAG_REQ_MSK, val) == 0) + return; + + /* Clear request-type */ + val &= ~HISI_UC_TRACETAG_REQ_MSK; + val &= ~HISI_UC_TRACETAG_REQ_EN; + writel(val, uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); +} + +static void hisi_uc_pmu_config_srcid_tracetag(struct perf_event *event) +{ + struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); + u32 val; + + if (!hisi_get_srcid_en(event)) + return; + + val = readl(uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); + + /* Do nothing, the source id has been configured */ + if (FIELD_GET(HISI_UC_TRACETAG_SRCID_EN, val)) + return; + + /* Enable source id tracetag */ + val |= HISI_UC_TRACETAG_SRCID_EN; + writel(val, uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); + + val = readl(uc_pmu->base + HISI_UC_SRCID_CTRL_REG); + val &= ~HISI_UC_SRCID_MSK; + val |= FIELD_PREP(HISI_UC_SRCID_MSK, hisi_get_srcid(event)); + writel(val, uc_pmu->base + HISI_UC_SRCID_CTRL_REG); + + /* Depend on request-type tracetag enabled */ + hisi_uc_pmu_config_req_tracetag(event); +} + +static void hisi_uc_pmu_clear_srcid_tracetag(struct perf_event *event) +{ + struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); + u32 val; + + if (!hisi_get_srcid_en(event)) + return; + + val = readl(uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); + + /* Do nothing, the source id has been cleaned up */ + if (FIELD_GET(HISI_UC_TRACETAG_SRCID_EN, val) == 0) + return; + + hisi_uc_pmu_clear_req_tracetag(event); + + /* Disable source id tracetag */ + val &= ~HISI_UC_TRACETAG_SRCID_EN; + writel(val, uc_pmu->base + HISI_UC_TRACETAG_CTRL_REG); + + val = readl(uc_pmu->base + HISI_UC_SRCID_CTRL_REG); + val &= ~HISI_UC_SRCID_MSK; + writel(val, uc_pmu->base + HISI_UC_SRCID_CTRL_REG); +} + +static void hisi_uc_pmu_config_uring_channel(struct perf_event *event) +{ + struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); + u32 uring_channel = hisi_get_uring_channel(event); + u32 val; + + /* Do nothing if not being set or is set explicitly to zero (default) */ + if (uring_channel == 0) + return; + + val = readl(uc_pmu->base + HISI_UC_EVENT_CTRL_REG); + + /* Do nothing, the uring_channel has been configured */ + if (uring_channel == FIELD_GET(HISI_UC_EVENT_URING_MSK, val)) + return; + + val &= ~HISI_UC_EVENT_URING_MSK; + val |= FIELD_PREP(HISI_UC_EVENT_URING_MSK, uring_channel); + writel(val, uc_pmu->base + HISI_UC_EVENT_CTRL_REG); +} + +static void hisi_uc_pmu_clear_uring_channel(struct perf_event *event) +{ + struct hisi_pmu *uc_pmu = to_hisi_pmu(event->pmu); + u32 val; + + /* Do nothing if not being set or is set explicitly to zero (default) */ + if (hisi_get_uring_channel(event) == 0) + return; + + val = readl(uc_pmu->base + HISI_UC_EVENT_CTRL_REG); + + /* Do nothing, the uring_channel has been cleaned up */ + if (FIELD_GET(HISI_UC_EVENT_URING_MSK, val) == 0) + return; + + val &= ~HISI_UC_EVENT_URING_MSK; + writel(val, uc_pmu->base + HISI_UC_EVENT_CTRL_REG); +} + +static void hisi_uc_pmu_enable_filter(struct perf_event *event) +{ + if (event->attr.config1 == 0) + return; + + hisi_uc_pmu_config_uring_channel(event); + hisi_uc_pmu_config_req_tracetag(event); + hisi_uc_pmu_config_srcid_tracetag(event); +} + +static void hisi_uc_pmu_disable_filter(struct perf_event *event) +{ + if (event->attr.config1 == 0) + return; + + hisi_uc_pmu_clear_srcid_tracetag(event); + hisi_uc_pmu_clear_req_tracetag(event); + hisi_uc_pmu_clear_uring_channel(event); +} + +static void hisi_uc_pmu_write_evtype(struct hisi_pmu *uc_pmu, int idx, u32 type) +{ + u32 val; + + /* + * Select the appropriate event select register. + * There are 2 32-bit event select registers for the + * 8 hardware counters, each event code is 8-bit wide. + */ + val = readl(uc_pmu->base + HISI_UC_EVTYPE_REGn(idx / 4)); + val &= ~(HISI_UC_EVTYPE_MASK << HISI_PMU_EVTYPE_SHIFT(idx)); + val |= (type << HISI_PMU_EVTYPE_SHIFT(idx)); + writel(val, uc_pmu->base + HISI_UC_EVTYPE_REGn(idx / 4)); +} + +static void hisi_uc_pmu_start_counters(struct hisi_pmu *uc_pmu) +{ + u32 val; + + val = readl(uc_pmu->base + HISI_UC_EVENT_CTRL_REG); + val |= HISI_UC_EVENT_GLB_EN; + writel(val, uc_pmu->base + HISI_UC_EVENT_CTRL_REG); +} + +static void hisi_uc_pmu_stop_counters(struct hisi_pmu *uc_pmu) +{ + u32 val; + + val = readl(uc_pmu->base + HISI_UC_EVENT_CTRL_REG); + val &= ~HISI_UC_EVENT_GLB_EN; + writel(val, uc_pmu->base + HISI_UC_EVENT_CTRL_REG); +} + +static void hisi_uc_pmu_enable_counter(struct hisi_pmu *uc_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Enable counter index */ + val = readl(uc_pmu->base + HISI_UC_EVENT_CTRL_REG); + val |= (1 << hwc->idx); + writel(val, uc_pmu->base + HISI_UC_EVENT_CTRL_REG); +} + +static void hisi_uc_pmu_disable_counter(struct hisi_pmu *uc_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + /* Clear counter index */ + val = readl(uc_pmu->base + HISI_UC_EVENT_CTRL_REG); + val &= ~(1 << hwc->idx); + writel(val, uc_pmu->base + HISI_UC_EVENT_CTRL_REG); +} + +static u64 hisi_uc_pmu_read_counter(struct hisi_pmu *uc_pmu, + struct hw_perf_event *hwc) +{ + return readq(uc_pmu->base + HISI_UC_CNTR_REGn(hwc->idx)); +} + +static void hisi_uc_pmu_write_counter(struct hisi_pmu *uc_pmu, + struct hw_perf_event *hwc, u64 val) +{ + writeq(val, uc_pmu->base + HISI_UC_CNTR_REGn(hwc->idx)); +} + +static void hisi_uc_pmu_enable_counter_int(struct hisi_pmu *uc_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + val = readl(uc_pmu->base + HISI_UC_INT_MASK_REG); + val &= ~(1 << hwc->idx); + writel(val, uc_pmu->base + HISI_UC_INT_MASK_REG); +} + +static void hisi_uc_pmu_disable_counter_int(struct hisi_pmu *uc_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + val = readl(uc_pmu->base + HISI_UC_INT_MASK_REG); + val |= (1 << hwc->idx); + writel(val, uc_pmu->base + HISI_UC_INT_MASK_REG); +} + +static u32 hisi_uc_pmu_get_int_status(struct hisi_pmu *uc_pmu) +{ + return readl(uc_pmu->base + HISI_UC_INT_STS_REG); +} + +static void hisi_uc_pmu_clear_int_status(struct hisi_pmu *uc_pmu, int idx) +{ + writel(1 << idx, uc_pmu->base + HISI_UC_INT_CLEAR_REG); +} + +static int hisi_uc_pmu_init_data(struct platform_device *pdev, + struct hisi_pmu *uc_pmu) +{ + /* + * Use SCCL (Super CPU Cluster) ID and CCL (CPU Cluster) ID to + * identify the topology information of UC PMU devices in the chip. + * They have some CCLs per SCCL and then 4 UC PMU per CCL. + */ + if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", + &uc_pmu->sccl_id)) { + dev_err(&pdev->dev, "Can not read uc sccl-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", + &uc_pmu->ccl_id)) { + dev_err(&pdev->dev, "Can not read uc ccl-id!\n"); + return -EINVAL; + } + + if (device_property_read_u32(&pdev->dev, "hisilicon,sub-id", + &uc_pmu->sub_id)) { + dev_err(&pdev->dev, "Can not read sub-id!\n"); + return -EINVAL; + } + + uc_pmu->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(uc_pmu->base)) { + dev_err(&pdev->dev, "ioremap failed for uc_pmu resource\n"); + return PTR_ERR(uc_pmu->base); + } + + uc_pmu->identifier = readl(uc_pmu->base + HISI_UC_VERSION_REG); + + return 0; +} + +static struct attribute *hisi_uc_pmu_format_attr[] = { + HISI_PMU_FORMAT_ATTR(event, "config:0-7"), + HISI_PMU_FORMAT_ATTR(rd_req_en, "config1:0-0"), + HISI_PMU_FORMAT_ATTR(uring_channel, "config1:4-5"), + HISI_PMU_FORMAT_ATTR(srcid, "config1:6-19"), + HISI_PMU_FORMAT_ATTR(srcid_en, "config1:20-20"), + NULL +}; + +static const struct attribute_group hisi_uc_pmu_format_group = { + .name = "format", + .attrs = hisi_uc_pmu_format_attr, +}; + +static struct attribute *hisi_uc_pmu_events_attr[] = { + HISI_PMU_EVENT_ATTR(sq_time, 0x00), + HISI_PMU_EVENT_ATTR(pq_time, 0x01), + HISI_PMU_EVENT_ATTR(hbm_time, 0x02), + HISI_PMU_EVENT_ATTR(iq_comp_time_cring, 0x03), + HISI_PMU_EVENT_ATTR(iq_comp_time_uring, 0x05), + HISI_PMU_EVENT_ATTR(cpu_rd, 0x10), + HISI_PMU_EVENT_ATTR(cpu_rd64, 0x17), + HISI_PMU_EVENT_ATTR(cpu_rs64, 0x19), + HISI_PMU_EVENT_ATTR(cpu_mru, 0x1a), + HISI_PMU_EVENT_ATTR(cycles, 0x9c), + HISI_PMU_EVENT_ATTR(spipe_hit, 0xb3), + HISI_PMU_EVENT_ATTR(hpipe_hit, 0xdb), + HISI_PMU_EVENT_ATTR(cring_rxdat_cnt, 0xfa), + HISI_PMU_EVENT_ATTR(cring_txdat_cnt, 0xfb), + HISI_PMU_EVENT_ATTR(uring_rxdat_cnt, 0xfc), + HISI_PMU_EVENT_ATTR(uring_txdat_cnt, 0xfd), + NULL +}; + +static const struct attribute_group hisi_uc_pmu_events_group = { + .name = "events", + .attrs = hisi_uc_pmu_events_attr, +}; + +static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); + +static struct attribute *hisi_uc_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL, +}; + +static const struct attribute_group hisi_uc_pmu_cpumask_attr_group = { + .attrs = hisi_uc_pmu_cpumask_attrs, +}; + +static struct device_attribute hisi_uc_pmu_identifier_attr = + __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); + +static struct attribute *hisi_uc_pmu_identifier_attrs[] = { + &hisi_uc_pmu_identifier_attr.attr, + NULL +}; + +static const struct attribute_group hisi_uc_pmu_identifier_group = { + .attrs = hisi_uc_pmu_identifier_attrs, +}; + +static const struct attribute_group *hisi_uc_pmu_attr_groups[] = { + &hisi_uc_pmu_format_group, + &hisi_uc_pmu_events_group, + &hisi_uc_pmu_cpumask_attr_group, + &hisi_uc_pmu_identifier_group, + NULL +}; + +static const struct hisi_uncore_ops hisi_uncore_uc_pmu_ops = { + .check_filter = hisi_uc_pmu_check_filter, + .write_evtype = hisi_uc_pmu_write_evtype, + .get_event_idx = hisi_uncore_pmu_get_event_idx, + .start_counters = hisi_uc_pmu_start_counters, + .stop_counters = hisi_uc_pmu_stop_counters, + .enable_counter = hisi_uc_pmu_enable_counter, + .disable_counter = hisi_uc_pmu_disable_counter, + .enable_counter_int = hisi_uc_pmu_enable_counter_int, + .disable_counter_int = hisi_uc_pmu_disable_counter_int, + .write_counter = hisi_uc_pmu_write_counter, + .read_counter = hisi_uc_pmu_read_counter, + .get_int_status = hisi_uc_pmu_get_int_status, + .clear_int_status = hisi_uc_pmu_clear_int_status, + .enable_filter = hisi_uc_pmu_enable_filter, + .disable_filter = hisi_uc_pmu_disable_filter, +}; + +static int hisi_uc_pmu_dev_probe(struct platform_device *pdev, + struct hisi_pmu *uc_pmu) +{ + int ret; + + ret = hisi_uc_pmu_init_data(pdev, uc_pmu); + if (ret) + return ret; + + ret = hisi_uncore_pmu_init_irq(uc_pmu, pdev); + if (ret) + return ret; + + uc_pmu->pmu_events.attr_groups = hisi_uc_pmu_attr_groups; + uc_pmu->check_event = HISI_UC_EVTYPE_MASK; + uc_pmu->ops = &hisi_uncore_uc_pmu_ops; + uc_pmu->counter_bits = HISI_UC_CNTR_REG_BITS; + uc_pmu->num_counters = HISI_UC_NR_COUNTERS; + uc_pmu->dev = &pdev->dev; + uc_pmu->on_cpu = -1; + + return 0; +} + +static void hisi_uc_pmu_remove_cpuhp_instance(void *hotplug_node) +{ + cpuhp_state_remove_instance_nocalls(hisi_uc_pmu_online, hotplug_node); +} + +static void hisi_uc_pmu_unregister_pmu(void *pmu) +{ + perf_pmu_unregister(pmu); +} + +static int hisi_uc_pmu_probe(struct platform_device *pdev) +{ + struct hisi_pmu *uc_pmu; + char *name; + int ret; + + uc_pmu = devm_kzalloc(&pdev->dev, sizeof(*uc_pmu), GFP_KERNEL); + if (!uc_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, uc_pmu); + + ret = hisi_uc_pmu_dev_probe(pdev, uc_pmu); + if (ret) + return ret; + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_uc%d_%u", + uc_pmu->sccl_id, uc_pmu->ccl_id, uc_pmu->sub_id); + if (!name) + return -ENOMEM; + + ret = cpuhp_state_add_instance(hisi_uc_pmu_online, &uc_pmu->node); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Error registering hotplug\n"); + + ret = devm_add_action_or_reset(&pdev->dev, + hisi_uc_pmu_remove_cpuhp_instance, + &uc_pmu->node); + if (ret) + return ret; + + hisi_pmu_init(uc_pmu, THIS_MODULE); + + ret = perf_pmu_register(&uc_pmu->pmu, name, -1); + if (ret) + return ret; + + return devm_add_action_or_reset(&pdev->dev, + hisi_uc_pmu_unregister_pmu, + &uc_pmu->pmu); +} + +static const struct acpi_device_id hisi_uc_pmu_acpi_match[] = { + { "HISI0291", }, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_uc_pmu_acpi_match); + +static struct platform_driver hisi_uc_pmu_driver = { + .driver = { + .name = "hisi_uc_pmu", + .acpi_match_table = hisi_uc_pmu_acpi_match, + /* + * We have not worked out a safe bind/unbind process, + * Forcefully unbinding during sampling will lead to a + * kernel panic, so this is not supported yet. + */ + .suppress_bind_attrs = true, + }, + .probe = hisi_uc_pmu_probe, +}; + +static int __init hisi_uc_pmu_module_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, + "perf/hisi/uc:online", + hisi_uncore_pmu_online_cpu, + hisi_uncore_pmu_offline_cpu); + if (ret < 0) { + pr_err("UC PMU: Error setup hotplug, ret = %d\n", ret); + return ret; + } + hisi_uc_pmu_online = ret; + + ret = platform_driver_register(&hisi_uc_pmu_driver); + if (ret) + cpuhp_remove_multi_state(hisi_uc_pmu_online); + + return ret; +} +module_init(hisi_uc_pmu_module_init); + +static void __exit hisi_uc_pmu_module_exit(void) +{ + platform_driver_unregister(&hisi_uc_pmu_driver); + cpuhp_remove_multi_state(hisi_uc_pmu_online); +} +module_exit(hisi_uc_pmu_module_exit); + +MODULE_DESCRIPTION("HiSilicon SoC UC uncore PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Junhao He "); From ea713b749f41b36ecef176f817b9ac3e5fccb63e Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:13:51 +0800 Subject: [PATCH 042/227] drivers/perf: hisi: Define a symbol namespace for HiSilicon Uncore PMUs driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- The HiSilicon Uncore PMU framework implements some common functions and exported them to the drivers. Use a specific HISI_PMU namespace for the exported symbols to avoid pollute the generic ones. Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_pmu.c | 36 +++++++++---------- drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c | 1 + drivers/perf/hisilicon/hisi_uncore_uc_pmu.c | 1 + 8 files changed, 25 insertions(+), 18 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c index 40f1bc9f9b913..a383ae07fd0c6 100644 --- a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c @@ -390,6 +390,7 @@ static void __exit hisi_cpa_pmu_module_exit(void) } module_exit(hisi_cpa_pmu_module_exit); +MODULE_IMPORT_NS("HISI_PMU"); MODULE_DESCRIPTION("HiSilicon SoC CPA PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Qi Liu "); diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index ffb039d05d07b..0d12993fcb67b 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -580,6 +580,7 @@ static void __exit hisi_ddrc_pmu_module_exit(void) } module_exit(hisi_ddrc_pmu_module_exit); +MODULE_IMPORT_NS("HISI_PMU"); MODULE_DESCRIPTION("HiSilicon SoC DDRC uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Shaokun Zhang "); diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c index 15caf99e1eefe..56bf727876306 100644 --- a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -582,6 +582,7 @@ static void __exit hisi_hha_pmu_module_exit(void) } module_exit(hisi_hha_pmu_module_exit); +MODULE_IMPORT_NS("HISI_PMU"); MODULE_DESCRIPTION("HiSilicon SoC HHA uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Shaokun Zhang "); diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 794dbcd19b7a7..c469d86d335dc 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -616,6 +616,7 @@ static void __exit hisi_l3c_pmu_module_exit(void) } module_exit(hisi_l3c_pmu_module_exit); +MODULE_IMPORT_NS("HISI_PMU"); MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Anurup M "); diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c index 797cf201996a9..4cd5341cb1aa3 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -570,6 +570,7 @@ static void __exit hisi_pa_pmu_module_exit(void) } module_exit(hisi_pa_pmu_module_exit); +MODULE_IMPORT_NS("HISI_PMU"); MODULE_DESCRIPTION("HiSilicon Protocol Adapter uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Shaokun Zhang "); diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index b356d9b363ec1..5f0acd0d42d61 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -48,7 +48,7 @@ ssize_t hisi_event_sysfs_show(struct device *dev, return sysfs_emit(page, "config=0x%lx\n", (unsigned long)eattr->var); } -EXPORT_SYMBOL_GPL(hisi_event_sysfs_show); +EXPORT_SYMBOL_NS_GPL(hisi_event_sysfs_show, "HISI_PMU"); /* * sysfs cpumask attributes. For uncore PMU, we only have a single CPU to show @@ -60,7 +60,7 @@ ssize_t hisi_cpumask_sysfs_show(struct device *dev, return sysfs_emit(buf, "%d\n", hisi_pmu->on_cpu); } -EXPORT_SYMBOL_GPL(hisi_cpumask_sysfs_show); +EXPORT_SYMBOL_NS_GPL(hisi_cpumask_sysfs_show, "HISI_PMU"); static bool hisi_validate_event_group(struct perf_event *event) { @@ -110,7 +110,7 @@ int hisi_uncore_pmu_get_event_idx(struct perf_event *event) return idx; } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_get_event_idx); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_get_event_idx, "HISI_PMU"); ssize_t hisi_uncore_pmu_identifier_attr_show(struct device *dev, struct device_attribute *attr, @@ -120,7 +120,7 @@ ssize_t hisi_uncore_pmu_identifier_attr_show(struct device *dev, return sysfs_emit(page, "0x%08x\n", hisi_pmu->identifier); } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_identifier_attr_show); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_identifier_attr_show, "HISI_PMU"); static void hisi_uncore_pmu_clear_event_idx(struct hisi_pmu *hisi_pmu, int idx) { @@ -179,7 +179,7 @@ int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu, return 0; } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_init_irq); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_init_irq, "HISI_PMU"); int hisi_uncore_pmu_event_init(struct perf_event *event) { @@ -233,7 +233,7 @@ int hisi_uncore_pmu_event_init(struct perf_event *event) return 0; } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_event_init); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_event_init, "HISI_PMU"); /* * Set the counter to count the event that we're interested in, @@ -287,7 +287,7 @@ void hisi_uncore_pmu_set_event_period(struct perf_event *event) /* Write start value to the hardware event counter */ hisi_pmu->ops->write_counter(hisi_pmu, hwc, val); } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_set_event_period); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_set_event_period, "HISI_PMU"); void hisi_uncore_pmu_event_update(struct perf_event *event) { @@ -308,7 +308,7 @@ void hisi_uncore_pmu_event_update(struct perf_event *event) HISI_MAX_PERIOD(hisi_pmu->counter_bits); local64_add(delta, &event->count); } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_event_update); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_event_update, "HISI_PMU"); void hisi_uncore_pmu_start(struct perf_event *event, int flags) { @@ -331,7 +331,7 @@ void hisi_uncore_pmu_start(struct perf_event *event, int flags) hisi_uncore_pmu_enable_event(event); perf_event_update_userpage(event); } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_start); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_start, "HISI_PMU"); void hisi_uncore_pmu_stop(struct perf_event *event, int flags) { @@ -348,7 +348,7 @@ void hisi_uncore_pmu_stop(struct perf_event *event, int flags) hisi_uncore_pmu_event_update(event); hwc->state |= PERF_HES_UPTODATE; } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_stop); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_stop, "HISI_PMU"); int hisi_uncore_pmu_add(struct perf_event *event, int flags) { @@ -371,7 +371,7 @@ int hisi_uncore_pmu_add(struct perf_event *event, int flags) return 0; } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_add); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_add, "HISI_PMU"); void hisi_uncore_pmu_del(struct perf_event *event, int flags) { @@ -383,14 +383,14 @@ void hisi_uncore_pmu_del(struct perf_event *event, int flags) perf_event_update_userpage(event); hisi_pmu->pmu_events.hw_events[hwc->idx] = NULL; } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_del); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_del, "HISI_PMU"); void hisi_uncore_pmu_read(struct perf_event *event) { /* Read hardware counter and update the perf counter statistics */ hisi_uncore_pmu_event_update(event); } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_read); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_read, "HISI_PMU"); void hisi_uncore_pmu_enable(struct pmu *pmu) { @@ -403,7 +403,7 @@ void hisi_uncore_pmu_enable(struct pmu *pmu) hisi_pmu->ops->start_counters(hisi_pmu); } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_enable); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_enable, "HISI_PMU"); void hisi_uncore_pmu_disable(struct pmu *pmu) { @@ -411,7 +411,7 @@ void hisi_uncore_pmu_disable(struct pmu *pmu) hisi_pmu->ops->stop_counters(hisi_pmu); } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_disable); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_disable, "HISI_PMU"); /* @@ -504,7 +504,7 @@ int hisi_uncore_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) return 0; } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_online_cpu); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_online_cpu, "HISI_PMU"); int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) { @@ -537,7 +537,7 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) return 0; } -EXPORT_SYMBOL_GPL(hisi_uncore_pmu_offline_cpu); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_offline_cpu, "HISI_PMU"); void hisi_pmu_init(struct hisi_pmu *hisi_pmu, struct module *module) { @@ -556,6 +556,6 @@ void hisi_pmu_init(struct hisi_pmu *hisi_pmu, struct module *module) pmu->attr_groups = hisi_pmu->pmu_events.attr_groups; pmu->capabilities = PERF_PMU_CAP_NO_EXCLUDE; } -EXPORT_SYMBOL_GPL(hisi_pmu_init); +EXPORT_SYMBOL_NS_GPL(hisi_pmu_init, "HISI_PMU"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c index e706ca5676764..3dc428d659ce9 100644 --- a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c @@ -508,6 +508,7 @@ static void __exit hisi_sllc_pmu_module_exit(void) } module_exit(hisi_sllc_pmu_module_exit); +MODULE_IMPORT_NS("HISI_PMU"); MODULE_DESCRIPTION("HiSilicon SLLC uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Shaokun Zhang "); diff --git a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c index 63da05e5831c1..d90bb8c877b8d 100644 --- a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c @@ -573,6 +573,7 @@ static void __exit hisi_uc_pmu_module_exit(void) } module_exit(hisi_uc_pmu_module_exit); +MODULE_IMPORT_NS("HISI_PMU"); MODULE_DESCRIPTION("HiSilicon SoC UC uncore PMU driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Junhao He "); From a58e53110ccd58970602ca2f103d07b44ca612b2 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:13:52 +0800 Subject: [PATCH 043/227] drivers/perf: hisi: Don't update the associated_cpus on CPU offline driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- Event will be scheduled on CPU of hisi_pmu::on_cpu which is selected from the intersection of hisi_pmu::associated_cpus and online CPUs. So the associated_cpus don't need to be maintained with online CPUs. This will save one update operation if one associated CPU is offlined. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pmu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index 5f0acd0d42d61..ab8dc919da935 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -513,9 +513,6 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) cpumask_t pmu_online_cpus; unsigned int target; - if (!cpumask_test_and_clear_cpu(cpu, &hisi_pmu->associated_cpus)) - return 0; - /* Nothing to do if this CPU doesn't own the PMU */ if (hisi_pmu->on_cpu != cpu) return 0; From 2d0149dd4149bdf5f21208a30ad2ed23bf719d07 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:13:53 +0800 Subject: [PATCH 044/227] drivers/perf: hisi: Migrate to one online CPU if no associated one online driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- If the selected CPU hisi_pmu::on_cpu goes offline, driver will select a new online CPU from hisi_pmu::associated_cpus, or if no online CPU found the PMU context won't be migrated. However for uncore PMUs the associated CPUs are just a peference and it also works to schedule the events on any online CPUs. So add a fallback to choose an online CPU if no associated CPUs found. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pmu.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index ab8dc919da935..d41bdb6331477 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -524,6 +524,10 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) cpumask_and(&pmu_online_cpus, &hisi_pmu->associated_cpus, cpu_online_mask); target = cpumask_any_but(&pmu_online_cpus, cpu); + + if (target >= nr_cpu_ids) + target = cpumask_any_but(cpu_online_mask, cpu); + if (target >= nr_cpu_ids) return 0; From 6d54e54c9554fb920ff281655d00e807bf784898 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:13:54 +0800 Subject: [PATCH 045/227] drivers/perf: hisi: Refactor the detection of associated CPUs driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- There are two type of PMUs supported currently: 1) PMUs locate on SCCL (Super CPU Cluster [1]), associated with certain CCL (CPU cluster [1])(e.g. L3C PMU) or not (e.g. DDRC PMU) 2) PMUs locate on the SICL (Super IO Cluster [1]), which has no association with certain CPU topology (e.g. CPA PMU) Currently the associated CPUs of the PMU is detected in the cpuhp online callback as below: - for type 1) the CPUs match PMU's sccl_id and ccl_id - for type 2) PMU's sccl_id is -1 and all online CPUs will be associated Since uncore PMUs are not bound to certain CPU context and event could be counting started by any online CPU, the associated CPUs are just a preference. Below disadvantages are observed in current implementation: - the PMU cannot be used if its associated CPUs are offline - SICL PMUs are associated to all the online CPUs implicitly without the consideration of locality So refactor the way we detect the associated CPUs in below aspects: - add a clear definition of hisi_pmu::associated_cpus - initialize hisi_pmu::on_cpu based on locality if no associated CPU found, otherwise update it from associated CPUs - drop the detection with a sccl_id of -1 for SICL PMUs [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/admin-guide/perf/hisi-pmu.rst?h=v6.12-rc1 Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pmu.c | 22 +++++++++++++++------- drivers/perf/hisilicon/hisi_uncore_pmu.h | 6 +++++- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index d41bdb6331477..51abe6cde7583 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -466,10 +466,6 @@ static bool hisi_pmu_cpu_is_associated_pmu(struct hisi_pmu *hisi_pmu) { int sccl_id, ccl_id; - /* If SCCL_ID is -1, the PMU is in a SICL and has no CPU affinity */ - if (hisi_pmu->sccl_id == -1) - return true; - if (hisi_pmu->ccl_id == -1) { /* If CCL_ID is -1, the PMU only shares the same SCCL */ hisi_read_sccl_and_ccl_id(&sccl_id, NULL); @@ -487,13 +483,25 @@ int hisi_uncore_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) struct hisi_pmu *hisi_pmu = hlist_entry_safe(node, struct hisi_pmu, node); - if (!hisi_pmu_cpu_is_associated_pmu(hisi_pmu)) + /* + * If the CPU is not associated to PMU, initialize the hisi_pmu->on_cpu + * based on the locality if it hasn't been initialized yet. For PMUs + * do have associated CPUs, it'll be updated later. + */ + if (!hisi_pmu_cpu_is_associated_pmu(hisi_pmu)) { + if (hisi_pmu->on_cpu != -1) + return 0; + + hisi_pmu->on_cpu = cpumask_local_spread(0, dev_to_node(hisi_pmu->dev)); + WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(cpu))); return 0; + } cpumask_set_cpu(cpu, &hisi_pmu->associated_cpus); - /* If another CPU is already managing this PMU, simply return. */ - if (hisi_pmu->on_cpu != -1) + /* If another associated CPU is already managing this PMU, simply return. */ + if (hisi_pmu->on_cpu != -1 && + cpumask_test_cpu(hisi_pmu->on_cpu, &hisi_pmu->associated_cpus)) return 0; /* Use this CPU in cpumask for event counting */ diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 92402aa69d70f..0719dac40434e 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -87,7 +87,11 @@ struct hisi_pmu { const struct hisi_uncore_ops *ops; const struct hisi_pmu_dev_info *dev_info; struct hisi_pmu_hwevents pmu_events; - /* associated_cpus: All CPUs associated with the PMU */ + /* + * CPUs associated to the PMU and are preferred to use for counting. + * Could be empty if PMU has no association (e.g. PMU on SICL), in + * which case any online CPU will be used. + */ cpumask_t associated_cpus; /* CPU used for counting */ int on_cpu; From 0b73c74cbf6460a632c76fe4bb601372413cba4f Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:13:55 +0800 Subject: [PATCH 046/227] drivers/perf: hisi: Extract topology information to a separate structure driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- HiSilicon Uncore PMUs are identified by the IDs of the topology element on which the PMUs are located. Add a new separate struct hisi_pmu_toplogy to encapsulate this information. Add additional documentation on the meaning of each ID. - make sccl_id and sicl_id into a union since they're exclusive. It can also be accessed by scl_id if the SICL/SCCL distinction is not relevant - make index_id and sub_id signed so -1 may be used to indicate the PMU doesn't have this topology element or it could not be retrieved. This patch should have no functional changes. Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c | 12 +++--- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 18 ++++----- drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 12 +++--- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 8 ++-- drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 14 +++---- drivers/perf/hisilicon/hisi_uncore_pmu.c | 7 ++-- drivers/perf/hisilicon/hisi_uncore_pmu.h | 38 +++++++++++++++---- drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c | 10 ++--- drivers/perf/hisilicon/hisi_uncore_uc_pmu.c | 11 +++--- 9 files changed, 78 insertions(+), 52 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c index a383ae07fd0c6..982df850b9121 100644 --- a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c @@ -181,19 +181,19 @@ static int hisi_cpa_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *cpa_pmu) { if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &cpa_pmu->sicl_id)) { + &cpa_pmu->topo.sicl_id)) { dev_err(&pdev->dev, "Can not read sicl-id\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", - &cpa_pmu->index_id)) { + &cpa_pmu->topo.index_id)) { dev_err(&pdev->dev, "Cannot read idx-id\n"); return -EINVAL; } - cpa_pmu->ccl_id = -1; - cpa_pmu->sccl_id = -1; + cpa_pmu->topo.ccl_id = -1; + cpa_pmu->topo.sccl_id = -1; cpa_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(cpa_pmu->base)) return PTR_ERR(cpa_pmu->base); @@ -311,8 +311,8 @@ static int hisi_cpa_pmu_probe(struct platform_device *pdev) if (ret) return ret; - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%d_cpa%u", - cpa_pmu->sicl_id, cpa_pmu->index_id); + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%d_cpa%d", + cpa_pmu->topo.sicl_id, cpa_pmu->topo.index_id); if (!name) return -ENOMEM; diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index 0d12993fcb67b..bce75f340358b 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -302,18 +302,18 @@ static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, * DDRC PMU, while SCCL_ID is in MPIDR[aff2]. */ if (device_property_read_u32(&pdev->dev, "hisilicon,ch-id", - &ddrc_pmu->index_id)) { + &ddrc_pmu->topo.index_id)) { dev_err(&pdev->dev, "Can not read ddrc channel-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &ddrc_pmu->sccl_id)) { + &ddrc_pmu->topo.sccl_id)) { dev_err(&pdev->dev, "Can not read ddrc sccl-id!\n"); return -EINVAL; } /* DDRC PMUs only share the same SCCL */ - ddrc_pmu->ccl_id = -1; + ddrc_pmu->topo.ccl_id = -1; ddrc_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ddrc_pmu->base)) { @@ -324,7 +324,7 @@ static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, ddrc_pmu->identifier = readl(ddrc_pmu->base + DDRC_VERSION); if (ddrc_pmu->identifier >= HISI_PMU_V2) { if (device_property_read_u32(&pdev->dev, "hisilicon,sub-id", - &ddrc_pmu->sub_id)) { + &ddrc_pmu->topo.sub_id)) { dev_err(&pdev->dev, "Can not read sub-id!\n"); return -EINVAL; } @@ -501,13 +501,13 @@ static int hisi_ddrc_pmu_probe(struct platform_device *pdev) if (ddrc_pmu->identifier >= HISI_PMU_V2) name = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "hisi_sccl%u_ddrc%u_%u", - ddrc_pmu->sccl_id, ddrc_pmu->index_id, - ddrc_pmu->sub_id); + "hisi_sccl%d_ddrc%d_%d", + ddrc_pmu->topo.sccl_id, ddrc_pmu->topo.index_id, + ddrc_pmu->topo.sub_id); else name = devm_kasprintf(&pdev->dev, GFP_KERNEL, - "hisi_sccl%u_ddrc%u", ddrc_pmu->sccl_id, - ddrc_pmu->index_id); + "hisi_sccl%d_ddrc%d", ddrc_pmu->topo.sccl_id, + ddrc_pmu->topo.index_id); if (!name) return -ENOMEM; diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c index 56bf727876306..c4283da17f058 100644 --- a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -300,7 +300,7 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev, * SCCL_ID is in MPIDR[aff2]. */ if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &hha_pmu->sccl_id)) { + &hha_pmu->topo.sccl_id)) { dev_err(&pdev->dev, "Can not read hha sccl-id!\n"); return -EINVAL; } @@ -310,7 +310,7 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev, * both "hisilicon, idx-id" as preference, if available. */ if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", - &hha_pmu->index_id)) { + &hha_pmu->topo.index_id)) { status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), "_UID", NULL, &id); if (ACPI_FAILURE(status)) { @@ -318,10 +318,10 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - hha_pmu->index_id = id; + hha_pmu->topo.index_id = id; } /* HHA PMUs only share the same SCCL */ - hha_pmu->ccl_id = -1; + hha_pmu->topo.ccl_id = -1; hha_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hha_pmu->base)) { @@ -510,8 +510,8 @@ static int hisi_hha_pmu_probe(struct platform_device *pdev) if (ret) return ret; - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_hha%u", - hha_pmu->sccl_id, hha_pmu->index_id); + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_hha%d", + hha_pmu->topo.sccl_id, hha_pmu->topo.index_id); if (!name) return -ENOMEM; diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index c469d86d335dc..1c0783a643f7e 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -360,13 +360,13 @@ static int hisi_l3c_pmu_init_data(struct platform_device *pdev, * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1]. */ if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &l3c_pmu->sccl_id)) { + &l3c_pmu->topo.sccl_id)) { dev_err(&pdev->dev, "Can not read l3c sccl-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", - &l3c_pmu->ccl_id)) { + &l3c_pmu->topo.ccl_id)) { dev_err(&pdev->dev, "Can not read l3c ccl-id!\n"); return -EINVAL; } @@ -544,8 +544,8 @@ static int hisi_l3c_pmu_probe(struct platform_device *pdev) if (ret) return ret; - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_l3c%u", - l3c_pmu->sccl_id, l3c_pmu->ccl_id); + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_l3c%d", + l3c_pmu->topo.sccl_id, l3c_pmu->topo.ccl_id); if (!name) return -ENOMEM; diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c index 4cd5341cb1aa3..57535defff71d 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -274,19 +274,19 @@ static int hisi_pa_pmu_init_data(struct platform_device *pdev, * to identify the PA PMU. */ if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &pa_pmu->sicl_id)) { + &pa_pmu->topo.sicl_id)) { dev_err(&pdev->dev, "Cannot read sicl-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", - &pa_pmu->index_id)) { + &pa_pmu->topo.index_id)) { dev_err(&pdev->dev, "Cannot read idx-id!\n"); return -EINVAL; } - pa_pmu->ccl_id = -1; - pa_pmu->sccl_id = -1; + pa_pmu->topo.ccl_id = -1; + pa_pmu->topo.sccl_id = -1; pa_pmu->dev_info = device_get_match_data(&pdev->dev); if (!pa_pmu->dev_info) @@ -488,9 +488,9 @@ static int hisi_pa_pmu_probe(struct platform_device *pdev) if (ret) return ret; - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%d_%s%u", - pa_pmu->sicl_id, pa_pmu->dev_info->name, - pa_pmu->index_id); + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sicl%d_%s%d", + pa_pmu->topo.sicl_id, pa_pmu->dev_info->name, + pa_pmu->topo.index_id); if (!name) return -ENOMEM; diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index 51abe6cde7583..5314950aaebb3 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -464,18 +464,19 @@ static void hisi_read_sccl_and_ccl_id(int *scclp, int *cclp) */ static bool hisi_pmu_cpu_is_associated_pmu(struct hisi_pmu *hisi_pmu) { + struct hisi_pmu_topology *topo = &hisi_pmu->topo; int sccl_id, ccl_id; - if (hisi_pmu->ccl_id == -1) { + if (topo->ccl_id == -1) { /* If CCL_ID is -1, the PMU only shares the same SCCL */ hisi_read_sccl_and_ccl_id(&sccl_id, NULL); - return sccl_id == hisi_pmu->sccl_id; + return sccl_id == topo->sccl_id; } hisi_read_sccl_and_ccl_id(&sccl_id, &ccl_id); - return sccl_id == hisi_pmu->sccl_id && ccl_id == hisi_pmu->ccl_id; + return sccl_id == topo->sccl_id && ccl_id == topo->ccl_id; } int hisi_uncore_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 0719dac40434e..4bb33118a1654 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -81,12 +81,43 @@ struct hisi_pmu_hwevents { const struct attribute_group **attr_groups; }; +/** + * struct hisi_pmu_topology - Describe the topology hierarchy on which the PMU + * is located. + * @sccl_id: ID of the SCCL on which the PMU locate is located. + * @sicl_id: ID of the SICL on which the PMU locate is located. + * @scl_id: ID used by the core which is unaware of the SCCL/SICL. + * @ccl_id: ID of the CCL (CPU cluster) on which the PMU is located. + * @index_id: the ID of the PMU module if there're several PMUs at a + * particularly location in the topology. + * @sub_id: submodule ID of the PMU. For example we use this for DDRC PMU v2 + * since each DDRC has more than one DMC + * + * The ID will be -1 if the PMU isn't located on a certain topology. + */ +struct hisi_pmu_topology { + /* + * SCCL (Super CPU CLuster) and SICL (Super I/O Cluster) are parallel + * so a PMU cannot locate on a SCCL and a SICL. If the SCCL/SICL + * distinction is not relevant, use scl_id instead. + */ + union { + int sccl_id; + int sicl_id; + int scl_id; + }; + int ccl_id; + int index_id; + int sub_id; +}; + /* Generic pmu struct for different pmu types */ struct hisi_pmu { struct pmu pmu; const struct hisi_uncore_ops *ops; const struct hisi_pmu_dev_info *dev_info; struct hisi_pmu_hwevents pmu_events; + struct hisi_pmu_topology topo; /* * CPUs associated to the PMU and are preferred to use for counting. * Could be empty if PMU has no association (e.g. PMU on SICL), in @@ -98,14 +129,7 @@ struct hisi_pmu { int irq; struct device *dev; struct hlist_node node; - int sccl_id; - int sicl_id; - int ccl_id; void __iomem *base; - /* the ID of the PMU modules */ - u32 index_id; - /* For DDRC PMU v2: each DDRC has more than one DMC */ - u32 sub_id; int num_counters; int counter_bits; /* check event code range */ diff --git a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c index 3dc428d659ce9..dd16140144481 100644 --- a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c @@ -293,19 +293,19 @@ static int hisi_sllc_pmu_init_data(struct platform_device *pdev, * while SCCL_ID is from MPIDR_EL1 by CPU. */ if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &sllc_pmu->sccl_id)) { + &sllc_pmu->topo.sccl_id)) { dev_err(&pdev->dev, "Cannot read sccl-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", - &sllc_pmu->index_id)) { + &sllc_pmu->topo.index_id)) { dev_err(&pdev->dev, "Cannot read idx-id!\n"); return -EINVAL; } /* SLLC PMUs only share the same SCCL */ - sllc_pmu->ccl_id = -1; + sllc_pmu->topo.ccl_id = -1; sllc_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sllc_pmu->base)) { @@ -433,8 +433,8 @@ static int hisi_sllc_pmu_probe(struct platform_device *pdev) if (ret) return ret; - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%u_sllc%u", - sllc_pmu->sccl_id, sllc_pmu->index_id); + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_sllc%d", + sllc_pmu->topo.sccl_id, sllc_pmu->topo.index_id); if (!name) return -ENOMEM; diff --git a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c index d90bb8c877b8d..b432b79187e32 100644 --- a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c @@ -332,19 +332,19 @@ static int hisi_uc_pmu_init_data(struct platform_device *pdev, * They have some CCLs per SCCL and then 4 UC PMU per CCL. */ if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &uc_pmu->sccl_id)) { + &uc_pmu->topo.sccl_id)) { dev_err(&pdev->dev, "Can not read uc sccl-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", - &uc_pmu->ccl_id)) { + &uc_pmu->topo.ccl_id)) { dev_err(&pdev->dev, "Can not read uc ccl-id!\n"); return -EINVAL; } if (device_property_read_u32(&pdev->dev, "hisilicon,sub-id", - &uc_pmu->sub_id)) { + &uc_pmu->topo.sub_id)) { dev_err(&pdev->dev, "Can not read sub-id!\n"); return -EINVAL; } @@ -498,8 +498,9 @@ static int hisi_uc_pmu_probe(struct platform_device *pdev) if (ret) return ret; - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_uc%d_%u", - uc_pmu->sccl_id, uc_pmu->ccl_id, uc_pmu->sub_id); + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_uc%d_%d", + uc_pmu->topo.sccl_id, uc_pmu->topo.ccl_id, + uc_pmu->topo.sub_id); if (!name) return -ENOMEM; From 04edc77cefd34da6e45be5aad37705c874cffa0d Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:13:56 +0800 Subject: [PATCH 047/227] drivers/perf: hisi: Add a common function to retrieve topology from firmware driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- Currently each type of uncore PMU driver uses almost the same routine and the same firmware interface (or properties) to retrieve the topology information, then reset the unused IDs to -1. Extract the common parts to the framework in hisi_uncore_pmu_init_topology(). Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c | 10 +++---- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 10 +++---- drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 10 +++---- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 8 ++--- drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 13 ++++---- drivers/perf/hisilicon/hisi_uncore_pmu.c | 30 +++++++++++++++++++ drivers/perf/hisilicon/hisi_uncore_pmu.h | 1 + drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c | 11 +++---- drivers/perf/hisilicon/hisi_uncore_uc_pmu.c | 12 ++++---- 9 files changed, 63 insertions(+), 42 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c index 982df850b9121..749470242782e 100644 --- a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c @@ -180,20 +180,18 @@ MODULE_DEVICE_TABLE(acpi, hisi_cpa_pmu_acpi_match); static int hisi_cpa_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *cpa_pmu) { - if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &cpa_pmu->topo.sicl_id)) { + hisi_uncore_pmu_init_topology(cpa_pmu, &pdev->dev); + + if (cpa_pmu->topo.sicl_id < 0) { dev_err(&pdev->dev, "Can not read sicl-id\n"); return -EINVAL; } - if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", - &cpa_pmu->topo.index_id)) { + if (cpa_pmu->topo.index_id < 0) { dev_err(&pdev->dev, "Cannot read idx-id\n"); return -EINVAL; } - cpa_pmu->topo.ccl_id = -1; - cpa_pmu->topo.sccl_id = -1; cpa_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(cpa_pmu->base)) return PTR_ERR(cpa_pmu->base); diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index bce75f340358b..ea749222a81ec 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -297,6 +297,8 @@ MODULE_DEVICE_TABLE(acpi, hisi_ddrc_pmu_acpi_match); static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *ddrc_pmu) { + hisi_uncore_pmu_init_topology(ddrc_pmu, &pdev->dev); + /* * Use the SCCL_ID and DDRC channel ID to identify the * DDRC PMU, while SCCL_ID is in MPIDR[aff2]. @@ -307,13 +309,10 @@ static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, return -EINVAL; } - if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &ddrc_pmu->topo.sccl_id)) { + if (ddrc_pmu->topo.sccl_id < 0) { dev_err(&pdev->dev, "Can not read ddrc sccl-id!\n"); return -EINVAL; } - /* DDRC PMUs only share the same SCCL */ - ddrc_pmu->topo.ccl_id = -1; ddrc_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ddrc_pmu->base)) { @@ -323,8 +322,7 @@ static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, ddrc_pmu->identifier = readl(ddrc_pmu->base + DDRC_VERSION); if (ddrc_pmu->identifier >= HISI_PMU_V2) { - if (device_property_read_u32(&pdev->dev, "hisilicon,sub-id", - &ddrc_pmu->topo.sub_id)) { + if (ddrc_pmu->topo.sub_id < 0) { dev_err(&pdev->dev, "Can not read sub-id!\n"); return -EINVAL; } diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c index c4283da17f058..238fb51c4a15b 100644 --- a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -295,12 +295,13 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev, unsigned long long id; acpi_status status; + hisi_uncore_pmu_init_topology(hha_pmu, &pdev->dev); + /* * Use SCCL_ID and UID to identify the HHA PMU, while * SCCL_ID is in MPIDR[aff2]. */ - if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &hha_pmu->topo.sccl_id)) { + if (hha_pmu->topo.sccl_id < 0) { dev_err(&pdev->dev, "Can not read hha sccl-id!\n"); return -EINVAL; } @@ -309,8 +310,7 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev, * Early versions of BIOS support _UID by mistake, so we support * both "hisilicon, idx-id" as preference, if available. */ - if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", - &hha_pmu->topo.index_id)) { + if (hha_pmu->topo.index_id < 0) { status = acpi_evaluate_integer(ACPI_HANDLE(&pdev->dev), "_UID", NULL, &id); if (ACPI_FAILURE(status)) { @@ -320,8 +320,6 @@ static int hisi_hha_pmu_init_data(struct platform_device *pdev, hha_pmu->topo.index_id = id; } - /* HHA PMUs only share the same SCCL */ - hha_pmu->topo.ccl_id = -1; hha_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(hha_pmu->base)) { diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 1c0783a643f7e..16f78222e88ed 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -355,18 +355,18 @@ MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); static int hisi_l3c_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *l3c_pmu) { + hisi_uncore_pmu_init_topology(l3c_pmu, &pdev->dev); + /* * Use the SCCL_ID and CCL_ID to identify the L3C PMU, while * SCCL_ID is in MPIDR[aff2] and CCL_ID is in MPIDR[aff1]. */ - if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &l3c_pmu->topo.sccl_id)) { + if (l3c_pmu->topo.sccl_id < 0) { dev_err(&pdev->dev, "Can not read l3c sccl-id!\n"); return -EINVAL; } - if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", - &l3c_pmu->topo.ccl_id)) { + if (l3c_pmu->topo.ccl_id < 0) { dev_err(&pdev->dev, "Can not read l3c ccl-id!\n"); return -EINVAL; } diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c index 57535defff71d..6917058808369 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -269,24 +269,25 @@ static void hisi_pa_pmu_clear_int_status(struct hisi_pmu *pa_pmu, int idx) static int hisi_pa_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *pa_pmu) { + hisi_uncore_pmu_init_topology(pa_pmu, &pdev->dev); + /* * As PA PMU is in a SICL, use the SICL_ID and the index ID * to identify the PA PMU. */ - if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &pa_pmu->topo.sicl_id)) { + if (pa_pmu->topo.sicl_id < 0) { dev_err(&pdev->dev, "Cannot read sicl-id!\n"); return -EINVAL; } - if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", - &pa_pmu->topo.index_id)) { + if (pa_pmu->topo.index_id < 0) { dev_err(&pdev->dev, "Cannot read idx-id!\n"); return -EINVAL; } - pa_pmu->topo.ccl_id = -1; - pa_pmu->topo.sccl_id = -1; + pa_pmu->dev_info = device_get_match_data(&pdev->dev); + if (!pa_pmu->dev_info) + return -ENODEV; pa_pmu->dev_info = device_get_match_data(&pdev->dev); if (!pa_pmu->dev_info) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index 5314950aaebb3..35a463177a59f 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -549,6 +550,35 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) } EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_offline_cpu, "HISI_PMU"); +/* + * Retrieve the topology information from the firmware for the hisi_pmu device. + * The topology ID will be -1 if we cannot initialize it, it may either due to + * the PMU doesn't locate on this certain topology or the firmware needs to be + * fixed. + */ +void hisi_uncore_pmu_init_topology(struct hisi_pmu *hisi_pmu, struct device *dev) +{ + struct hisi_pmu_topology *topo = &hisi_pmu->topo; + + topo->sccl_id = -1; + topo->ccl_id = -1; + topo->index_id = -1; + topo->sub_id = -1; + + if (device_property_read_u32(dev, "hisilicon,scl-id", &topo->sccl_id)) + dev_dbg(dev, "no scl-id present\n"); + + if (device_property_read_u32(dev, "hisilicon,ccl-id", &topo->ccl_id)) + dev_dbg(dev, "no ccl-id present\n"); + + if (device_property_read_u32(dev, "hisilicon,idx-id", &topo->index_id)) + dev_dbg(dev, "no idx-id present\n"); + + if (device_property_read_u32(dev, "hisilicon,sub-id", &topo->sub_id)) + dev_dbg(dev, "no sub-id present\n"); +} +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_init_topology, "HISI_PMU"); + void hisi_pmu_init(struct hisi_pmu *hisi_pmu, struct module *module) { struct pmu *pmu = &hisi_pmu->pmu; diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 4bb33118a1654..99370ca84ce0d 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -162,6 +162,7 @@ ssize_t hisi_uncore_pmu_identifier_attr_show(struct device *dev, char *page); int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu, struct platform_device *pdev); +void hisi_uncore_pmu_init_topology(struct hisi_pmu *hisi_pmu, struct device *dev); void hisi_pmu_init(struct hisi_pmu *hisi_pmu, struct module *module); #endif /* __HISI_UNCORE_PMU_H__ */ diff --git a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c index dd16140144481..ffebb188a8212 100644 --- a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c @@ -288,25 +288,22 @@ MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match); static int hisi_sllc_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *sllc_pmu) { + hisi_uncore_pmu_init_topology(sllc_pmu, &pdev->dev); + /* * Use the SCCL_ID and the index ID to identify the SLLC PMU, * while SCCL_ID is from MPIDR_EL1 by CPU. */ - if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &sllc_pmu->topo.sccl_id)) { + if (sllc_pmu->topo.sccl_id < 0) { dev_err(&pdev->dev, "Cannot read sccl-id!\n"); return -EINVAL; } - if (device_property_read_u32(&pdev->dev, "hisilicon,idx-id", - &sllc_pmu->topo.index_id)) { + if (sllc_pmu->topo.index_id < 0) { dev_err(&pdev->dev, "Cannot read idx-id!\n"); return -EINVAL; } - /* SLLC PMUs only share the same SCCL */ - sllc_pmu->topo.ccl_id = -1; - sllc_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sllc_pmu->base)) { dev_err(&pdev->dev, "ioremap failed for sllc_pmu resource.\n"); diff --git a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c index b432b79187e32..c0934eeec4bde 100644 --- a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "hisi_uncore_pmu.h" @@ -326,25 +325,24 @@ static void hisi_uc_pmu_clear_int_status(struct hisi_pmu *uc_pmu, int idx) static int hisi_uc_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *uc_pmu) { + hisi_uncore_pmu_init_topology(uc_pmu, &pdev->dev); + /* * Use SCCL (Super CPU Cluster) ID and CCL (CPU Cluster) ID to * identify the topology information of UC PMU devices in the chip. * They have some CCLs per SCCL and then 4 UC PMU per CCL. */ - if (device_property_read_u32(&pdev->dev, "hisilicon,scl-id", - &uc_pmu->topo.sccl_id)) { + if (uc_pmu->topo.sccl_id < 0) { dev_err(&pdev->dev, "Can not read uc sccl-id!\n"); return -EINVAL; } - if (device_property_read_u32(&pdev->dev, "hisilicon,ccl-id", - &uc_pmu->topo.ccl_id)) { + if (uc_pmu->topo.ccl_id < 0) { dev_err(&pdev->dev, "Can not read uc ccl-id!\n"); return -EINVAL; } - if (device_property_read_u32(&pdev->dev, "hisilicon,sub-id", - &uc_pmu->topo.sub_id)) { + if (uc_pmu->topo.sub_id < 0) { dev_err(&pdev->dev, "Can not read sub-id!\n"); return -EINVAL; } From 407a3bf0545b5bcc537c6bd32f10de0e0e6be9dc Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:13:57 +0800 Subject: [PATCH 048/227] drivers/perf: hisi: Provide a generic implementation of cpumask/identifier driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- Each type of HiSilicon Uncore PMU has the following sysfs attributes: - format: bitmask in perf_event_attr::config[012] of corresponding attribute - event: events name and corresponding event code - cpumask: range of CPUs the events can be opened on - identifier: the version of this PMU Different types of PMU have different implementations of the "format" and "event" but all share the same implementation of the "cpumask" and "identifier". Thus we can move cpumask and identifier to the hisi_uncore_pmu framework and drivers can use the generic implementation. Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c | 29 +------ drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 33 ++------ drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 33 ++------ drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 33 ++------ drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 37 ++------- drivers/perf/hisilicon/hisi_uncore_pmu.c | 83 ++++++++++++------- drivers/perf/hisilicon/hisi_uncore_pmu.h | 4 + drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c | 29 +------ drivers/perf/hisilicon/hisi_uncore_uc_pmu.c | 29 +------ 9 files changed, 89 insertions(+), 221 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c index 749470242782e..006afb4d12085 100644 --- a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c @@ -225,34 +225,11 @@ static const struct attribute_group hisi_cpa_pmu_events_group = { .attrs = hisi_cpa_pmu_events_attr, }; -static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); - -static struct attribute *hisi_cpa_pmu_cpumask_attrs[] = { - &dev_attr_cpumask.attr, - NULL -}; - -static const struct attribute_group hisi_cpa_pmu_cpumask_attr_group = { - .attrs = hisi_cpa_pmu_cpumask_attrs, -}; - -static struct device_attribute hisi_cpa_pmu_identifier_attr = - __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); - -static struct attribute *hisi_cpa_pmu_identifier_attrs[] = { - &hisi_cpa_pmu_identifier_attr.attr, - NULL -}; - -static const struct attribute_group hisi_cpa_pmu_identifier_group = { - .attrs = hisi_cpa_pmu_identifier_attrs, -}; - static const struct attribute_group *hisi_cpa_pmu_attr_groups[] = { &hisi_cpa_pmu_format_group, &hisi_cpa_pmu_events_group, - &hisi_cpa_pmu_cpumask_attr_group, - &hisi_cpa_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -388,7 +365,7 @@ static void __exit hisi_cpa_pmu_module_exit(void) } module_exit(hisi_cpa_pmu_module_exit); -MODULE_IMPORT_NS("HISI_PMU"); +MODULE_IMPORT_NS(HISI_PMU); MODULE_DESCRIPTION("HiSilicon SoC CPA PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Qi Liu "); diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index ea749222a81ec..ae990fbda7292 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -380,42 +380,19 @@ static const struct attribute_group hisi_ddrc_pmu_v2_events_group = { .attrs = hisi_ddrc_pmu_v2_events_attr, }; -static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); - -static struct attribute *hisi_ddrc_pmu_cpumask_attrs[] = { - &dev_attr_cpumask.attr, - NULL, -}; - -static const struct attribute_group hisi_ddrc_pmu_cpumask_attr_group = { - .attrs = hisi_ddrc_pmu_cpumask_attrs, -}; - -static struct device_attribute hisi_ddrc_pmu_identifier_attr = - __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); - -static struct attribute *hisi_ddrc_pmu_identifier_attrs[] = { - &hisi_ddrc_pmu_identifier_attr.attr, - NULL -}; - -static const struct attribute_group hisi_ddrc_pmu_identifier_group = { - .attrs = hisi_ddrc_pmu_identifier_attrs, -}; - static const struct attribute_group *hisi_ddrc_pmu_v1_attr_groups[] = { &hisi_ddrc_pmu_v1_format_group, &hisi_ddrc_pmu_v1_events_group, - &hisi_ddrc_pmu_cpumask_attr_group, - &hisi_ddrc_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL, }; static const struct attribute_group *hisi_ddrc_pmu_v2_attr_groups[] = { &hisi_ddrc_pmu_v2_format_group, &hisi_ddrc_pmu_v2_events_group, - &hisi_ddrc_pmu_cpumask_attr_group, - &hisi_ddrc_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -578,7 +555,7 @@ static void __exit hisi_ddrc_pmu_module_exit(void) } module_exit(hisi_ddrc_pmu_module_exit); -MODULE_IMPORT_NS("HISI_PMU"); +MODULE_IMPORT_NS(HISI_PMU); MODULE_DESCRIPTION("HiSilicon SoC DDRC uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Shaokun Zhang "); diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c index 238fb51c4a15b..cb928b450b97b 100644 --- a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -405,42 +405,19 @@ static const struct attribute_group hisi_hha_pmu_v2_events_group = { .attrs = hisi_hha_pmu_v2_events_attr, }; -static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); - -static struct attribute *hisi_hha_pmu_cpumask_attrs[] = { - &dev_attr_cpumask.attr, - NULL, -}; - -static const struct attribute_group hisi_hha_pmu_cpumask_attr_group = { - .attrs = hisi_hha_pmu_cpumask_attrs, -}; - -static struct device_attribute hisi_hha_pmu_identifier_attr = - __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); - -static struct attribute *hisi_hha_pmu_identifier_attrs[] = { - &hisi_hha_pmu_identifier_attr.attr, - NULL -}; - -static const struct attribute_group hisi_hha_pmu_identifier_group = { - .attrs = hisi_hha_pmu_identifier_attrs, -}; - static const struct attribute_group *hisi_hha_pmu_v1_attr_groups[] = { &hisi_hha_pmu_v1_format_group, &hisi_hha_pmu_v1_events_group, - &hisi_hha_pmu_cpumask_attr_group, - &hisi_hha_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL, }; static const struct attribute_group *hisi_hha_pmu_v2_attr_groups[] = { &hisi_hha_pmu_v2_format_group, &hisi_hha_pmu_v2_events_group, - &hisi_hha_pmu_cpumask_attr_group, - &hisi_hha_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -580,7 +557,7 @@ static void __exit hisi_hha_pmu_module_exit(void) } module_exit(hisi_hha_pmu_module_exit); -MODULE_IMPORT_NS("HISI_PMU"); +MODULE_IMPORT_NS(HISI_PMU); MODULE_DESCRIPTION("HiSilicon SoC HHA uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Shaokun Zhang "); diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 16f78222e88ed..4c74eb88e8f0e 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -441,42 +441,19 @@ static const struct attribute_group hisi_l3c_pmu_v2_events_group = { .attrs = hisi_l3c_pmu_v2_events_attr, }; -static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); - -static struct attribute *hisi_l3c_pmu_cpumask_attrs[] = { - &dev_attr_cpumask.attr, - NULL, -}; - -static const struct attribute_group hisi_l3c_pmu_cpumask_attr_group = { - .attrs = hisi_l3c_pmu_cpumask_attrs, -}; - -static struct device_attribute hisi_l3c_pmu_identifier_attr = - __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); - -static struct attribute *hisi_l3c_pmu_identifier_attrs[] = { - &hisi_l3c_pmu_identifier_attr.attr, - NULL -}; - -static const struct attribute_group hisi_l3c_pmu_identifier_group = { - .attrs = hisi_l3c_pmu_identifier_attrs, -}; - static const struct attribute_group *hisi_l3c_pmu_v1_attr_groups[] = { &hisi_l3c_pmu_v1_format_group, &hisi_l3c_pmu_v1_events_group, - &hisi_l3c_pmu_cpumask_attr_group, - &hisi_l3c_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL, }; static const struct attribute_group *hisi_l3c_pmu_v2_attr_groups[] = { &hisi_l3c_pmu_v2_format_group, &hisi_l3c_pmu_v2_events_group, - &hisi_l3c_pmu_cpumask_attr_group, - &hisi_l3c_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -616,7 +593,7 @@ static void __exit hisi_l3c_pmu_module_exit(void) } module_exit(hisi_l3c_pmu_module_exit); -MODULE_IMPORT_NS("HISI_PMU"); +MODULE_IMPORT_NS(HISI_PMU); MODULE_DESCRIPTION("HiSilicon SoC L3C uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Anurup M "); diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c index 6917058808369..e91f7623cd1c0 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -357,29 +357,6 @@ static const struct attribute_group hisi_h60pa_pmu_events_group = { .attrs = hisi_h60pa_pmu_events_attr, }; -static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); - -static struct attribute *hisi_pa_pmu_cpumask_attrs[] = { - &dev_attr_cpumask.attr, - NULL -}; - -static const struct attribute_group hisi_pa_pmu_cpumask_attr_group = { - .attrs = hisi_pa_pmu_cpumask_attrs, -}; - -static struct device_attribute hisi_pa_pmu_identifier_attr = - __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); - -static struct attribute *hisi_pa_pmu_identifier_attrs[] = { - &hisi_pa_pmu_identifier_attr.attr, - NULL -}; - -static const struct attribute_group hisi_pa_pmu_identifier_group = { - .attrs = hisi_pa_pmu_identifier_attrs, -}; - static struct hisi_pa_pmu_int_regs hisi_pa_pmu_regs = { .mask_offset = PA_INT_MASK, .clear_offset = PA_INT_CLEAR, @@ -389,8 +366,8 @@ static struct hisi_pa_pmu_int_regs hisi_pa_pmu_regs = { static const struct attribute_group *hisi_pa_pmu_v2_attr_groups[] = { &hisi_pa_pmu_v2_format_group, &hisi_pa_pmu_v2_events_group, - &hisi_pa_pmu_cpumask_attr_group, - &hisi_pa_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -403,8 +380,8 @@ static const struct hisi_pmu_dev_info hisi_h32pa_v2 = { static const struct attribute_group *hisi_pa_pmu_v3_attr_groups[] = { &hisi_pa_pmu_v2_format_group, &hisi_pa_pmu_v3_events_group, - &hisi_pa_pmu_cpumask_attr_group, - &hisi_pa_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -423,8 +400,8 @@ static struct hisi_pa_pmu_int_regs hisi_h60pa_pmu_regs = { static const struct attribute_group *hisi_h60pa_pmu_attr_groups[] = { &hisi_pa_pmu_v2_format_group, &hisi_h60pa_pmu_events_group, - &hisi_pa_pmu_cpumask_attr_group, - &hisi_pa_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -571,7 +548,7 @@ static void __exit hisi_pa_pmu_module_exit(void) } module_exit(hisi_pa_pmu_module_exit); -MODULE_IMPORT_NS("HISI_PMU"); +MODULE_IMPORT_NS(HISI_PMU); MODULE_DESCRIPTION("HiSilicon Protocol Adapter uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Shaokun Zhang "); diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index 35a463177a59f..2366dccec797a 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -35,7 +35,7 @@ ssize_t hisi_format_sysfs_show(struct device *dev, return sysfs_emit(buf, "%s\n", (char *)eattr->var); } -EXPORT_SYMBOL_GPL(hisi_format_sysfs_show); +EXPORT_SYMBOL_NS_GPL(hisi_format_sysfs_show, HISI_PMU); /* * PMU event attributes @@ -49,7 +49,7 @@ ssize_t hisi_event_sysfs_show(struct device *dev, return sysfs_emit(page, "config=0x%lx\n", (unsigned long)eattr->var); } -EXPORT_SYMBOL_NS_GPL(hisi_event_sysfs_show, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_event_sysfs_show, HISI_PMU); /* * sysfs cpumask attributes. For uncore PMU, we only have a single CPU to show @@ -61,7 +61,42 @@ ssize_t hisi_cpumask_sysfs_show(struct device *dev, return sysfs_emit(buf, "%d\n", hisi_pmu->on_cpu); } -EXPORT_SYMBOL_NS_GPL(hisi_cpumask_sysfs_show, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_cpumask_sysfs_show, HISI_PMU); + +static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); + +static struct attribute *hisi_pmu_cpumask_attrs[] = { + &dev_attr_cpumask.attr, + NULL +}; + +const struct attribute_group hisi_pmu_cpumask_attr_group = { + .attrs = hisi_pmu_cpumask_attrs, +}; +EXPORT_SYMBOL_NS_GPL(hisi_pmu_cpumask_attr_group, HISI_PMU); + +ssize_t hisi_uncore_pmu_identifier_attr_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct hisi_pmu *hisi_pmu = to_hisi_pmu(dev_get_drvdata(dev)); + + return sysfs_emit(page, "0x%08x\n", hisi_pmu->identifier); +} +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_identifier_attr_show, HISI_PMU); + +static struct device_attribute hisi_pmu_identifier_attr = + __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); + +static struct attribute *hisi_pmu_identifier_attrs[] = { + &hisi_pmu_identifier_attr.attr, + NULL +}; + +const struct attribute_group hisi_pmu_identifier_group = { + .attrs = hisi_pmu_identifier_attrs, +}; +EXPORT_SYMBOL_NS_GPL(hisi_pmu_identifier_group, HISI_PMU); static bool hisi_validate_event_group(struct perf_event *event) { @@ -111,17 +146,7 @@ int hisi_uncore_pmu_get_event_idx(struct perf_event *event) return idx; } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_get_event_idx, "HISI_PMU"); - -ssize_t hisi_uncore_pmu_identifier_attr_show(struct device *dev, - struct device_attribute *attr, - char *page) -{ - struct hisi_pmu *hisi_pmu = to_hisi_pmu(dev_get_drvdata(dev)); - - return sysfs_emit(page, "0x%08x\n", hisi_pmu->identifier); -} -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_identifier_attr_show, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_get_event_idx, HISI_PMU); static void hisi_uncore_pmu_clear_event_idx(struct hisi_pmu *hisi_pmu, int idx) { @@ -180,7 +205,7 @@ int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu, return 0; } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_init_irq, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_init_irq, HISI_PMU); int hisi_uncore_pmu_event_init(struct perf_event *event) { @@ -234,7 +259,7 @@ int hisi_uncore_pmu_event_init(struct perf_event *event) return 0; } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_event_init, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_event_init, HISI_PMU); /* * Set the counter to count the event that we're interested in, @@ -288,7 +313,7 @@ void hisi_uncore_pmu_set_event_period(struct perf_event *event) /* Write start value to the hardware event counter */ hisi_pmu->ops->write_counter(hisi_pmu, hwc, val); } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_set_event_period, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_set_event_period, HISI_PMU); void hisi_uncore_pmu_event_update(struct perf_event *event) { @@ -309,7 +334,7 @@ void hisi_uncore_pmu_event_update(struct perf_event *event) HISI_MAX_PERIOD(hisi_pmu->counter_bits); local64_add(delta, &event->count); } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_event_update, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_event_update, HISI_PMU); void hisi_uncore_pmu_start(struct perf_event *event, int flags) { @@ -332,7 +357,7 @@ void hisi_uncore_pmu_start(struct perf_event *event, int flags) hisi_uncore_pmu_enable_event(event); perf_event_update_userpage(event); } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_start, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_start, HISI_PMU); void hisi_uncore_pmu_stop(struct perf_event *event, int flags) { @@ -349,7 +374,7 @@ void hisi_uncore_pmu_stop(struct perf_event *event, int flags) hisi_uncore_pmu_event_update(event); hwc->state |= PERF_HES_UPTODATE; } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_stop, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_stop, HISI_PMU); int hisi_uncore_pmu_add(struct perf_event *event, int flags) { @@ -372,7 +397,7 @@ int hisi_uncore_pmu_add(struct perf_event *event, int flags) return 0; } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_add, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_add, HISI_PMU); void hisi_uncore_pmu_del(struct perf_event *event, int flags) { @@ -384,14 +409,14 @@ void hisi_uncore_pmu_del(struct perf_event *event, int flags) perf_event_update_userpage(event); hisi_pmu->pmu_events.hw_events[hwc->idx] = NULL; } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_del, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_del, HISI_PMU); void hisi_uncore_pmu_read(struct perf_event *event) { /* Read hardware counter and update the perf counter statistics */ hisi_uncore_pmu_event_update(event); } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_read, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_read, HISI_PMU); void hisi_uncore_pmu_enable(struct pmu *pmu) { @@ -404,7 +429,7 @@ void hisi_uncore_pmu_enable(struct pmu *pmu) hisi_pmu->ops->start_counters(hisi_pmu); } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_enable, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_enable, HISI_PMU); void hisi_uncore_pmu_disable(struct pmu *pmu) { @@ -412,7 +437,7 @@ void hisi_uncore_pmu_disable(struct pmu *pmu) hisi_pmu->ops->stop_counters(hisi_pmu); } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_disable, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_disable, HISI_PMU); /* @@ -514,7 +539,7 @@ int hisi_uncore_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) return 0; } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_online_cpu, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_online_cpu, HISI_PMU); int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) { @@ -548,7 +573,7 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) return 0; } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_offline_cpu, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_offline_cpu, HISI_PMU); /* * Retrieve the topology information from the firmware for the hisi_pmu device. @@ -577,7 +602,7 @@ void hisi_uncore_pmu_init_topology(struct hisi_pmu *hisi_pmu, struct device *dev if (device_property_read_u32(dev, "hisilicon,sub-id", &topo->sub_id)) dev_dbg(dev, "no sub-id present\n"); } -EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_init_topology, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_init_topology, HISI_PMU); void hisi_pmu_init(struct hisi_pmu *hisi_pmu, struct module *module) { @@ -596,6 +621,6 @@ void hisi_pmu_init(struct hisi_pmu *hisi_pmu, struct module *module) pmu->attr_groups = hisi_pmu->pmu_events.attr_groups; pmu->capabilities = PERF_PMU_CAP_NO_EXCLUDE; } -EXPORT_SYMBOL_NS_GPL(hisi_pmu_init, "HISI_PMU"); +EXPORT_SYMBOL_NS_GPL(hisi_pmu_init, HISI_PMU); MODULE_LICENSE("GPL v2"); diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 99370ca84ce0d..c0bad72f671dd 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -137,6 +137,10 @@ struct hisi_pmu { u32 identifier; }; +/* Generic implementation of cpumask/identifier group */ +extern const struct attribute_group hisi_pmu_cpumask_attr_group; +extern const struct attribute_group hisi_pmu_identifier_group; + int hisi_uncore_pmu_get_event_idx(struct perf_event *event); void hisi_uncore_pmu_read(struct perf_event *event); int hisi_uncore_pmu_add(struct perf_event *event, int flags); diff --git a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c index ffebb188a8212..e99a5b32bd92f 100644 --- a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c @@ -344,34 +344,11 @@ static const struct attribute_group hisi_sllc_pmu_v2_events_group = { .attrs = hisi_sllc_pmu_v2_events_attr, }; -static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); - -static struct attribute *hisi_sllc_pmu_cpumask_attrs[] = { - &dev_attr_cpumask.attr, - NULL -}; - -static const struct attribute_group hisi_sllc_pmu_cpumask_attr_group = { - .attrs = hisi_sllc_pmu_cpumask_attrs, -}; - -static struct device_attribute hisi_sllc_pmu_identifier_attr = - __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); - -static struct attribute *hisi_sllc_pmu_identifier_attrs[] = { - &hisi_sllc_pmu_identifier_attr.attr, - NULL -}; - -static const struct attribute_group hisi_sllc_pmu_identifier_group = { - .attrs = hisi_sllc_pmu_identifier_attrs, -}; - static const struct attribute_group *hisi_sllc_pmu_v2_attr_groups[] = { &hisi_sllc_pmu_v2_format_group, &hisi_sllc_pmu_v2_events_group, - &hisi_sllc_pmu_cpumask_attr_group, - &hisi_sllc_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -505,7 +482,7 @@ static void __exit hisi_sllc_pmu_module_exit(void) } module_exit(hisi_sllc_pmu_module_exit); -MODULE_IMPORT_NS("HISI_PMU"); +MODULE_IMPORT_NS(HISI_PMU); MODULE_DESCRIPTION("HiSilicon SLLC uncore PMU driver"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Shaokun Zhang "); diff --git a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c index c0934eeec4bde..81046f3de38d7 100644 --- a/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_uc_pmu.c @@ -397,34 +397,11 @@ static const struct attribute_group hisi_uc_pmu_events_group = { .attrs = hisi_uc_pmu_events_attr, }; -static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); - -static struct attribute *hisi_uc_pmu_cpumask_attrs[] = { - &dev_attr_cpumask.attr, - NULL, -}; - -static const struct attribute_group hisi_uc_pmu_cpumask_attr_group = { - .attrs = hisi_uc_pmu_cpumask_attrs, -}; - -static struct device_attribute hisi_uc_pmu_identifier_attr = - __ATTR(identifier, 0444, hisi_uncore_pmu_identifier_attr_show, NULL); - -static struct attribute *hisi_uc_pmu_identifier_attrs[] = { - &hisi_uc_pmu_identifier_attr.attr, - NULL -}; - -static const struct attribute_group hisi_uc_pmu_identifier_group = { - .attrs = hisi_uc_pmu_identifier_attrs, -}; - static const struct attribute_group *hisi_uc_pmu_attr_groups[] = { &hisi_uc_pmu_format_group, &hisi_uc_pmu_events_group, - &hisi_uc_pmu_cpumask_attr_group, - &hisi_uc_pmu_identifier_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, NULL }; @@ -572,7 +549,7 @@ static void __exit hisi_uc_pmu_module_exit(void) } module_exit(hisi_uc_pmu_module_exit); -MODULE_IMPORT_NS("HISI_PMU"); +MODULE_IMPORT_NS(HISI_PMU); MODULE_DESCRIPTION("HiSilicon SoC UC uncore PMU driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Junhao He "); From f6c4ff6cf560823a1bee36e73e50741697e280af Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:13:58 +0800 Subject: [PATCH 049/227] drivers/perf: hisi: Export associated CPUs of each PMU through sysfs driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- Although the event of the uncore PMU can only be opened on a single CPU, some PMU does have the affinity on a range of CPUs. For example the L3C PMU is associated to the CPUs sharing the L3T it monitors. Users may infer this affinity by the PMU name which may have SCCL ID and CCL ID encoded (for L3C etc), but it's not that straightforward. So export this information by adding an "associated_cpus" sysfs attribute then user can get this directly. Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- Documentation/admin-guide/perf/hisi-pmu.rst | 5 ++++- drivers/perf/hisilicon/hisi_uncore_pmu.c | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/perf/hisi-pmu.rst b/Documentation/admin-guide/perf/hisi-pmu.rst index 5469793605131..6df14534b0ebf 100644 --- a/Documentation/admin-guide/perf/hisi-pmu.rst +++ b/Documentation/admin-guide/perf/hisi-pmu.rst @@ -36,7 +36,10 @@ e.g. hisi_sccl1_hha0/rx_operations is RX_OPERATIONS event of HHA index #0 in SCCL ID #1. The driver also provides a "cpumask" sysfs attribute, which shows the CPU core -ID used to count the uncore PMU event. +ID used to count the uncore PMU event. An "associated_cpus" sysfs attribute is +also provided to show the CPUs associated with this PMU. The "cpumask" indicates +the CPUs to open the events, usually as a hint for userspaces tools like perf. +It only contains one associated CPU from the "associated_cpus". Example usage of perf:: diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index 2366dccec797a..e5beddabeddd8 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -65,8 +65,18 @@ EXPORT_SYMBOL_NS_GPL(hisi_cpumask_sysfs_show, HISI_PMU); static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL); +static ssize_t hisi_associated_cpus_sysfs_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hisi_pmu *hisi_pmu = to_hisi_pmu(dev_get_drvdata(dev)); + + return cpumap_print_to_pagebuf(true, buf, &hisi_pmu->associated_cpus); +} +static DEVICE_ATTR(associated_cpus, 0444, hisi_associated_cpus_sysfs_show, NULL); + static struct attribute *hisi_pmu_cpumask_attrs[] = { &dev_attr_cpumask.attr, + &dev_attr_associated_cpus.attr, NULL }; From 30ba88429cd47919e4e3150ffc28c2c1fdba9490 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Fri, 11 Apr 2025 19:13:59 +0800 Subject: [PATCH 050/227] drivers/perf: hisi: Fix incorrect variable name "hha_pmu" in DDRC PMU driver driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- In the callback function write_evtype(), the variable name of struct hisi_pmu should be "ddrc_pmu" instead of "hha_pmu". Signed-off-by: Junhao He Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index ae990fbda7292..ff2607bc7701d 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -111,14 +111,14 @@ static void hisi_ddrc_pmu_v2_write_counter(struct hisi_pmu *ddrc_pmu, * so there is no need to write event type, while it is programmable counter in * PMU v2. */ -static void hisi_ddrc_pmu_write_evtype(struct hisi_pmu *hha_pmu, int idx, +static void hisi_ddrc_pmu_write_evtype(struct hisi_pmu *ddrc_pmu, int idx, u32 type) { u32 offset; - if (hha_pmu->identifier >= HISI_PMU_V2) { + if (ddrc_pmu->identifier >= HISI_PMU_V2) { offset = DDRC_V2_EVENT_TYPE + 4 * idx; - writel(type, hha_pmu->base + offset); + writel(type, ddrc_pmu->base + offset); } } From 7dbe8e63ad9256101ceeffe40dc0c569508df863 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Fri, 11 Apr 2025 19:14:00 +0800 Subject: [PATCH 051/227] drivers/perf: hisi: Delete redundant blank line of DDRC PMU driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- Do not add blank line at the end of a code block defined by braces. Signed-off-by: Junhao He Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index ff2607bc7701d..e6552f686a0f9 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -551,7 +551,6 @@ static void __exit hisi_ddrc_pmu_module_exit(void) { platform_driver_unregister(&hisi_ddrc_pmu_driver); cpuhp_remove_multi_state(CPUHP_AP_PERF_ARM_HISI_DDRC_ONLINE); - } module_exit(hisi_ddrc_pmu_module_exit); From 473ed788518b1e9eb4aff0d41a70e169d1291b43 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Thu, 5 Dec 2024 11:07:16 +0800 Subject: [PATCH 052/227] drivers/perf: hisi: Add support for HiSilicon DDRC v3 PMU driver driver inclusion category: featrue bugzilla: https://gitee.com/openeuler/kernel/issues/IARCNN -------------------------------- On HiSilicon HIP10C platform, the DDRC PMU is a v3 PMU. And only the offset of it's interrupt registers offset quite a different tune DDRC v2 PMUs. The control registeres and events code are the same as v2 PMU. Their modification was unexpected, which caused the interrupt handler to not handle counter overflows correctly (HiSilicon Erratum 162400501). The before DDRC PMU driver will probe v3 as v2. Therefore DDRC v3 interrupt handler cannot work properly. We fixed that by adding new ID HISI0235 to correct the DDRC v3 PMU interrupt register offset (including mask/status/clear registers). Signed-off-by: Junhao He (cherry picked from commit aa58bd1c28c317c65a4196215ef29cb85aafb922) Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 146 ++++++++++-------- 1 file changed, 85 insertions(+), 61 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index e6552f686a0f9..062a2938640a0 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -43,6 +43,11 @@ #define DDRC_V2_EVENT_TYPE 0xe74 #define DDRC_V2_PERF_CTRL 0xeA0 +/* DDRC interrupt registers definition in v3 */ +#define DDRC_V3_INT_MASK 0x534 +#define DDRC_V3_INT_STATUS 0x538 +#define DDRC_V3_INT_CLEAR 0x53C + /* DDRC has 8-counters */ #define DDRC_NR_COUNTERS 0x8 #define DDRC_V1_PERF_CTRL_EN 0x2 @@ -63,6 +68,12 @@ static const u32 ddrc_reg_off[] = { DDRC_PRE_CMD, DDRC_ACT_CMD, DDRC_RNK_CHG, DDRC_RW_CHG }; +struct hisi_ddrc_pmu_regs { + u32 int_mask; + u32 int_clear; + u32 int_status; +}; + /* * Select the counter register offset using the counter index. * In PMU v1, there are no programmable counter, the count @@ -223,77 +234,43 @@ static void hisi_ddrc_pmu_v2_disable_counter(struct hisi_pmu *ddrc_pmu, writel(val, ddrc_pmu->base + DDRC_V2_EVENT_CTRL); } -static void hisi_ddrc_pmu_v1_enable_counter_int(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc) -{ - u32 val; - - /* Write 0 to enable interrupt */ - val = readl(ddrc_pmu->base + DDRC_INT_MASK); - val &= ~(1 << hwc->idx); - writel(val, ddrc_pmu->base + DDRC_INT_MASK); -} - -static void hisi_ddrc_pmu_v1_disable_counter_int(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc) -{ - u32 val; - - /* Write 1 to mask interrupt */ - val = readl(ddrc_pmu->base + DDRC_INT_MASK); - val |= 1 << hwc->idx; - writel(val, ddrc_pmu->base + DDRC_INT_MASK); -} - -static void hisi_ddrc_pmu_v2_enable_counter_int(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc) +static void hisi_ddrc_pmu_enable_counter_int(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc) { + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; u32 val; - val = readl(ddrc_pmu->base + DDRC_V2_INT_MASK); + val = readl(ddrc_pmu->base + regs->int_mask); val &= ~(1 << hwc->idx); - writel(val, ddrc_pmu->base + DDRC_V2_INT_MASK); + writel(val, ddrc_pmu->base + regs->int_mask); } -static void hisi_ddrc_pmu_v2_disable_counter_int(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc) +static void hisi_ddrc_pmu_disable_counter_int(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc) { + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; u32 val; - val = readl(ddrc_pmu->base + DDRC_V2_INT_MASK); + val = readl(ddrc_pmu->base + regs->int_mask); val |= 1 << hwc->idx; - writel(val, ddrc_pmu->base + DDRC_V2_INT_MASK); + writel(val, ddrc_pmu->base + regs->int_mask); } -static u32 hisi_ddrc_pmu_v1_get_int_status(struct hisi_pmu *ddrc_pmu) +static u32 hisi_ddrc_pmu_get_int_status(struct hisi_pmu *ddrc_pmu) { - return readl(ddrc_pmu->base + DDRC_INT_STATUS); -} + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; -static void hisi_ddrc_pmu_v1_clear_int_status(struct hisi_pmu *ddrc_pmu, - int idx) -{ - writel(1 << idx, ddrc_pmu->base + DDRC_INT_CLEAR); + return readl(ddrc_pmu->base + regs->int_status); } -static u32 hisi_ddrc_pmu_v2_get_int_status(struct hisi_pmu *ddrc_pmu) +static void hisi_ddrc_pmu_clear_int_status(struct hisi_pmu *ddrc_pmu, + int idx) { - return readl(ddrc_pmu->base + DDRC_V2_INT_STATUS); -} + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; -static void hisi_ddrc_pmu_v2_clear_int_status(struct hisi_pmu *ddrc_pmu, - int idx) -{ - writel(1 << idx, ddrc_pmu->base + DDRC_V2_INT_CLEAR); + writel(1 << idx, ddrc_pmu->base + regs->int_clear); } -static const struct acpi_device_id hisi_ddrc_pmu_acpi_match[] = { - { "HISI0233", }, - { "HISI0234", }, - {} -}; -MODULE_DEVICE_TABLE(acpi, hisi_ddrc_pmu_acpi_match); - static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *ddrc_pmu) { @@ -314,6 +291,10 @@ static int hisi_ddrc_pmu_init_data(struct platform_device *pdev, return -EINVAL; } + ddrc_pmu->dev_info = device_get_match_data(&pdev->dev); + if (!ddrc_pmu->dev_info) + return -ENODEV; + ddrc_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ddrc_pmu->base)) { dev_err(&pdev->dev, "ioremap failed for ddrc_pmu resource\n"); @@ -403,12 +384,12 @@ static const struct hisi_uncore_ops hisi_uncore_ddrc_v1_ops = { .stop_counters = hisi_ddrc_pmu_v1_stop_counters, .enable_counter = hisi_ddrc_pmu_v1_enable_counter, .disable_counter = hisi_ddrc_pmu_v1_disable_counter, - .enable_counter_int = hisi_ddrc_pmu_v1_enable_counter_int, - .disable_counter_int = hisi_ddrc_pmu_v1_disable_counter_int, + .enable_counter_int = hisi_ddrc_pmu_enable_counter_int, + .disable_counter_int = hisi_ddrc_pmu_disable_counter_int, .write_counter = hisi_ddrc_pmu_v1_write_counter, .read_counter = hisi_ddrc_pmu_v1_read_counter, - .get_int_status = hisi_ddrc_pmu_v1_get_int_status, - .clear_int_status = hisi_ddrc_pmu_v1_clear_int_status, + .get_int_status = hisi_ddrc_pmu_get_int_status, + .clear_int_status = hisi_ddrc_pmu_clear_int_status, }; static const struct hisi_uncore_ops hisi_uncore_ddrc_v2_ops = { @@ -418,12 +399,12 @@ static const struct hisi_uncore_ops hisi_uncore_ddrc_v2_ops = { .stop_counters = hisi_ddrc_pmu_v2_stop_counters, .enable_counter = hisi_ddrc_pmu_v2_enable_counter, .disable_counter = hisi_ddrc_pmu_v2_disable_counter, - .enable_counter_int = hisi_ddrc_pmu_v2_enable_counter_int, - .disable_counter_int = hisi_ddrc_pmu_v2_disable_counter_int, + .enable_counter_int = hisi_ddrc_pmu_enable_counter_int, + .disable_counter_int = hisi_ddrc_pmu_disable_counter_int, .write_counter = hisi_ddrc_pmu_v2_write_counter, .read_counter = hisi_ddrc_pmu_v2_read_counter, - .get_int_status = hisi_ddrc_pmu_v2_get_int_status, - .clear_int_status = hisi_ddrc_pmu_v2_clear_int_status, + .get_int_status = hisi_ddrc_pmu_get_int_status, + .clear_int_status = hisi_ddrc_pmu_clear_int_status, }; static int hisi_ddrc_pmu_dev_probe(struct platform_device *pdev, @@ -442,15 +423,14 @@ static int hisi_ddrc_pmu_dev_probe(struct platform_device *pdev, if (ddrc_pmu->identifier >= HISI_PMU_V2) { ddrc_pmu->counter_bits = 48; ddrc_pmu->check_event = DDRC_V2_NR_EVENTS; - ddrc_pmu->pmu_events.attr_groups = hisi_ddrc_pmu_v2_attr_groups; ddrc_pmu->ops = &hisi_uncore_ddrc_v2_ops; } else { ddrc_pmu->counter_bits = 32; ddrc_pmu->check_event = DDRC_V1_NR_EVENTS; - ddrc_pmu->pmu_events.attr_groups = hisi_ddrc_pmu_v1_attr_groups; ddrc_pmu->ops = &hisi_uncore_ddrc_v1_ops; } + ddrc_pmu->pmu_events.attr_groups = ddrc_pmu->dev_info->attr_groups; ddrc_pmu->num_counters = DDRC_NR_COUNTERS; ddrc_pmu->dev = &pdev->dev; ddrc_pmu->on_cpu = -1; @@ -516,6 +496,50 @@ static int hisi_ddrc_pmu_remove(struct platform_device *pdev) return 0; } +static struct hisi_ddrc_pmu_regs hisi_ddrc_v1_pmu_regs = { + .int_mask = DDRC_INT_MASK, + .int_clear = DDRC_INT_CLEAR, + .int_status = DDRC_INT_STATUS, +}; + +static const struct hisi_pmu_dev_info hisi_ddrc_v1 = { + .name = "ddrc", + .attr_groups = hisi_ddrc_pmu_v1_attr_groups, + .private = &hisi_ddrc_v1_pmu_regs, +}; + +static struct hisi_ddrc_pmu_regs hisi_ddrc_v2_pmu_regs = { + .int_mask = DDRC_V2_INT_MASK, + .int_clear = DDRC_V2_INT_CLEAR, + .int_status = DDRC_V2_INT_STATUS, +}; + +static const struct hisi_pmu_dev_info hisi_ddrc_v2 = { + .name = "ddrc", + .attr_groups = hisi_ddrc_pmu_v2_attr_groups, + .private = &hisi_ddrc_v2_pmu_regs, +}; + +static struct hisi_ddrc_pmu_regs hisi_ddrc_v3_pmu_regs = { + .int_mask = DDRC_V3_INT_MASK, + .int_clear = DDRC_V3_INT_CLEAR, + .int_status = DDRC_V3_INT_STATUS, +}; + +static const struct hisi_pmu_dev_info hisi_ddrc_v3 = { + .name = "ddrc", + .attr_groups = hisi_ddrc_pmu_v2_attr_groups, + .private = &hisi_ddrc_v3_pmu_regs, +}; + +static const struct acpi_device_id hisi_ddrc_pmu_acpi_match[] = { + { "HISI0233", (kernel_ulong_t)&hisi_ddrc_v1}, + { "HISI0234", (kernel_ulong_t)&hisi_ddrc_v2}, + { "HISI0235", (kernel_ulong_t)&hisi_ddrc_v3}, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_ddrc_pmu_acpi_match); + static struct platform_driver hisi_ddrc_pmu_driver = { .driver = { .name = "hisi_ddrc_pmu", From fb9567b7f69cb2c21a6f428c6bc9ac0eb98bcec5 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Fri, 11 Apr 2025 19:14:01 +0800 Subject: [PATCH 053/227] drivers/perf: hisi: Simplify the probe process for each DDRC version driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- Version 1 and 2 of DDRC PMU also use different HID. Make use of struct acpi_device_id::driver_data for version specific information rather than judge the version register. This will help to simplify the probe process and also a bit easier for extension. In order to support this extend struct hisi_pmu_dev_info for version specific counter bits and event range. Signed-off-by: Junhao He Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 232 +++++++----------- drivers/perf/hisilicon/hisi_uncore_pmu.h | 2 + 2 files changed, 84 insertions(+), 150 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index 062a2938640a0..c51738c7f6a49 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -43,11 +43,6 @@ #define DDRC_V2_EVENT_TYPE 0xe74 #define DDRC_V2_PERF_CTRL 0xeA0 -/* DDRC interrupt registers definition in v3 */ -#define DDRC_V3_INT_MASK 0x534 -#define DDRC_V3_INT_STATUS 0x538 -#define DDRC_V3_INT_CLEAR 0x53C - /* DDRC has 8-counters */ #define DDRC_NR_COUNTERS 0x8 #define DDRC_V1_PERF_CTRL_EN 0x2 @@ -55,6 +50,10 @@ #define DDRC_V1_NR_EVENTS 0x7 #define DDRC_V2_NR_EVENTS 0x90 +#define DDRC_EVENT_CNTn(base, n) ((base) + (n) * 8) +#define DDRC_EVENT_TYPEn(base, n) ((base) + (n) * 4) +#define DDRC_UNIMPLEMENTED_REG GENMASK(31, 0) + /* * For PMU v1, there are eight-events and every event has been mapped * to fixed-purpose counters which register offset is not consistent. @@ -69,52 +68,36 @@ static const u32 ddrc_reg_off[] = { }; struct hisi_ddrc_pmu_regs { + u32 event_cnt; + u32 event_ctrl; + u32 event_type; + u32 perf_ctrl; + u32 perf_ctrl_en; u32 int_mask; u32 int_clear; u32 int_status; }; -/* - * Select the counter register offset using the counter index. - * In PMU v1, there are no programmable counter, the count - * is read form the statistics counter register itself. - */ -static u32 hisi_ddrc_pmu_v1_get_counter_offset(int cntr_idx) +static u64 hisi_ddrc_pmu_read_counter(struct hisi_pmu *ddrc_pmu, + struct hw_perf_event *hwc) { - return ddrc_reg_off[cntr_idx]; -} + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; -static u32 hisi_ddrc_pmu_v2_get_counter_offset(int cntr_idx) -{ - return DDRC_V2_EVENT_CNT + cntr_idx * 8; -} + if (regs->event_cnt == DDRC_UNIMPLEMENTED_REG) + return readl(ddrc_pmu->base + ddrc_reg_off[hwc->idx]); -static u64 hisi_ddrc_pmu_v1_read_counter(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc) -{ - return readl(ddrc_pmu->base + - hisi_ddrc_pmu_v1_get_counter_offset(hwc->idx)); + return readq(ddrc_pmu->base + DDRC_EVENT_CNTn(regs->event_cnt, hwc->idx)); } -static void hisi_ddrc_pmu_v1_write_counter(struct hisi_pmu *ddrc_pmu, +static void hisi_ddrc_pmu_write_counter(struct hisi_pmu *ddrc_pmu, struct hw_perf_event *hwc, u64 val) { - writel((u32)val, - ddrc_pmu->base + hisi_ddrc_pmu_v1_get_counter_offset(hwc->idx)); -} - -static u64 hisi_ddrc_pmu_v2_read_counter(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc) -{ - return readq(ddrc_pmu->base + - hisi_ddrc_pmu_v2_get_counter_offset(hwc->idx)); -} + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; -static void hisi_ddrc_pmu_v2_write_counter(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc, u64 val) -{ - writeq(val, - ddrc_pmu->base + hisi_ddrc_pmu_v2_get_counter_offset(hwc->idx)); + if (regs->event_cnt == DDRC_UNIMPLEMENTED_REG) + writel((u32)val, ddrc_pmu->base + ddrc_reg_off[hwc->idx]); + else + writeq(val, ddrc_pmu->base + DDRC_EVENT_CNTn(regs->event_cnt, hwc->idx)); } /* @@ -125,54 +108,12 @@ static void hisi_ddrc_pmu_v2_write_counter(struct hisi_pmu *ddrc_pmu, static void hisi_ddrc_pmu_write_evtype(struct hisi_pmu *ddrc_pmu, int idx, u32 type) { - u32 offset; - - if (ddrc_pmu->identifier >= HISI_PMU_V2) { - offset = DDRC_V2_EVENT_TYPE + 4 * idx; - writel(type, ddrc_pmu->base + offset); - } -} - -static void hisi_ddrc_pmu_v1_start_counters(struct hisi_pmu *ddrc_pmu) -{ - u32 val; - - /* Set perf_enable in DDRC_PERF_CTRL to start event counting */ - val = readl(ddrc_pmu->base + DDRC_PERF_CTRL); - val |= DDRC_V1_PERF_CTRL_EN; - writel(val, ddrc_pmu->base + DDRC_PERF_CTRL); -} - -static void hisi_ddrc_pmu_v1_stop_counters(struct hisi_pmu *ddrc_pmu) -{ - u32 val; - - /* Clear perf_enable in DDRC_PERF_CTRL to stop event counting */ - val = readl(ddrc_pmu->base + DDRC_PERF_CTRL); - val &= ~DDRC_V1_PERF_CTRL_EN; - writel(val, ddrc_pmu->base + DDRC_PERF_CTRL); -} - -static void hisi_ddrc_pmu_v1_enable_counter(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc) -{ - u32 val; + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; - /* Set counter index(event code) in DDRC_EVENT_CTRL register */ - val = readl(ddrc_pmu->base + DDRC_EVENT_CTRL); - val |= (1 << GET_DDRC_EVENTID(hwc)); - writel(val, ddrc_pmu->base + DDRC_EVENT_CTRL); -} + if (regs->event_type == DDRC_UNIMPLEMENTED_REG) + return; -static void hisi_ddrc_pmu_v1_disable_counter(struct hisi_pmu *ddrc_pmu, - struct hw_perf_event *hwc) -{ - u32 val; - - /* Clear counter index(event code) in DDRC_EVENT_CTRL register */ - val = readl(ddrc_pmu->base + DDRC_EVENT_CTRL); - val &= ~(1 << GET_DDRC_EVENTID(hwc)); - writel(val, ddrc_pmu->base + DDRC_EVENT_CTRL); + writel(type, ddrc_pmu->base + DDRC_EVENT_TYPEn(regs->event_type, idx)); } static int hisi_ddrc_pmu_v1_get_event_idx(struct perf_event *event) @@ -191,47 +132,58 @@ static int hisi_ddrc_pmu_v1_get_event_idx(struct perf_event *event) return idx; } -static int hisi_ddrc_pmu_v2_get_event_idx(struct perf_event *event) +static int hisi_ddrc_pmu_get_event_idx(struct perf_event *event) { + struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu); + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; + + if (regs->event_type == DDRC_UNIMPLEMENTED_REG) + return hisi_ddrc_pmu_v1_get_event_idx(event); + return hisi_uncore_pmu_get_event_idx(event); } -static void hisi_ddrc_pmu_v2_start_counters(struct hisi_pmu *ddrc_pmu) +static void hisi_ddrc_pmu_start_counters(struct hisi_pmu *ddrc_pmu) { + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; u32 val; - val = readl(ddrc_pmu->base + DDRC_V2_PERF_CTRL); - val |= DDRC_V2_PERF_CTRL_EN; - writel(val, ddrc_pmu->base + DDRC_V2_PERF_CTRL); + val = readl(ddrc_pmu->base + regs->perf_ctrl); + val |= regs->perf_ctrl_en; + writel(val, ddrc_pmu->base + regs->perf_ctrl); } -static void hisi_ddrc_pmu_v2_stop_counters(struct hisi_pmu *ddrc_pmu) +static void hisi_ddrc_pmu_stop_counters(struct hisi_pmu *ddrc_pmu) { + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; u32 val; - val = readl(ddrc_pmu->base + DDRC_V2_PERF_CTRL); - val &= ~DDRC_V2_PERF_CTRL_EN; - writel(val, ddrc_pmu->base + DDRC_V2_PERF_CTRL); + val = readl(ddrc_pmu->base + regs->perf_ctrl); + val &= ~regs->perf_ctrl_en; + writel(val, ddrc_pmu->base + regs->perf_ctrl); } -static void hisi_ddrc_pmu_v2_enable_counter(struct hisi_pmu *ddrc_pmu, +static void hisi_ddrc_pmu_enable_counter(struct hisi_pmu *ddrc_pmu, struct hw_perf_event *hwc) { + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; u32 val; - val = readl(ddrc_pmu->base + DDRC_V2_EVENT_CTRL); - val |= 1 << hwc->idx; - writel(val, ddrc_pmu->base + DDRC_V2_EVENT_CTRL); + val = readl(ddrc_pmu->base + regs->event_ctrl); + val |= BIT_ULL(hwc->idx); + writel(val, ddrc_pmu->base + regs->event_ctrl); } -static void hisi_ddrc_pmu_v2_disable_counter(struct hisi_pmu *ddrc_pmu, +static void hisi_ddrc_pmu_disable_counter(struct hisi_pmu *ddrc_pmu, struct hw_perf_event *hwc) { + struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; u32 val; - val = readl(ddrc_pmu->base + DDRC_V2_EVENT_CTRL); - val &= ~(1 << hwc->idx); - writel(val, ddrc_pmu->base + DDRC_V2_EVENT_CTRL); + + val = readl(ddrc_pmu->base + regs->event_ctrl); + val &= ~BIT_ULL(hwc->idx); + writel(val, ddrc_pmu->base + regs->event_ctrl); } static void hisi_ddrc_pmu_enable_counter_int(struct hisi_pmu *ddrc_pmu, @@ -241,7 +193,7 @@ static void hisi_ddrc_pmu_enable_counter_int(struct hisi_pmu *ddrc_pmu, u32 val; val = readl(ddrc_pmu->base + regs->int_mask); - val &= ~(1 << hwc->idx); + val &= ~BIT_ULL(hwc->idx); writel(val, ddrc_pmu->base + regs->int_mask); } @@ -251,8 +203,9 @@ static void hisi_ddrc_pmu_disable_counter_int(struct hisi_pmu *ddrc_pmu, struct hisi_ddrc_pmu_regs *regs = ddrc_pmu->dev_info->private; u32 val; + val = readl(ddrc_pmu->base + regs->int_mask); - val |= 1 << hwc->idx; + val |= BIT_ULL(hwc->idx); writel(val, ddrc_pmu->base + regs->int_mask); } @@ -377,32 +330,17 @@ static const struct attribute_group *hisi_ddrc_pmu_v2_attr_groups[] = { NULL }; -static const struct hisi_uncore_ops hisi_uncore_ddrc_v1_ops = { - .write_evtype = hisi_ddrc_pmu_write_evtype, - .get_event_idx = hisi_ddrc_pmu_v1_get_event_idx, - .start_counters = hisi_ddrc_pmu_v1_start_counters, - .stop_counters = hisi_ddrc_pmu_v1_stop_counters, - .enable_counter = hisi_ddrc_pmu_v1_enable_counter, - .disable_counter = hisi_ddrc_pmu_v1_disable_counter, - .enable_counter_int = hisi_ddrc_pmu_enable_counter_int, - .disable_counter_int = hisi_ddrc_pmu_disable_counter_int, - .write_counter = hisi_ddrc_pmu_v1_write_counter, - .read_counter = hisi_ddrc_pmu_v1_read_counter, - .get_int_status = hisi_ddrc_pmu_get_int_status, - .clear_int_status = hisi_ddrc_pmu_clear_int_status, -}; - -static const struct hisi_uncore_ops hisi_uncore_ddrc_v2_ops = { +static const struct hisi_uncore_ops hisi_uncore_ddrc_ops = { .write_evtype = hisi_ddrc_pmu_write_evtype, - .get_event_idx = hisi_ddrc_pmu_v2_get_event_idx, - .start_counters = hisi_ddrc_pmu_v2_start_counters, - .stop_counters = hisi_ddrc_pmu_v2_stop_counters, - .enable_counter = hisi_ddrc_pmu_v2_enable_counter, - .disable_counter = hisi_ddrc_pmu_v2_disable_counter, + .get_event_idx = hisi_ddrc_pmu_get_event_idx, + .start_counters = hisi_ddrc_pmu_start_counters, + .stop_counters = hisi_ddrc_pmu_stop_counters, + .enable_counter = hisi_ddrc_pmu_enable_counter, + .disable_counter = hisi_ddrc_pmu_disable_counter, .enable_counter_int = hisi_ddrc_pmu_enable_counter_int, .disable_counter_int = hisi_ddrc_pmu_disable_counter_int, - .write_counter = hisi_ddrc_pmu_v2_write_counter, - .read_counter = hisi_ddrc_pmu_v2_read_counter, + .write_counter = hisi_ddrc_pmu_write_counter, + .read_counter = hisi_ddrc_pmu_read_counter, .get_int_status = hisi_ddrc_pmu_get_int_status, .clear_int_status = hisi_ddrc_pmu_clear_int_status, }; @@ -420,15 +358,10 @@ static int hisi_ddrc_pmu_dev_probe(struct platform_device *pdev, if (ret) return ret; - if (ddrc_pmu->identifier >= HISI_PMU_V2) { - ddrc_pmu->counter_bits = 48; - ddrc_pmu->check_event = DDRC_V2_NR_EVENTS; - ddrc_pmu->ops = &hisi_uncore_ddrc_v2_ops; - } else { - ddrc_pmu->counter_bits = 32; - ddrc_pmu->check_event = DDRC_V1_NR_EVENTS; - ddrc_pmu->ops = &hisi_uncore_ddrc_v1_ops; - } + ddrc_pmu->pmu_events.attr_groups = ddrc_pmu->dev_info->attr_groups; + ddrc_pmu->counter_bits = ddrc_pmu->dev_info->counter_bits; + ddrc_pmu->check_event = ddrc_pmu->dev_info->check_event; + ddrc_pmu->ops = &hisi_uncore_ddrc_ops; ddrc_pmu->pmu_events.attr_groups = ddrc_pmu->dev_info->attr_groups; ddrc_pmu->num_counters = DDRC_NR_COUNTERS; @@ -497,45 +430,44 @@ static int hisi_ddrc_pmu_remove(struct platform_device *pdev) } static struct hisi_ddrc_pmu_regs hisi_ddrc_v1_pmu_regs = { + .event_cnt = DDRC_UNIMPLEMENTED_REG, + .event_ctrl = DDRC_EVENT_CTRL, + .event_type = DDRC_UNIMPLEMENTED_REG, + .perf_ctrl = DDRC_PERF_CTRL, + .perf_ctrl_en = DDRC_V1_PERF_CTRL_EN, .int_mask = DDRC_INT_MASK, .int_clear = DDRC_INT_CLEAR, .int_status = DDRC_INT_STATUS, }; static const struct hisi_pmu_dev_info hisi_ddrc_v1 = { - .name = "ddrc", + .counter_bits = 32, + .check_event = DDRC_V1_NR_EVENTS, .attr_groups = hisi_ddrc_pmu_v1_attr_groups, .private = &hisi_ddrc_v1_pmu_regs, }; static struct hisi_ddrc_pmu_regs hisi_ddrc_v2_pmu_regs = { + .event_cnt = DDRC_V2_EVENT_CNT, + .event_ctrl = DDRC_V2_EVENT_CTRL, + .event_type = DDRC_V2_EVENT_TYPE, + .perf_ctrl = DDRC_V2_PERF_CTRL, + .perf_ctrl_en = DDRC_V2_PERF_CTRL_EN, .int_mask = DDRC_V2_INT_MASK, .int_clear = DDRC_V2_INT_CLEAR, .int_status = DDRC_V2_INT_STATUS, }; static const struct hisi_pmu_dev_info hisi_ddrc_v2 = { - .name = "ddrc", + .counter_bits = 48, + .check_event = DDRC_V2_NR_EVENTS, .attr_groups = hisi_ddrc_pmu_v2_attr_groups, .private = &hisi_ddrc_v2_pmu_regs, }; -static struct hisi_ddrc_pmu_regs hisi_ddrc_v3_pmu_regs = { - .int_mask = DDRC_V3_INT_MASK, - .int_clear = DDRC_V3_INT_CLEAR, - .int_status = DDRC_V3_INT_STATUS, -}; - -static const struct hisi_pmu_dev_info hisi_ddrc_v3 = { - .name = "ddrc", - .attr_groups = hisi_ddrc_pmu_v2_attr_groups, - .private = &hisi_ddrc_v3_pmu_regs, -}; - static const struct acpi_device_id hisi_ddrc_pmu_acpi_match[] = { { "HISI0233", (kernel_ulong_t)&hisi_ddrc_v1}, { "HISI0234", (kernel_ulong_t)&hisi_ddrc_v2}, - { "HISI0235", (kernel_ulong_t)&hisi_ddrc_v3}, {} }; MODULE_DEVICE_TABLE(acpi, hisi_ddrc_pmu_acpi_match); diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index c0bad72f671dd..42d6473b1223c 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -72,6 +72,8 @@ struct hisi_uncore_ops { struct hisi_pmu_dev_info { const char *name; const struct attribute_group **attr_groups; + u32 counter_bits; + u32 check_event; void *private; }; From f95cf8988a698b02138e3b1e3492e45bc307dec0 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Fri, 11 Apr 2025 19:14:02 +0800 Subject: [PATCH 054/227] drivers/perf: hisi: Add support for HiSilicon DDRC v3 PMU driver driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- HiSilicon DDRC v3 PMU has the different interrupt register offset compared to the v2. Add device information of v3 PMU with ACPI HID HISI0235. Signed-off-by: Junhao He Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index c51738c7f6a49..ccf8aa399de82 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -43,6 +43,11 @@ #define DDRC_V2_EVENT_TYPE 0xe74 #define DDRC_V2_PERF_CTRL 0xeA0 +/* DDRC interrupt registers definition in v3 */ +#define DDRC_V3_INT_MASK 0x534 +#define DDRC_V3_INT_STATUS 0x538 +#define DDRC_V3_INT_CLEAR 0x53C + /* DDRC has 8-counters */ #define DDRC_NR_COUNTERS 0x8 #define DDRC_V1_PERF_CTRL_EN 0x2 @@ -465,9 +470,28 @@ static const struct hisi_pmu_dev_info hisi_ddrc_v2 = { .private = &hisi_ddrc_v2_pmu_regs, }; +static struct hisi_ddrc_pmu_regs hisi_ddrc_v3_pmu_regs = { + .event_cnt = DDRC_V2_EVENT_CNT, + .event_ctrl = DDRC_V2_EVENT_CTRL, + .event_type = DDRC_V2_EVENT_TYPE, + .perf_ctrl = DDRC_V2_PERF_CTRL, + .perf_ctrl_en = DDRC_V2_PERF_CTRL_EN, + .int_mask = DDRC_V3_INT_MASK, + .int_clear = DDRC_V3_INT_CLEAR, + .int_status = DDRC_V3_INT_STATUS, +}; + +static const struct hisi_pmu_dev_info hisi_ddrc_v3 = { + .counter_bits = 48, + .check_event = DDRC_V2_NR_EVENTS, + .attr_groups = hisi_ddrc_pmu_v2_attr_groups, + .private = &hisi_ddrc_v3_pmu_regs, +}; + static const struct acpi_device_id hisi_ddrc_pmu_acpi_match[] = { { "HISI0233", (kernel_ulong_t)&hisi_ddrc_v1}, { "HISI0234", (kernel_ulong_t)&hisi_ddrc_v2}, + { "HISI0235", (kernel_ulong_t)&hisi_ddrc_v3 }, {} }; MODULE_DEVICE_TABLE(acpi, hisi_ddrc_pmu_acpi_match); From 3d001fa22e31bc5228c2fde0bd3583e02406e7c6 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Fri, 11 Apr 2025 19:14:03 +0800 Subject: [PATCH 055/227] drivers/perf: hisi: Use ACPI driver_data to retrieve SLLC PMU information driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- Make use of struct acpi_device_id::driver_data for version specific information rather than judge the version register. This will help to simplify the probe process and also a bit easier for extension. Factor out SLLC register definition to struct hisi_sllc_pmu_regs. No functional changes intended. Signed-off-by: Junhao He Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c | 178 ++++++++++++------ 1 file changed, 118 insertions(+), 60 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c index e99a5b32bd92f..dd17db7adc0e5 100644 --- a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c @@ -41,6 +41,7 @@ #define SLLC_SRCID_CMD_SHIFT 1 #define SLLC_SRCID_MSK_SHIFT 12 #define SLLC_NR_EVENTS 0x80 +#define SLLC_EVENT_CNTn(cnt0, n) ((cnt0) + (n) * 8) HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_min, config1, 10, 0); HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_max, config1, 21, 11); @@ -48,6 +49,23 @@ HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 32, 22); HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 43, 33); HISI_PMU_EVENT_ATTR_EXTRACTOR(tracetag_en, config1, 44, 44); +struct hisi_sllc_pmu_regs { + u32 int_mask; + u32 int_clear; + u32 int_status; + u32 perf_ctrl; + u32 srcid_ctrl; + u32 srcid_cmd_shift; + u32 srcid_mask_shift; + u32 tgtid_ctrl; + u32 tgtid_min_shift; + u32 tgtid_max_shift; + u32 event_ctrl; + u32 event_type0; + u32 version; + u32 event_cnt0; +}; + static bool tgtid_is_valid(u32 max, u32 min) { return max > 0 && max >= min; @@ -56,96 +74,104 @@ static bool tgtid_is_valid(u32 max, u32 min) static void hisi_sllc_pmu_enable_tracetag(struct perf_event *event) { struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 tt_en = hisi_get_tracetag_en(event); if (tt_en) { u32 val; - val = readl(sllc_pmu->base + SLLC_PERF_CTRL); + val = readl(sllc_pmu->base + regs->perf_ctrl); val |= SLLC_TRACETAG_EN | SLLC_FILT_EN; - writel(val, sllc_pmu->base + SLLC_PERF_CTRL); + writel(val, sllc_pmu->base + regs->perf_ctrl); } } static void hisi_sllc_pmu_disable_tracetag(struct perf_event *event) { struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 tt_en = hisi_get_tracetag_en(event); if (tt_en) { u32 val; - val = readl(sllc_pmu->base + SLLC_PERF_CTRL); + val = readl(sllc_pmu->base + regs->perf_ctrl); val &= ~(SLLC_TRACETAG_EN | SLLC_FILT_EN); - writel(val, sllc_pmu->base + SLLC_PERF_CTRL); + writel(val, sllc_pmu->base + regs->perf_ctrl); } } static void hisi_sllc_pmu_config_tgtid(struct perf_event *event) { struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 min = hisi_get_tgtid_min(event); u32 max = hisi_get_tgtid_max(event); if (tgtid_is_valid(max, min)) { - u32 val = (max << SLLC_TGTID_MAX_SHIFT) | (min << SLLC_TGTID_MIN_SHIFT); + u32 val = (max << regs->tgtid_max_shift) | + (min << regs->tgtid_min_shift); - writel(val, sllc_pmu->base + SLLC_TGTID_CTRL); + writel(val, sllc_pmu->base + regs->tgtid_ctrl); /* Enable the tgtid */ - val = readl(sllc_pmu->base + SLLC_PERF_CTRL); + val = readl(sllc_pmu->base + regs->perf_ctrl); val |= SLLC_TGTID_EN | SLLC_FILT_EN; - writel(val, sllc_pmu->base + SLLC_PERF_CTRL); + writel(val, sllc_pmu->base + regs->perf_ctrl); } } static void hisi_sllc_pmu_clear_tgtid(struct perf_event *event) { struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 min = hisi_get_tgtid_min(event); u32 max = hisi_get_tgtid_max(event); if (tgtid_is_valid(max, min)) { u32 val; - writel(SLLC_TGTID_NONE, sllc_pmu->base + SLLC_TGTID_CTRL); + writel(SLLC_TGTID_NONE, sllc_pmu->base + regs->tgtid_ctrl); /* Disable the tgtid */ - val = readl(sllc_pmu->base + SLLC_PERF_CTRL); + val = readl(sllc_pmu->base + regs->perf_ctrl); val &= ~(SLLC_TGTID_EN | SLLC_FILT_EN); - writel(val, sllc_pmu->base + SLLC_PERF_CTRL); + writel(val, sllc_pmu->base + regs->perf_ctrl); } } static void hisi_sllc_pmu_config_srcid(struct perf_event *event) { struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 cmd = hisi_get_srcid_cmd(event); if (cmd) { u32 val, msk; msk = hisi_get_srcid_msk(event); - val = (cmd << SLLC_SRCID_CMD_SHIFT) | (msk << SLLC_SRCID_MSK_SHIFT); - writel(val, sllc_pmu->base + SLLC_SRCID_CTRL); + val = (cmd << regs->srcid_cmd_shift) | + (msk << regs->srcid_mask_shift); + writel(val, sllc_pmu->base + regs->srcid_ctrl); /* Enable the srcid */ - val = readl(sllc_pmu->base + SLLC_PERF_CTRL); + val = readl(sllc_pmu->base + regs->perf_ctrl); val |= SLLC_SRCID_EN | SLLC_FILT_EN; - writel(val, sllc_pmu->base + SLLC_PERF_CTRL); + writel(val, sllc_pmu->base + regs->perf_ctrl); } } static void hisi_sllc_pmu_clear_srcid(struct perf_event *event) { struct hisi_pmu *sllc_pmu = to_hisi_pmu(event->pmu); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 cmd = hisi_get_srcid_cmd(event); if (cmd) { u32 val; - writel(SLLC_SRCID_NONE, sllc_pmu->base + SLLC_SRCID_CTRL); + writel(SLLC_SRCID_NONE, sllc_pmu->base + regs->srcid_ctrl); /* Disable the srcid */ - val = readl(sllc_pmu->base + SLLC_PERF_CTRL); + val = readl(sllc_pmu->base + regs->perf_ctrl); val &= ~(SLLC_SRCID_EN | SLLC_FILT_EN); - writel(val, sllc_pmu->base + SLLC_PERF_CTRL); + writel(val, sllc_pmu->base + regs->perf_ctrl); } } @@ -167,29 +193,27 @@ static void hisi_sllc_pmu_clear_filter(struct perf_event *event) } } -static u32 hisi_sllc_pmu_get_counter_offset(int idx) -{ - return (SLLC_EVENT_CNT0_L + idx * 8); -} - static u64 hisi_sllc_pmu_read_counter(struct hisi_pmu *sllc_pmu, struct hw_perf_event *hwc) { - return readq(sllc_pmu->base + - hisi_sllc_pmu_get_counter_offset(hwc->idx)); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; + + return readq(sllc_pmu->base + SLLC_EVENT_CNTn(regs->event_cnt0, hwc->idx)); } static void hisi_sllc_pmu_write_counter(struct hisi_pmu *sllc_pmu, struct hw_perf_event *hwc, u64 val) { - writeq(val, sllc_pmu->base + - hisi_sllc_pmu_get_counter_offset(hwc->idx)); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; + + writeq(val, sllc_pmu->base + SLLC_EVENT_CNTn(regs->event_cnt0, hwc->idx)); } static void hisi_sllc_pmu_write_evtype(struct hisi_pmu *sllc_pmu, int idx, u32 type) { - u32 reg, reg_idx, shift, val; + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; + u32 reg, val; /* * Select the appropriate event select register(SLLC_EVENT_TYPE0/1). @@ -198,96 +222,98 @@ static void hisi_sllc_pmu_write_evtype(struct hisi_pmu *sllc_pmu, int idx, * SLLC_EVENT_TYPE0 is chosen. For the latter 4 hardware counters, * SLLC_EVENT_TYPE1 is chosen. */ - reg = SLLC_EVENT_TYPE0 + (idx / 4) * 4; - reg_idx = idx % 4; - shift = 8 * reg_idx; + reg = regs->event_type0 + (idx / 4) * 4; /* Write event code to SLLC_EVENT_TYPEx Register */ val = readl(sllc_pmu->base + reg); - val &= ~(SLLC_EVTYPE_MASK << shift); - val |= (type << shift); + val &= ~(SLLC_EVTYPE_MASK << HISI_PMU_EVTYPE_SHIFT(idx)); + val |= (type << HISI_PMU_EVTYPE_SHIFT(idx)); writel(val, sllc_pmu->base + reg); } static void hisi_sllc_pmu_start_counters(struct hisi_pmu *sllc_pmu) { + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 val; - val = readl(sllc_pmu->base + SLLC_PERF_CTRL); + val = readl(sllc_pmu->base + regs->perf_ctrl); val |= SLLC_PERF_CTRL_EN; - writel(val, sllc_pmu->base + SLLC_PERF_CTRL); + writel(val, sllc_pmu->base + regs->perf_ctrl); } static void hisi_sllc_pmu_stop_counters(struct hisi_pmu *sllc_pmu) { + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 val; - val = readl(sllc_pmu->base + SLLC_PERF_CTRL); + val = readl(sllc_pmu->base + regs->perf_ctrl); val &= ~(SLLC_PERF_CTRL_EN); - writel(val, sllc_pmu->base + SLLC_PERF_CTRL); + writel(val, sllc_pmu->base + regs->perf_ctrl); } static void hisi_sllc_pmu_enable_counter(struct hisi_pmu *sllc_pmu, struct hw_perf_event *hwc) { + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 val; - val = readl(sllc_pmu->base + SLLC_EVENT_CTRL); - val |= 1 << hwc->idx; - writel(val, sllc_pmu->base + SLLC_EVENT_CTRL); + val = readl(sllc_pmu->base + regs->event_ctrl); + val |= BIT_ULL(hwc->idx); + writel(val, sllc_pmu->base + regs->event_ctrl); } static void hisi_sllc_pmu_disable_counter(struct hisi_pmu *sllc_pmu, struct hw_perf_event *hwc) { + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 val; - val = readl(sllc_pmu->base + SLLC_EVENT_CTRL); - val &= ~(1 << hwc->idx); - writel(val, sllc_pmu->base + SLLC_EVENT_CTRL); + val = readl(sllc_pmu->base + regs->event_ctrl); + val &= ~BIT_ULL(hwc->idx); + writel(val, sllc_pmu->base + regs->event_ctrl); } static void hisi_sllc_pmu_enable_counter_int(struct hisi_pmu *sllc_pmu, struct hw_perf_event *hwc) { + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 val; - val = readl(sllc_pmu->base + SLLC_INT_MASK); - /* Write 0 to enable interrupt */ - val &= ~(1 << hwc->idx); - writel(val, sllc_pmu->base + SLLC_INT_MASK); + val = readl(sllc_pmu->base + regs->int_mask); + val &= ~BIT_ULL(hwc->idx); + writel(val, sllc_pmu->base + regs->int_mask); } static void hisi_sllc_pmu_disable_counter_int(struct hisi_pmu *sllc_pmu, struct hw_perf_event *hwc) { + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; u32 val; - val = readl(sllc_pmu->base + SLLC_INT_MASK); - /* Write 1 to mask interrupt */ - val |= 1 << hwc->idx; - writel(val, sllc_pmu->base + SLLC_INT_MASK); + val = readl(sllc_pmu->base + regs->int_mask); + val |= BIT_ULL(hwc->idx); + writel(val, sllc_pmu->base + regs->int_mask); } static u32 hisi_sllc_pmu_get_int_status(struct hisi_pmu *sllc_pmu) { - return readl(sllc_pmu->base + SLLC_INT_STATUS); + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; + + return readl(sllc_pmu->base + regs->int_status); } static void hisi_sllc_pmu_clear_int_status(struct hisi_pmu *sllc_pmu, int idx) { - writel(1 << idx, sllc_pmu->base + SLLC_INT_CLEAR); -} + struct hisi_sllc_pmu_regs *regs = sllc_pmu->dev_info->private; -static const struct acpi_device_id hisi_sllc_pmu_acpi_match[] = { - { "HISI0263", }, - {} -}; -MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match); + writel(BIT_ULL(idx), sllc_pmu->base + regs->int_clear); +} static int hisi_sllc_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *sllc_pmu) { + struct hisi_sllc_pmu_regs *regs; + hisi_uncore_pmu_init_topology(sllc_pmu, &pdev->dev); /* @@ -304,13 +330,18 @@ static int hisi_sllc_pmu_init_data(struct platform_device *pdev, return -EINVAL; } + sllc_pmu->dev_info = device_get_match_data(&pdev->dev); + if (!sllc_pmu->dev_info) + return -ENODEV; + sllc_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(sllc_pmu->base)) { dev_err(&pdev->dev, "ioremap failed for sllc_pmu resource.\n"); return PTR_ERR(sllc_pmu->base); } - sllc_pmu->identifier = readl(sllc_pmu->base + SLLC_VERSION); + regs = sllc_pmu->dev_info->private; + sllc_pmu->identifier = readl(sllc_pmu->base + regs->version); return 0; } @@ -352,6 +383,27 @@ static const struct attribute_group *hisi_sllc_pmu_v2_attr_groups[] = { NULL }; +static struct hisi_sllc_pmu_regs hisi_sllc_v2_pmu_regs = { + .int_mask = SLLC_INT_MASK, + .int_clear = SLLC_INT_CLEAR, + .int_status = SLLC_INT_STATUS, + .perf_ctrl = SLLC_PERF_CTRL, + .srcid_ctrl = SLLC_SRCID_CTRL, + .srcid_cmd_shift = SLLC_SRCID_CMD_SHIFT, + .srcid_mask_shift = SLLC_SRCID_MSK_SHIFT, + .tgtid_ctrl = SLLC_TGTID_CTRL, + .tgtid_min_shift = SLLC_TGTID_MIN_SHIFT, + .tgtid_max_shift = SLLC_TGTID_MAX_SHIFT, + .event_ctrl = SLLC_EVENT_CTRL, + .event_type0 = SLLC_EVENT_TYPE0, + .version = SLLC_VERSION, + .event_cnt0 = SLLC_EVENT_CNT0_L, +}; + +static const struct hisi_pmu_dev_info hisi_sllc_v2 = { + .private = &hisi_sllc_v2_pmu_regs, +}; + static const struct hisi_uncore_ops hisi_uncore_sllc_ops = { .write_evtype = hisi_sllc_pmu_write_evtype, .get_event_idx = hisi_uncore_pmu_get_event_idx, @@ -444,6 +496,12 @@ static int hisi_sllc_pmu_remove(struct platform_device *pdev) return 0; } +static const struct acpi_device_id hisi_sllc_pmu_acpi_match[] = { + { "HISI0263", (kernel_ulong_t)&hisi_sllc_v2 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match); + static struct platform_driver hisi_sllc_pmu_driver = { .driver = { .name = "hisi_sllc_pmu", From c4a268e34e9c822a88b7db252b6d0a8a4a43a667 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Fri, 11 Apr 2025 19:14:04 +0800 Subject: [PATCH 056/227] drivers/perf: hisi: Add support for HiSilicon SLLC v3 PMU driver driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- SLLC v3 PMU has the following changes compared to previous version: a) update the register layout b) update the definition of SRCID_CTRL and TGTID_CTRL registers. To be compatible with v2, we use maximum width (11 bits) and mask the extra length for themselves. c) remove latency events (driver does not need to be adapted). SLLC v3 PMU is identified with HID HISI0264. Signed-off-by: Junhao He Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c index dd17db7adc0e5..ca0e422adca7d 100644 --- a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c @@ -28,6 +28,18 @@ #define SLLC_VERSION 0x1cf0 #define SLLC_EVENT_CNT0_L 0x1d00 +/* SLLC registers definition in v3 */ +#define SLLC_V3_INT_MASK 0x6834 +#define SLLC_V3_INT_STATUS 0x6838 +#define SLLC_V3_INT_CLEAR 0x683c +#define SLLC_V3_VERSION 0x6c00 +#define SLLC_V3_PERF_CTRL 0x6d00 +#define SLLC_V3_SRCID_CTRL 0x6d04 +#define SLLC_V3_TGTID_CTRL 0x6d08 +#define SLLC_V3_EVENT_CTRL 0x6d14 +#define SLLC_V3_EVENT_TYPE0 0x6d18 +#define SLLC_V3_EVENT_CNT0_L 0x6e00 + #define SLLC_EVTYPE_MASK 0xff #define SLLC_PERF_CTRL_EN BIT(0) #define SLLC_FILT_EN BIT(1) @@ -40,6 +52,12 @@ #define SLLC_TGTID_MAX_SHIFT 12 #define SLLC_SRCID_CMD_SHIFT 1 #define SLLC_SRCID_MSK_SHIFT 12 + +#define SLLC_V3_TGTID_MIN_SHIFT 1 +#define SLLC_V3_TGTID_MAX_SHIFT 10 +#define SLLC_V3_SRCID_CMD_SHIFT 1 +#define SLLC_V3_SRCID_MSK_SHIFT 10 + #define SLLC_NR_EVENTS 0x80 #define SLLC_EVENT_CNTn(cnt0, n) ((cnt0) + (n) * 8) @@ -404,6 +422,27 @@ static const struct hisi_pmu_dev_info hisi_sllc_v2 = { .private = &hisi_sllc_v2_pmu_regs, }; +static struct hisi_sllc_pmu_regs hisi_sllc_v3_pmu_regs = { + .int_mask = SLLC_V3_INT_MASK, + .int_clear = SLLC_V3_INT_CLEAR, + .int_status = SLLC_V3_INT_STATUS, + .perf_ctrl = SLLC_V3_PERF_CTRL, + .srcid_ctrl = SLLC_V3_SRCID_CTRL, + .srcid_cmd_shift = SLLC_V3_SRCID_CMD_SHIFT, + .srcid_mask_shift = SLLC_V3_SRCID_MSK_SHIFT, + .tgtid_ctrl = SLLC_V3_TGTID_CTRL, + .tgtid_min_shift = SLLC_V3_TGTID_MIN_SHIFT, + .tgtid_max_shift = SLLC_V3_TGTID_MAX_SHIFT, + .event_ctrl = SLLC_V3_EVENT_CTRL, + .event_type0 = SLLC_V3_EVENT_TYPE0, + .version = SLLC_V3_VERSION, + .event_cnt0 = SLLC_V3_EVENT_CNT0_L, +}; + +static const struct hisi_pmu_dev_info hisi_sllc_v3 = { + .private = &hisi_sllc_v3_pmu_regs, +}; + static const struct hisi_uncore_ops hisi_uncore_sllc_ops = { .write_evtype = hisi_sllc_pmu_write_evtype, .get_event_idx = hisi_uncore_pmu_get_event_idx, @@ -498,6 +537,7 @@ static int hisi_sllc_pmu_remove(struct platform_device *pdev) static const struct acpi_device_id hisi_sllc_pmu_acpi_match[] = { { "HISI0263", (kernel_ulong_t)&hisi_sllc_v2 }, + { "HISI0264", (kernel_ulong_t)&hisi_sllc_v3 }, {} }; MODULE_DEVICE_TABLE(acpi, hisi_sllc_pmu_acpi_match); From c74eaecbb1bb05eb024c323ce3ead1d224b10172 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Fri, 11 Apr 2025 19:14:05 +0800 Subject: [PATCH 057/227] drivers/perf: hisi: Relax the event number check of v2 PMUs driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- The supported event number range of each Uncore PMUs is provided by each driver in hisi_pmu::check_event and out of range events will be rejected. A later version with expanded event number range needs to register the PMU with updated hisi_pmu::check_event even if it's the only update, which means the expanded events cannot be used unless the driver's updated. However the unsupported events won't be counted by the hardware so we can relax the event number check to allow the use the expanded events. Signed-off-by: Junhao He Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c | 2 +- drivers/perf/hisilicon/hisi_uncore_hha_pmu.c | 6 +++--- drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 2 +- drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c index ccf8aa399de82..b22775f6f727d 100644 --- a/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_ddrc_pmu.c @@ -53,7 +53,7 @@ #define DDRC_V1_PERF_CTRL_EN 0x2 #define DDRC_V2_PERF_CTRL_EN 0x1 #define DDRC_V1_NR_EVENTS 0x7 -#define DDRC_V2_NR_EVENTS 0x90 +#define DDRC_V2_NR_EVENTS 0xFF #define DDRC_EVENT_CNTn(base, n) ((base) + (n) * 8) #define DDRC_EVENT_TYPEn(base, n) ((base) + (n) * 4) diff --git a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c index cb928b450b97b..a0a467cc2f49e 100644 --- a/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_hha_pmu.c @@ -47,9 +47,9 @@ #define HHA_SRCID_CMD GENMASK(16, 6) #define HHA_SRCID_MSK GENMASK(30, 20) #define HHA_DATSRC_SKT_EN BIT(23) -#define HHA_EVTYPE_NONE 0xff +#define HHA_EVTYPE_MASK GENMASK(7, 0) #define HHA_V1_NR_EVENT 0x65 -#define HHA_V2_NR_EVENT 0xCE +#define HHA_V2_NR_EVENT 0xFF HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_cmd, config1, 10, 0); HISI_PMU_EVENT_ATTR_EXTRACTOR(srcid_msk, config1, 21, 11); @@ -197,7 +197,7 @@ static void hisi_hha_pmu_write_evtype(struct hisi_pmu *hha_pmu, int idx, /* Write event code to HHA_EVENT_TYPEx register */ val = readl(hha_pmu->base + reg); - val &= ~(HHA_EVTYPE_NONE << shift); + val &= ~(HHA_EVTYPE_MASK << shift); val |= (type << shift); writel(val, hha_pmu->base + reg); } diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c index e91f7623cd1c0..7c95d116b79fb 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -444,7 +444,7 @@ static int hisi_pa_pmu_dev_probe(struct platform_device *pdev, pa_pmu->pmu_events.attr_groups = pa_pmu->dev_info->attr_groups; pa_pmu->num_counters = PA_NR_COUNTERS; pa_pmu->ops = &hisi_uncore_pa_ops; - pa_pmu->check_event = 0xB0; + pa_pmu->check_event = PA_EVTYPE_MASK; pa_pmu->counter_bits = 64; pa_pmu->dev = &pdev->dev; pa_pmu->on_cpu = -1; diff --git a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c index ca0e422adca7d..020bea8c6c7bb 100644 --- a/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_sllc_pmu.c @@ -58,7 +58,7 @@ #define SLLC_V3_SRCID_CMD_SHIFT 1 #define SLLC_V3_SRCID_MSK_SHIFT 10 -#define SLLC_NR_EVENTS 0x80 +#define SLLC_NR_EVENTS 0xff #define SLLC_EVENT_CNTn(cnt0, n) ((cnt0) + (n) * 8) HISI_PMU_EVENT_ATTR_EXTRACTOR(tgtid_min, config1, 10, 0); From 2f8d2e54ee52c76e8489a2419f30577eec67c75a Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:14:06 +0800 Subject: [PATCH 058/227] drivers/perf: hisi: Support PMUs with no interrupt driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- We'll have PMUs don't have an interrupt to indicate the counter overflow, but the Uncore PMU core assume all the PMUs have interrupt. So handle this case in the core. The existing PMUs won't be affected. Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pmu.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index e5beddabeddd8..f6fe04e8f4d62 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -530,7 +530,9 @@ int hisi_uncore_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) return 0; hisi_pmu->on_cpu = cpumask_local_spread(0, dev_to_node(hisi_pmu->dev)); - WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(cpu))); + if (hisi_pmu->irq > 0) + WARN_ON(irq_set_affinity(hisi_pmu->irq, + cpumask_of(hisi_pmu->on_cpu))); return 0; } @@ -545,7 +547,8 @@ int hisi_uncore_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) hisi_pmu->on_cpu = cpu; /* Overflow interrupt also should use the same CPU */ - WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(cpu))); + if (hisi_pmu->irq > 0) + WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(cpu))); return 0; } @@ -579,7 +582,9 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) perf_pmu_migrate_context(&hisi_pmu->pmu, cpu, target); /* Use this CPU for event counting */ hisi_pmu->on_cpu = target; - WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(target))); + + if (hisi_pmu->irq > 0) + WARN_ON(irq_set_affinity(hisi_pmu->irq, cpumask_of(target))); return 0; } From 562a0b11611c2f6269525da87957f170469c42d9 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 11 Apr 2025 19:14:07 +0800 Subject: [PATCH 059/227] drivers/perf: hisi: Add support for HiSilicon NoC PMU driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- Adds the support for HiSilicon NoC (Network on Chip) PMU which will be used to monitor the events on the system bus. The PMU device will be named after the SCL ID (either Super CPU cluster or Super IO cluster) and the index ID, just similar to other HiSilicon Uncore PMUs. Below PMU formats are provided besides the event: - ch: the transaction channel (data, request, response, etc) which can be used to filter the counting. - tt_en: tracetag filtering enable. Just as other HiSilicon Uncore PMUs the NoC PMU supports only counting the transactions with tracetag. The NoC PMU doesn't have an interrupt to indicate the overflow. However we have a 64 bit counter which is large enough and it's nearly impossible to overflow. Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- Documentation/admin-guide/perf/hisi-pmu.rst | 11 + drivers/perf/hisilicon/Makefile | 3 +- drivers/perf/hisilicon/hisi_uncore_noc_pmu.c | 392 +++++++++++++++++++ 3 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 drivers/perf/hisilicon/hisi_uncore_noc_pmu.c diff --git a/Documentation/admin-guide/perf/hisi-pmu.rst b/Documentation/admin-guide/perf/hisi-pmu.rst index 6df14534b0ebf..85514c8eed14f 100644 --- a/Documentation/admin-guide/perf/hisi-pmu.rst +++ b/Documentation/admin-guide/perf/hisi-pmu.rst @@ -101,6 +101,17 @@ CCL/ICL-ID. For I/O die, the ICL-ID is followed by: 5'b00011: HAC_ICL; 5'b10000: PCIe_ICL; +6. ch: NoC PMU supports filtering the event counts of certain transaction +channel with this option. The current supported channels are as follows: + +- 3'b010: Request channel +- 3'b100: Snoop channel +- 3'b110: Response channel +- 3'b111: Data channel + +7. tt_en: NoC PMU supports counting only transactions that have tracetag set +if this option is set. See the 2nd list for more information about tracetag. + Users could configure IDs to count data come from specific CCL/ICL, by setting srcid_cmd & srcid_msk, and data desitined for specific CCL/ICL by setting tgtid_cmd & tgtid_msk. A set bit in srcid_msk/tgtid_msk means the PMU will not diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index 48dcc8381ea75..dcec8f39719d0 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -1,7 +1,8 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o \ hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o hisi_uncore_sllc_pmu.o \ - hisi_uncore_pa_pmu.o hisi_uncore_cpa_pmu.o hisi_uncore_uc_pmu.o + hisi_uncore_pa_pmu.o hisi_uncore_cpa_pmu.o hisi_uncore_uc_pmu.o \ + hisi_uncore_noc_pmu.o obj-$(CONFIG_HISI_PCIE_PMU) += hisi_pcie_pmu.o obj-$(CONFIG_HNS3_PMU) += hns3_pmu.o diff --git a/drivers/perf/hisilicon/hisi_uncore_noc_pmu.c b/drivers/perf/hisilicon/hisi_uncore_noc_pmu.c new file mode 100644 index 0000000000000..bf0a6bce71c43 --- /dev/null +++ b/drivers/perf/hisilicon/hisi_uncore_noc_pmu.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for HiSilicon Uncore NoC (Network on Chip) PMU device + * + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. + * Author: Yicong Yang + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hisi_uncore_pmu.h" + +#define NOC_PMU_VERSION 0x1e00 +#define NOC_PMU_GLOBAL_CTRL 0x1e04 +#define NOC_PMU_GLOBAL_CTRL_PMU_EN BIT(0) +#define NOC_PMU_GLOBAL_CTRL_TT_EN BIT(1) +#define NOC_PMU_CNT_INFO 0x1e08 +#define NOC_PMU_CNT_INFO_OVERFLOW(n) BIT(n) +#define NOC_PMU_EVENT_CTRL(n) (0x1e20 + 4 * (n)) +#define NOC_PMU_EVENT_CTRL_TYPE GENMASK(4, 0) +/* + * Note channel of 0x0 will reset the counter value, so don't do it before + * we read out the counter. + */ +#define NOC_PMU_EVENT_CTRL_CHANNEL GENMASK(10, 8) +#define NOC_PMU_EVENT_CTRL_EN BIT(11) +#define NOC_PMU_EVENT_COUNTER(n) (0x1e80 + 8 * (n)) + +#define NOC_PMU_NR_COUNTERS 4 +#define NOC_PMU_COUNTER_BITS 64 + +#define NOC_PMU_CH_DEFAULT 0x7 + +HISI_PMU_EVENT_ATTR_EXTRACTOR(ch, config1, 2, 0); +HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_en, config1, 3, 3); + +/* Dynamic CPU hotplug state used by this PMU driver */ +static enum cpuhp_state hisi_noc_pmu_cpuhp_state; + +/* + * Tracetag filtering is not per event and all the events should keep + * the consistence. Return true if the new comer doesn't match the + * tracetag filtering configuration of the current scheduled events. + */ +static bool hisi_noc_pmu_check_global_filter(struct perf_event *curr, + struct perf_event *new) +{ + return hisi_get_tt_en(curr) == hisi_get_tt_en(new); +} + +static void hisi_noc_pmu_write_evtype(struct hisi_pmu *noc_pmu, int idx, u32 type) +{ + u32 reg; + + reg = readl(noc_pmu->base + NOC_PMU_EVENT_CTRL(idx)); + reg &= ~NOC_PMU_EVENT_CTRL_TYPE; + reg |= FIELD_PREP(NOC_PMU_EVENT_CTRL_TYPE, type); + writel(reg, noc_pmu->base + NOC_PMU_EVENT_CTRL(idx)); +} + +static int hisi_noc_pmu_get_event_idx(struct perf_event *event) +{ + struct hisi_pmu *noc_pmu = to_hisi_pmu(event->pmu); + struct hisi_pmu_hwevents *pmu_events = &noc_pmu->pmu_events; + int cur_idx; + + cur_idx = find_first_bit(pmu_events->used_mask, noc_pmu->num_counters); + if (cur_idx != noc_pmu->num_counters && + !hisi_noc_pmu_check_global_filter(pmu_events->hw_events[cur_idx], event)) + return -EAGAIN; + + return hisi_uncore_pmu_get_event_idx(event); +} + +static u64 hisi_noc_pmu_read_counter(struct hisi_pmu *noc_pmu, + struct hw_perf_event *hwc) +{ + return readq(noc_pmu->base + NOC_PMU_EVENT_COUNTER(hwc->idx)); +} + +static void hisi_noc_pmu_write_counter(struct hisi_pmu *noc_pmu, + struct hw_perf_event *hwc, u64 val) +{ + writeq(val, noc_pmu->base + NOC_PMU_EVENT_COUNTER(hwc->idx)); +} + +static void hisi_noc_pmu_enable_counter(struct hisi_pmu *noc_pmu, + struct hw_perf_event *hwc) +{ + u32 reg; + + reg = readl(noc_pmu->base + NOC_PMU_EVENT_CTRL(hwc->idx)); + reg |= NOC_PMU_EVENT_CTRL_EN; + writel(reg, noc_pmu->base + NOC_PMU_EVENT_CTRL(hwc->idx)); +} + +static void hisi_noc_pmu_disable_counter(struct hisi_pmu *noc_pmu, + struct hw_perf_event *hwc) +{ + u32 reg; + + reg = readl(noc_pmu->base + NOC_PMU_EVENT_CTRL(hwc->idx)); + reg &= ~NOC_PMU_EVENT_CTRL_EN; + writel(reg, noc_pmu->base + NOC_PMU_EVENT_CTRL(hwc->idx)); +} + +static void hisi_noc_pmu_enable_counter_int(struct hisi_pmu *noc_pmu, + struct hw_perf_event *hwc) +{ + /* We don't support interrupt, so a stub here. */ +} + +static void hisi_noc_pmu_disable_counter_int(struct hisi_pmu *noc_pmu, + struct hw_perf_event *hwc) +{ +} + +static void hisi_noc_pmu_start_counters(struct hisi_pmu *noc_pmu) +{ + u32 reg; + + reg = readl(noc_pmu->base + NOC_PMU_GLOBAL_CTRL); + reg |= NOC_PMU_GLOBAL_CTRL_PMU_EN; + writel(reg, noc_pmu->base + NOC_PMU_GLOBAL_CTRL); +} + +static void hisi_noc_pmu_stop_counters(struct hisi_pmu *noc_pmu) +{ + u32 reg; + + reg = readl(noc_pmu->base + NOC_PMU_GLOBAL_CTRL); + reg &= ~NOC_PMU_GLOBAL_CTRL_PMU_EN; + writel(reg, noc_pmu->base + NOC_PMU_GLOBAL_CTRL); +} + +static u32 hisi_noc_pmu_get_int_status(struct hisi_pmu *noc_pmu) +{ + return readl(noc_pmu->base + NOC_PMU_CNT_INFO); +} + +static void hisi_noc_pmu_clear_int_status(struct hisi_pmu *noc_pmu, int idx) +{ + writel(~NOC_PMU_CNT_INFO_OVERFLOW(idx), noc_pmu->base + NOC_PMU_CNT_INFO); +} + +static void hisi_noc_pmu_enable_filter(struct perf_event *event) +{ + struct hisi_pmu *noc_pmu = to_hisi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; + u32 tt_en = hisi_get_tt_en(event); + u32 ch = hisi_get_ch(event); + u32 reg; + + if (!ch) + ch = NOC_PMU_CH_DEFAULT; + + reg = readl(noc_pmu->base + NOC_PMU_EVENT_CTRL(hwc->idx)); + reg &= ~NOC_PMU_EVENT_CTRL_CHANNEL; + reg |= FIELD_PREP(NOC_PMU_EVENT_CTRL_CHANNEL, ch); + writel(reg, noc_pmu->base + NOC_PMU_EVENT_CTRL(hwc->idx)); + + /* + * Since tracetag filter applies to all the counters, don't touch it + * if user doesn't specify it explicitly. + */ + if (tt_en) { + reg = readl(noc_pmu->base + NOC_PMU_GLOBAL_CTRL); + reg |= NOC_PMU_GLOBAL_CTRL_TT_EN; + writel(reg, noc_pmu->base + NOC_PMU_GLOBAL_CTRL); + } +} + +static void hisi_noc_pmu_disable_filter(struct perf_event *event) +{ + struct hisi_pmu *noc_pmu = to_hisi_pmu(event->pmu); + u32 tt_en = hisi_get_tt_en(event); + u32 reg; + + /* + * If we're not the last counter, don't touch the global tracetag + * configuration. + */ + if (bitmap_weight(noc_pmu->pmu_events.used_mask, noc_pmu->num_counters) > 1) + return; + + if (tt_en) { + reg = readl(noc_pmu->base + NOC_PMU_GLOBAL_CTRL); + reg &= ~NOC_PMU_GLOBAL_CTRL_TT_EN; + writel(reg, noc_pmu->base + NOC_PMU_GLOBAL_CTRL); + } +} + +static const struct hisi_uncore_ops hisi_uncore_noc_ops = { + .write_evtype = hisi_noc_pmu_write_evtype, + .get_event_idx = hisi_noc_pmu_get_event_idx, + .read_counter = hisi_noc_pmu_read_counter, + .write_counter = hisi_noc_pmu_write_counter, + .enable_counter = hisi_noc_pmu_enable_counter, + .disable_counter = hisi_noc_pmu_disable_counter, + .enable_counter_int = hisi_noc_pmu_enable_counter_int, + .disable_counter_int = hisi_noc_pmu_disable_counter_int, + .start_counters = hisi_noc_pmu_start_counters, + .stop_counters = hisi_noc_pmu_stop_counters, + .get_int_status = hisi_noc_pmu_get_int_status, + .clear_int_status = hisi_noc_pmu_clear_int_status, + .enable_filter = hisi_noc_pmu_enable_filter, + .disable_filter = hisi_noc_pmu_disable_filter, +}; + +static struct attribute *hisi_noc_pmu_format_attrs[] = { + HISI_PMU_FORMAT_ATTR(event, "config:0-7"), + HISI_PMU_FORMAT_ATTR(ch, "config1:0-2"), + HISI_PMU_FORMAT_ATTR(tt_en, "config1:3"), + NULL +}; + +static const struct attribute_group hisi_noc_pmu_format_group = { + .name = "format", + .attrs = hisi_noc_pmu_format_attrs, +}; + +static struct attribute *hisi_noc_pmu_events_attrs[] = { + HISI_PMU_EVENT_ATTR(cycles, 0x0e), + /* Flux on/off the ring */ + HISI_PMU_EVENT_ATTR(ingress_flow_sum, 0x1a), + HISI_PMU_EVENT_ATTR(egress_flow_sum, 0x17), + /* Buffer full duration on/off the ring */ + HISI_PMU_EVENT_ATTR(ingress_buf_full, 0x19), + HISI_PMU_EVENT_ATTR(egress_buf_full, 0x12), + /* Failure packets count on/off the ring */ + HISI_PMU_EVENT_ATTR(cw_ingress_fail, 0x01), + HISI_PMU_EVENT_ATTR(cc_ingress_fail, 0x09), + HISI_PMU_EVENT_ATTR(cw_egress_fail, 0x03), + HISI_PMU_EVENT_ATTR(cc_egress_fail, 0x0b), + /* Flux of the ring */ + HISI_PMU_EVENT_ATTR(cw_main_flow_sum, 0x05), + HISI_PMU_EVENT_ATTR(cc_main_flow_sum, 0x0d), + NULL +}; + +static const struct attribute_group hisi_noc_pmu_events_group = { + .name = "events", + .attrs = hisi_noc_pmu_events_attrs, +}; + +static const struct attribute_group *hisi_noc_pmu_attr_groups[] = { + &hisi_noc_pmu_format_group, + &hisi_noc_pmu_events_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, + NULL +}; + +static int hisi_noc_pmu_dev_init(struct platform_device *pdev, struct hisi_pmu *noc_pmu) +{ + hisi_uncore_pmu_init_topology(noc_pmu, &pdev->dev); + + if (noc_pmu->topo.scl_id < 0) + return dev_err_probe(&pdev->dev, -EINVAL, "failed to get scl-id\n"); + + if (noc_pmu->topo.index_id < 0) + return dev_err_probe(&pdev->dev, -EINVAL, "failed to get idx-id\n"); + + if (noc_pmu->topo.sub_id < 0) + return dev_err_probe(&pdev->dev, -EINVAL, "failed to get sub-id\n"); + + noc_pmu->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(noc_pmu->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(noc_pmu->base), + "fail to remap io memory\n"); + + noc_pmu->on_cpu = -1; + noc_pmu->dev = &pdev->dev; + noc_pmu->ops = &hisi_uncore_noc_ops; + noc_pmu->pmu_events.attr_groups = hisi_noc_pmu_attr_groups; + noc_pmu->num_counters = NOC_PMU_NR_COUNTERS; + noc_pmu->counter_bits = NOC_PMU_COUNTER_BITS; + noc_pmu->check_event = NOC_PMU_EVENT_CTRL_TYPE; + noc_pmu->identifier = readl(noc_pmu->base + NOC_PMU_VERSION); + return 0; +} + +static void hisi_noc_pmu_remove_cpuhp_instance(void *hotplug_node) +{ + cpuhp_state_remove_instance_nocalls(hisi_noc_pmu_cpuhp_state, hotplug_node); +} + +static void hisi_noc_pmu_unregister_pmu(void *pmu) +{ + perf_pmu_unregister(pmu); +} + +static int hisi_noc_pmu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hisi_pmu *noc_pmu; + char *name; + int ret; + + noc_pmu = devm_kzalloc(dev, sizeof(*noc_pmu), GFP_KERNEL); + if (!noc_pmu) + return -ENOMEM; + + /* + * HiSilicon Uncore PMU framework needs to get common hisi_pmu device + * from device's drvdata. + */ + platform_set_drvdata(pdev, noc_pmu); + + ret = hisi_noc_pmu_dev_init(pdev, noc_pmu); + if (ret) + return ret; + + ret = cpuhp_state_add_instance(hisi_noc_pmu_cpuhp_state, &noc_pmu->node); + if (ret) + return dev_err_probe(dev, ret, "Fail to register cpuhp instance\n"); + + ret = devm_add_action_or_reset(dev, hisi_noc_pmu_remove_cpuhp_instance, + &noc_pmu->node); + if (ret) + return ret; + + hisi_pmu_init(noc_pmu, THIS_MODULE); + + name = devm_kasprintf(dev, GFP_KERNEL, "hisi_scl%d_noc%d_%d", + noc_pmu->topo.scl_id, noc_pmu->topo.index_id, + noc_pmu->topo.sub_id); + if (!name) + return -ENOMEM; + + ret = perf_pmu_register(&noc_pmu->pmu, name, -1); + if (ret) + return dev_err_probe(dev, ret, "Fail to register PMU\n"); + + return devm_add_action_or_reset(dev, hisi_noc_pmu_unregister_pmu, + &noc_pmu->pmu); +} + +const struct acpi_device_id hisi_noc_pmu_ids[] = { + { "HISI04E0", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_noc_pmu_ids); + +static struct platform_driver hisi_noc_pmu_driver = { + .driver = { + .name = "hisi_noc_pmu", + .acpi_match_table = hisi_noc_pmu_ids, + .suppress_bind_attrs = true, + }, + .probe = hisi_noc_pmu_probe, +}; + +static int __init hisi_noc_pmu_module_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "perf/hisi/noc:online", + hisi_uncore_pmu_online_cpu, + hisi_uncore_pmu_offline_cpu); + if (ret < 0) { + pr_err("hisi_noc_pmu: Fail to setup cpuhp callbacks, ret = %d\n", ret); + return ret; + } + hisi_noc_pmu_cpuhp_state = ret; + + ret = platform_driver_register(&hisi_noc_pmu_driver); + if (ret) + cpuhp_remove_multi_state(hisi_noc_pmu_cpuhp_state); + + return ret; +} +module_init(hisi_noc_pmu_module_init); + +static void __exit hisi_noc_pmu_module_exit(void) +{ + platform_driver_unregister(&hisi_noc_pmu_driver); + cpuhp_remove_multi_state(hisi_noc_pmu_cpuhp_state); +} +module_exit(hisi_noc_pmu_module_exit); + +MODULE_IMPORT_NS(HISI_PMU); +MODULE_DESCRIPTION("HiSilicon SoC Uncore NoC PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yicong Yang "); From 6c4557afced69f89185a57895f205c64db8a79ea Mon Sep 17 00:00:00 2001 From: Junhao He Date: Fri, 11 Apr 2025 19:14:08 +0800 Subject: [PATCH 060/227] drivers/perf: hisi: Add support for HiSilicon MN PMU driver driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC0GTV ---------------------------------------------------------------------- MN (Miscellaneous Node) is a hybrid node in ARM CHI. It broadcasts the following two types of requests: DVM operations and PCIe configuration. MN PMU devices exist on both SCCL and SICL, so we named the MN pmu driver after SCL (Super cluster) ID. The MN PMU driver using the HiSilicon uncore PMU framework. And only the event parameter is supported. Signed-off-by: Junhao He Reviewed-by: Jonathan Cameron Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/Makefile | 2 +- drivers/perf/hisilicon/hisi_uncore_mn_pmu.c | 355 ++++++++++++++++++++ 2 files changed, 356 insertions(+), 1 deletion(-) create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn_pmu.c diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile index dcec8f39719d0..186be3d02238b 100644 --- a/drivers/perf/hisilicon/Makefile +++ b/drivers/perf/hisilicon/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c_pmu.o \ hisi_uncore_hha_pmu.o hisi_uncore_ddrc_pmu.o hisi_uncore_sllc_pmu.o \ hisi_uncore_pa_pmu.o hisi_uncore_cpa_pmu.o hisi_uncore_uc_pmu.o \ - hisi_uncore_noc_pmu.o + hisi_uncore_noc_pmu.o hisi_uncore_mn_pmu.o obj-$(CONFIG_HISI_PCIE_PMU) += hisi_pcie_pmu.o obj-$(CONFIG_HNS3_PMU) += hns3_pmu.o diff --git a/drivers/perf/hisilicon/hisi_uncore_mn_pmu.c b/drivers/perf/hisilicon/hisi_uncore_mn_pmu.c new file mode 100644 index 0000000000000..bad06cac3f8a3 --- /dev/null +++ b/drivers/perf/hisilicon/hisi_uncore_mn_pmu.c @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * HiSilicon SoC MN uncore Hardware event counters support + * + * Copyright (c) 2025 HiSilicon Technologies Co., Ltd. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "hisi_uncore_pmu.h" + +/* Dynamic CPU hotplug state used by MN PMU */ +static enum cpuhp_state hisi_mn_pmu_online; + +/* MN register definition */ +#define HISI_MN_DYNAMIC_CTRL 0x400 +#define HISI_MN_DYNAMIC_CTRL_EN BIT(0) +#define HISI_MN_PERF_CTRL_REG 0x408 +#define HISI_MN_PERF_CTRL_EN BIT(6) +#define HISI_MN_INT_MASK_REG 0x800 +#define HISI_MN_INT_STATUS_REG 0x808 +#define HISI_MN_INT_CLEAR_REG 0x80C +#define HISI_MN_EVENT_CTRL_REG 0x1C00 +#define HISI_MN_VERSION_REG 0x1C04 +#define HISI_MN_EVTYPE_REGn(n) (0x1d00 + (n) * 4) +#define HISI_MN_EVTYPE_MASK GENMASK(7, 0) +#define HISI_MN_CNTR_REGn(n) (0x1e00 + (n) * 8) + +#define HISI_MN_NR_COUNTERS 4 +#define HISI_MN_COUNTER_BITS 48 +#define HISI_MN_TIMEOUT_US 500U + +/* + * Each event request takes a certain amount of time to complete. If + * we counting the latency related event, we need to wait for the all + * requests complete. Otherwise, the value of counter is slightly larger. + */ +static void hisi_mn_pmu_counter_flush(struct hisi_pmu *mn_pmu) +{ + int ret; + u32 val; + + val = readl(mn_pmu->base + HISI_MN_DYNAMIC_CTRL); + val |= HISI_MN_DYNAMIC_CTRL_EN; + writel(val, mn_pmu->base + HISI_MN_DYNAMIC_CTRL); + + ret = readl_poll_timeout_atomic(mn_pmu->base + HISI_MN_DYNAMIC_CTRL, + val, !(val & HISI_MN_DYNAMIC_CTRL_EN), + 1, HISI_MN_TIMEOUT_US); + if (ret) + dev_warn(mn_pmu->dev, "Counter flush timeout\n"); +} + +static u64 hisi_mn_pmu_read_counter(struct hisi_pmu *mn_pmu, + struct hw_perf_event *hwc) +{ + return readq(mn_pmu->base + HISI_MN_CNTR_REGn(hwc->idx)); +} + +static void hisi_mn_pmu_write_counter(struct hisi_pmu *mn_pmu, + struct hw_perf_event *hwc, u64 val) +{ + writeq(val, mn_pmu->base + HISI_MN_CNTR_REGn(hwc->idx)); +} + +static void hisi_mn_pmu_write_evtype(struct hisi_pmu *mn_pmu, int idx, u32 type) +{ + u32 val; + + /* + * Select the appropriate event select register. + * There are 2 32-bit event select registers for the + * 8 hardware counters, each event code is 8-bit wide. + */ + val = readl(mn_pmu->base + HISI_MN_EVTYPE_REGn(idx / 4)); + val &= ~(HISI_MN_EVTYPE_MASK << HISI_PMU_EVTYPE_SHIFT(idx)); + val |= (type << HISI_PMU_EVTYPE_SHIFT(idx)); + writel(val, mn_pmu->base + HISI_MN_EVTYPE_REGn(idx / 4)); +} + +static void hisi_mn_pmu_start_counters(struct hisi_pmu *mn_pmu) +{ + u32 val; + + val = readl(mn_pmu->base + HISI_MN_PERF_CTRL_REG); + val |= HISI_MN_PERF_CTRL_EN; + writel(val, mn_pmu->base + HISI_MN_PERF_CTRL_REG); +} + +static void hisi_mn_pmu_stop_counters(struct hisi_pmu *mn_pmu) +{ + u32 val; + + val = readl(mn_pmu->base + HISI_MN_PERF_CTRL_REG); + val &= ~HISI_MN_PERF_CTRL_EN; + writel(val, mn_pmu->base + HISI_MN_PERF_CTRL_REG); + + hisi_mn_pmu_counter_flush(mn_pmu); +} + +static void hisi_mn_pmu_enable_counter(struct hisi_pmu *mn_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + val = readl(mn_pmu->base + HISI_MN_EVENT_CTRL_REG); + val |= BIT(hwc->idx); + writel(val, mn_pmu->base + HISI_MN_EVENT_CTRL_REG); +} + +static void hisi_mn_pmu_disable_counter(struct hisi_pmu *mn_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + val = readl(mn_pmu->base + HISI_MN_EVENT_CTRL_REG); + val &= ~BIT(hwc->idx); + writel(val, mn_pmu->base + HISI_MN_EVENT_CTRL_REG); +} + +static void hisi_mn_pmu_enable_counter_int(struct hisi_pmu *mn_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + val = readl(mn_pmu->base + HISI_MN_INT_MASK_REG); + val &= ~BIT(hwc->idx); + writel(val, mn_pmu->base + HISI_MN_INT_MASK_REG); +} + +static void hisi_mn_pmu_disable_counter_int(struct hisi_pmu *mn_pmu, + struct hw_perf_event *hwc) +{ + u32 val; + + val = readl(mn_pmu->base + HISI_MN_INT_MASK_REG); + val |= BIT(hwc->idx); + writel(val, mn_pmu->base + HISI_MN_INT_MASK_REG); +} + +static u32 hisi_mn_pmu_get_int_status(struct hisi_pmu *mn_pmu) +{ + return readl(mn_pmu->base + HISI_MN_INT_STATUS_REG); +} + +static void hisi_mn_pmu_clear_int_status(struct hisi_pmu *mn_pmu, int idx) +{ + writel(BIT(idx), mn_pmu->base + HISI_MN_INT_CLEAR_REG); +} + +static struct attribute *hisi_mn_pmu_format_attr[] = { + HISI_PMU_FORMAT_ATTR(event, "config:0-7"), + NULL +}; + +static const struct attribute_group hisi_mn_pmu_format_group = { + .name = "format", + .attrs = hisi_mn_pmu_format_attr, +}; + +static struct attribute *hisi_mn_pmu_events_attr[] = { + HISI_PMU_EVENT_ATTR(req_eobarrier_num, 0x00), + HISI_PMU_EVENT_ATTR(req_ecbarrier_num, 0x01), + HISI_PMU_EVENT_ATTR(req_dvmop_num, 0x02), + HISI_PMU_EVENT_ATTR(req_dvmsync_num, 0x03), + HISI_PMU_EVENT_ATTR(req_retry_num, 0x04), + HISI_PMU_EVENT_ATTR(req_writenosnp_num, 0x05), + HISI_PMU_EVENT_ATTR(req_readnosnp_num, 0x06), + HISI_PMU_EVENT_ATTR(snp_dvm_num, 0x07), + HISI_PMU_EVENT_ATTR(snp_dvmsync_num, 0x08), + HISI_PMU_EVENT_ATTR(l3t_req_dvm_num, 0x09), + HISI_PMU_EVENT_ATTR(l3t_req_dvmsync_num, 0x0A), + HISI_PMU_EVENT_ATTR(mn_req_dvm_num, 0x0B), + HISI_PMU_EVENT_ATTR(mn_req_dvmsync_num, 0x0C), + HISI_PMU_EVENT_ATTR(pa_req_dvm_num, 0x0D), + HISI_PMU_EVENT_ATTR(pa_req_dvmsync_num, 0x0E), + HISI_PMU_EVENT_ATTR(snp_dvm_latency, 0x80), + HISI_PMU_EVENT_ATTR(snp_dvmsync_latency, 0x81), + HISI_PMU_EVENT_ATTR(l3t_req_dvm_latency, 0x82), + HISI_PMU_EVENT_ATTR(l3t_req_dvmsync_latency, 0x83), + HISI_PMU_EVENT_ATTR(mn_req_dvm_latency, 0x84), + HISI_PMU_EVENT_ATTR(mn_req_dvmsync_latency, 0x85), + HISI_PMU_EVENT_ATTR(pa_req_dvm_latency, 0x86), + HISI_PMU_EVENT_ATTR(pa_req_dvmsync_latency, 0x87), + NULL +}; + +static const struct attribute_group hisi_mn_pmu_events_group = { + .name = "events", + .attrs = hisi_mn_pmu_events_attr, +}; + +static const struct attribute_group *hisi_mn_pmu_attr_groups[] = { + &hisi_mn_pmu_format_group, + &hisi_mn_pmu_events_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, + NULL +}; + +static const struct hisi_uncore_ops hisi_uncore_mn_ops = { + .write_evtype = hisi_mn_pmu_write_evtype, + .get_event_idx = hisi_uncore_pmu_get_event_idx, + .start_counters = hisi_mn_pmu_start_counters, + .stop_counters = hisi_mn_pmu_stop_counters, + .enable_counter = hisi_mn_pmu_enable_counter, + .disable_counter = hisi_mn_pmu_disable_counter, + .enable_counter_int = hisi_mn_pmu_enable_counter_int, + .disable_counter_int = hisi_mn_pmu_disable_counter_int, + .write_counter = hisi_mn_pmu_write_counter, + .read_counter = hisi_mn_pmu_read_counter, + .get_int_status = hisi_mn_pmu_get_int_status, + .clear_int_status = hisi_mn_pmu_clear_int_status, +}; + +static int hisi_mn_pmu_dev_init(struct platform_device *pdev, + struct hisi_pmu *mn_pmu) +{ + int ret; + + hisi_uncore_pmu_init_topology(mn_pmu, &pdev->dev); + + if (mn_pmu->topo.scl_id < 0) + return dev_err_probe(&pdev->dev, -EINVAL, + "Failed to read MN scl id\n"); + + if (mn_pmu->topo.index_id < 0) + return dev_err_probe(&pdev->dev, -EINVAL, + "Failed to read MN index id\n"); + + mn_pmu->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mn_pmu->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(mn_pmu->base), + "Failed to ioremap resource\n"); + + ret = hisi_uncore_pmu_init_irq(mn_pmu, pdev); + if (ret) + return ret; + + mn_pmu->on_cpu = -1; + mn_pmu->dev = &pdev->dev; + mn_pmu->ops = &hisi_uncore_mn_ops; + mn_pmu->pmu_events.attr_groups = hisi_mn_pmu_attr_groups; + mn_pmu->check_event = HISI_MN_EVTYPE_MASK; + mn_pmu->num_counters = HISI_MN_NR_COUNTERS; + mn_pmu->counter_bits = HISI_MN_COUNTER_BITS; + mn_pmu->identifier = readl(mn_pmu->base + HISI_MN_VERSION_REG); + return 0; +} + +static void hisi_mn_pmu_remove_cpuhp(void *hotplug_node) +{ + cpuhp_state_remove_instance_nocalls(hisi_mn_pmu_online, hotplug_node); +} + +static void hisi_mn_pmu_unregister(void *pmu) +{ + perf_pmu_unregister(pmu); +} + +static int hisi_mn_pmu_probe(struct platform_device *pdev) +{ + struct hisi_pmu *mn_pmu; + char *name; + int ret; + + mn_pmu = devm_kzalloc(&pdev->dev, sizeof(*mn_pmu), GFP_KERNEL); + if (!mn_pmu) + return -ENOMEM; + + platform_set_drvdata(pdev, mn_pmu); + + ret = hisi_mn_pmu_dev_init(pdev, mn_pmu); + if (ret) + return ret; + + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_scl%d_mn%d", + mn_pmu->topo.scl_id, mn_pmu->topo.index_id); + if (!name) + return -ENOMEM; + + ret = cpuhp_state_add_instance(hisi_mn_pmu_online, &mn_pmu->node); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register cpu hotplug\n"); + + ret = devm_add_action_or_reset(&pdev->dev, hisi_mn_pmu_remove_cpuhp, &mn_pmu->node); + if (ret) + return ret; + + hisi_pmu_init(mn_pmu, THIS_MODULE); + + ret = perf_pmu_register(&mn_pmu->pmu, name, -1); + if (ret) + return dev_err_probe(mn_pmu->dev, ret, "Failed to register MN PMU\n"); + + return devm_add_action_or_reset(&pdev->dev, hisi_mn_pmu_unregister, &mn_pmu->pmu); +} + +static const struct acpi_device_id hisi_mn_pmu_acpi_match[] = { + { "HISI0222", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, hisi_mn_pmu_acpi_match); + +static struct platform_driver hisi_mn_pmu_driver = { + .driver = { + .name = "hisi_mn_pmu", + .acpi_match_table = hisi_mn_pmu_acpi_match, + /* + * We have not worked out a safe bind/unbind process, + * Forcefully unbinding during sampling will lead to a + * kernel panic, so this is not supported yet. + */ + .suppress_bind_attrs = true, + }, + .probe = hisi_mn_pmu_probe, +}; + +static int __init hisi_mn_pmu_module_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "perf/hisi/mn:online", + hisi_uncore_pmu_online_cpu, + hisi_uncore_pmu_offline_cpu); + if (ret < 0) { + pr_err("hisi_mn_pmu: Failed to setup MN PMU hotplug: %d\n", ret); + return ret; + } + hisi_mn_pmu_online = ret; + + ret = platform_driver_register(&hisi_mn_pmu_driver); + if (ret) + cpuhp_remove_multi_state(hisi_mn_pmu_online); + + return ret; +} +module_init(hisi_mn_pmu_module_init); + +static void __exit hisi_mn_pmu_module_exit(void) +{ + platform_driver_unregister(&hisi_mn_pmu_driver); + cpuhp_remove_multi_state(hisi_mn_pmu_online); +} +module_exit(hisi_mn_pmu_module_exit); + +MODULE_IMPORT_NS(HISI_PMU); +MODULE_DESCRIPTION("HiSilicon SoC MN uncore PMU driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Junhao He "); From a07d92ad0ca05993ef18479cca025e88317e6f0a Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 16 Apr 2025 15:04:41 +0800 Subject: [PATCH 061/227] drivers/perf: hisi: Relax the event ID check in the framework driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC1F9Z CVE: NA ---------------------------------------------------------------------- Event ID is only using the attr::config bit [7, 0] but we check the event range using the whole 64bit field. It blocks the usage of the resident field of attr::config. Relax the check by only using the bit [7, 0]. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pmu.c | 2 +- drivers/perf/hisilicon/hisi_uncore_pmu.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index f6fe04e8f4d62..a355833b2d933 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -248,7 +248,7 @@ int hisi_uncore_pmu_event_init(struct perf_event *event) return -EINVAL; hisi_pmu = to_hisi_pmu(event->pmu); - if (event->attr.config > hisi_pmu->check_event) + if ((event->attr.config & HISI_EVENTID_MASK) > hisi_pmu->check_event) return -EINVAL; if (hisi_pmu->on_cpu == -1) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 42d6473b1223c..740aab63d7968 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -43,7 +43,8 @@ return FIELD_GET(GENMASK_ULL(hi, lo), event->attr.config); \ } -#define HISI_GET_EVENTID(ev) (ev->hw.config_base & 0xff) +#define HISI_EVENTID_MASK 0xff +#define HISI_GET_EVENTID(ev) (ev->hw.config_base & HISI_EVENTID_MASK) #define HISI_PMU_EVTYPE_BITS 8 #define HISI_PMU_EVTYPE_SHIFT(idx) ((idx) % 4 * HISI_PMU_EVTYPE_BITS) From 5b52a0328216945dfe0f94708d8f728dfc8e1f00 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 16 Apr 2025 15:04:42 +0800 Subject: [PATCH 062/227] drivers/perf: hisi: Export hisi_uncore_pmu_isr() driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC1F9Z CVE: NA ---------------------------------------------------------------------- Currently Uncore PMU framework assume one PMU device only have one interrupt and will help register the interrupt handler. It cannot support a PMU with multiple interrupt resources. However the interrupt handling for the Uncore PMU maybe the same and could share the same interrupt handler. Export hisi_uncore_pmu_isr() to allow drivers register the irq handler by their own routine. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pmu.c | 3 ++- drivers/perf/hisilicon/hisi_uncore_pmu.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c index a355833b2d933..f4ca07e071272 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c @@ -163,7 +163,7 @@ static void hisi_uncore_pmu_clear_event_idx(struct hisi_pmu *hisi_pmu, int idx) clear_bit(idx, hisi_pmu->pmu_events.used_mask); } -static irqreturn_t hisi_uncore_pmu_isr(int irq, void *data) +irqreturn_t hisi_uncore_pmu_isr(int irq, void *data) { struct hisi_pmu *hisi_pmu = data; struct perf_event *event; @@ -192,6 +192,7 @@ static irqreturn_t hisi_uncore_pmu_isr(int irq, void *data) return IRQ_HANDLED; } +EXPORT_SYMBOL_NS_GPL(hisi_uncore_pmu_isr, HISI_PMU); int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu, struct platform_device *pdev) diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h index 740aab63d7968..31225c2ccdcee 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pmu.h +++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h @@ -167,6 +167,7 @@ int hisi_uncore_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node); ssize_t hisi_uncore_pmu_identifier_attr_show(struct device *dev, struct device_attribute *attr, char *page); +irqreturn_t hisi_uncore_pmu_isr(int irq, void *data); int hisi_uncore_pmu_init_irq(struct hisi_pmu *hisi_pmu, struct platform_device *pdev); void hisi_uncore_pmu_init_topology(struct hisi_pmu *hisi_pmu, struct device *dev); From 3beffc921bb1c655f5eddceb033e32fe14078697 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 16 Apr 2025 15:04:43 +0800 Subject: [PATCH 063/227] drivers/perf: hisi: Simplify the probe process of each L3C PMU version driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC1F9Z CVE: NA ---------------------------------------------------------------------- Version 1 and 2 of L3C PMU also use different HID. Make use of struct acpi_device_id::driver_data for version specific information rather than judge the version register. This will help to simplify the probe process and also a bit easier for extension. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 43 ++++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 4c74eb88e8f0e..298687d1e09ab 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -345,13 +345,6 @@ static void hisi_l3c_pmu_clear_int_status(struct hisi_pmu *l3c_pmu, int idx) writel(1 << idx, l3c_pmu->base + L3C_INT_CLEAR); } -static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = { - { "HISI0213", }, - { "HISI0214", }, - {} -}; -MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); - static int hisi_l3c_pmu_init_data(struct platform_device *pdev, struct hisi_pmu *l3c_pmu) { @@ -371,6 +364,10 @@ static int hisi_l3c_pmu_init_data(struct platform_device *pdev, return -EINVAL; } + l3c_pmu->dev_info = device_get_match_data(&pdev->dev); + if (!l3c_pmu->dev_info) + return -ENODEV; + l3c_pmu->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(l3c_pmu->base)) { dev_err(&pdev->dev, "ioremap failed for l3c_pmu resource\n"); @@ -457,6 +454,18 @@ static const struct attribute_group *hisi_l3c_pmu_v2_attr_groups[] = { NULL }; +static const struct hisi_pmu_dev_info hisi_l3c_pmu_v1 = { + .attr_groups = hisi_l3c_pmu_v1_attr_groups, + .counter_bits = 48, + .check_event = L3C_V1_NR_EVENTS, +}; + +static const struct hisi_pmu_dev_info hisi_l3c_pmu_v2 = { + .attr_groups = hisi_l3c_pmu_v2_attr_groups, + .counter_bits = 64, + .check_event = L3C_V2_NR_EVENTS, +}; + static const struct hisi_uncore_ops hisi_uncore_l3c_ops = { .write_evtype = hisi_l3c_pmu_write_evtype, .get_event_idx = hisi_uncore_pmu_get_event_idx, @@ -487,16 +496,9 @@ static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev, if (ret) return ret; - if (l3c_pmu->identifier >= HISI_PMU_V2) { - l3c_pmu->counter_bits = 64; - l3c_pmu->check_event = L3C_V2_NR_EVENTS; - l3c_pmu->pmu_events.attr_groups = hisi_l3c_pmu_v2_attr_groups; - } else { - l3c_pmu->counter_bits = 48; - l3c_pmu->check_event = L3C_V1_NR_EVENTS; - l3c_pmu->pmu_events.attr_groups = hisi_l3c_pmu_v1_attr_groups; - } - + l3c_pmu->pmu_events.attr_groups = l3c_pmu->dev_info->attr_groups; + l3c_pmu->counter_bits = l3c_pmu->dev_info->counter_bits; + l3c_pmu->check_event = l3c_pmu->dev_info->check_event; l3c_pmu->num_counters = L3C_NR_COUNTERS; l3c_pmu->ops = &hisi_uncore_l3c_ops; l3c_pmu->dev = &pdev->dev; @@ -555,6 +557,13 @@ static int hisi_l3c_pmu_remove(struct platform_device *pdev) return 0; } +static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = { + { "HISI0213", (kernel_ulong_t)&hisi_l3c_pmu_v1 }, + { "HISI0214", (kernel_ulong_t)&hisi_l3c_pmu_v2 }, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); + static struct platform_driver hisi_l3c_pmu_driver = { .driver = { .name = "hisi_l3c_pmu", From 831a87b4f4c36945ebf9f62ce99447261cc7823c Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 16 Apr 2025 15:04:44 +0800 Subject: [PATCH 064/227] drivers/perf: hisi: Extract the event filter check of L3C PMU driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC1F9Z CVE: NA ---------------------------------------------------------------------- L3C PMU has 4 filter options which are sharing perf_event_attr::config1. Driver will check config1 to see whether a certain event has a filter setting. It'll be incorrect if we make use of other bits in config1 for non-filter options. So check whether each filter options are set directly in a separate function instead. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 298687d1e09ab..760195be9e6ed 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -204,9 +204,15 @@ static void hisi_l3c_pmu_clear_core_tracetag(struct perf_event *event) } } +static bool hisi_l3c_pmu_have_filter(struct perf_event *event) +{ + return hisi_get_tt_req(event) || hisi_get_tt_core(event) || + hisi_get_datasrc_cfg(event) || hisi_get_datasrc_skt(event); +} + static void hisi_l3c_pmu_enable_filter(struct perf_event *event) { - if (event->attr.config1 != 0x0) { + if (hisi_l3c_pmu_have_filter(event)) { hisi_l3c_pmu_config_req_tracetag(event); hisi_l3c_pmu_config_core_tracetag(event); hisi_l3c_pmu_config_ds(event); @@ -215,7 +221,7 @@ static void hisi_l3c_pmu_enable_filter(struct perf_event *event) static void hisi_l3c_pmu_disable_filter(struct perf_event *event) { - if (event->attr.config1 != 0x0) { + if (hisi_l3c_pmu_have_filter(event)) { hisi_l3c_pmu_clear_ds(event); hisi_l3c_pmu_clear_core_tracetag(event); hisi_l3c_pmu_clear_req_tracetag(event); From 8e4115af1acbc4ccfa417d72615694b1579fc051 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 16 Apr 2025 15:04:45 +0800 Subject: [PATCH 065/227] drivers/perf: hisi: Extend the field of tt_core driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC1F9Z CVE: NA ---------------------------------------------------------------------- Currently the tt_core's using config1's bit [7, 0] and can not be extended. For some platforms there's more the 8 CPUs sharing the L3 cache. So make tt_core use config2's bit [15, 0] and the remaining bits in config2 is reserved for extension. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 760195be9e6ed..915413e7f77d4 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -55,10 +55,10 @@ #define L3C_V1_NR_EVENTS 0x59 #define L3C_V2_NR_EVENTS 0xFF -HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_core, config1, 7, 0); HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_req, config1, 10, 8); HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_cfg, config1, 15, 11); HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_skt, config1, 16, 16); +HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_core, config2, 15, 0); static void hisi_l3c_pmu_config_req_tracetag(struct perf_event *event) { @@ -397,7 +397,7 @@ static const struct attribute_group hisi_l3c_pmu_v1_format_group = { static struct attribute *hisi_l3c_pmu_v2_format_attr[] = { HISI_PMU_FORMAT_ATTR(event, "config:0-7"), - HISI_PMU_FORMAT_ATTR(tt_core, "config1:0-7"), + HISI_PMU_FORMAT_ATTR(tt_core, "config2:0-15"), HISI_PMU_FORMAT_ATTR(tt_req, "config1:8-10"), HISI_PMU_FORMAT_ATTR(datasrc_cfg, "config1:11-15"), HISI_PMU_FORMAT_ATTR(datasrc_skt, "config1:16"), From 6e3f3803d68178672ed81982da9a51773bb3f66d Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 16 Apr 2025 15:04:46 +0800 Subject: [PATCH 066/227] drivers/perf: hisi: Refactor the event configuration of L3C PMU driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC1F9Z CVE: NA ---------------------------------------------------------------------- The event register is configured using hisi_pmu::base directly since only one address space is supported for L3C PMU. It'll be hard to extend if events configuration locates in different address space. In order to make preparation for such hardware, extract the event register configuration to separate function using hw_perf_event::event_base as each event's base address. Implement a private hisi_uncore_ops::get_event_idx() callback for initialize the event_base besides get the hardware index. No functional changes intended. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 128 ++++++++++++------- 1 file changed, 83 insertions(+), 45 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 915413e7f77d4..9ae7563ec98e5 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -60,51 +60,86 @@ HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_cfg, config1, 15, 11); HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_skt, config1, 16, 16); HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_core, config2, 15, 0); -static void hisi_l3c_pmu_config_req_tracetag(struct perf_event *event) +static int hisi_l3c_pmu_get_event_idx(struct perf_event *event) { struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); + unsigned long *used_mask = l3c_pmu->pmu_events.used_mask; + u32 num_counters = l3c_pmu->num_counters; + int idx; + + idx = find_first_zero_bit(used_mask, num_counters); + if (idx == num_counters) + return -EAGAIN; + + set_bit(idx, used_mask); + event->hw.event_base = (unsigned long)l3c_pmu->base; + return idx; +} + +static u32 hisi_l3c_pmu_event_readl(struct hw_perf_event *hwc, u32 reg) +{ + return readl((void __iomem *)hwc->event_base + reg); +} + +static void hisi_l3c_pmu_event_writel(struct hw_perf_event *hwc, u32 reg, u32 val) +{ + writel(val, (void __iomem *)hwc->event_base + reg); +} + +static u64 hisi_l3c_pmu_event_readq(struct hw_perf_event *hwc, u32 reg) +{ + return readq((void __iomem *)hwc->event_base + reg); +} + +static void hisi_l3c_pmu_event_writeq(struct hw_perf_event *hwc, u32 reg, u64 val) +{ + writeq(val, (void __iomem *)hwc->event_base + reg); +} + +static void hisi_l3c_pmu_config_req_tracetag(struct perf_event *event) +{ + struct hw_perf_event *hwc = &event->hw; u32 tt_req = hisi_get_tt_req(event); if (tt_req) { u32 val; /* Set request-type for tracetag */ - val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_TRACETAG_CTRL); val |= tt_req << L3C_TRACETAG_REQ_SHIFT; val |= L3C_TRACETAG_REQ_EN; - writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_TRACETAG_CTRL, val); /* Enable request-tracetag statistics */ - val = readl(l3c_pmu->base + L3C_PERF_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_PERF_CTRL); val |= L3C_TRACETAG_EN; - writel(val, l3c_pmu->base + L3C_PERF_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_PERF_CTRL, val); } } static void hisi_l3c_pmu_clear_req_tracetag(struct perf_event *event) { - struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; u32 tt_req = hisi_get_tt_req(event); if (tt_req) { u32 val; /* Clear request-type */ - val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_TRACETAG_CTRL); val &= ~(tt_req << L3C_TRACETAG_REQ_SHIFT); val &= ~L3C_TRACETAG_REQ_EN; - writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_TRACETAG_CTRL, val); /* Disable request-tracetag statistics */ - val = readl(l3c_pmu->base + L3C_PERF_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_PERF_CTRL); val &= ~L3C_TRACETAG_EN; - writel(val, l3c_pmu->base + L3C_PERF_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_PERF_CTRL, val); } } static void hisi_l3c_pmu_write_ds(struct perf_event *event, u32 ds_cfg) { - struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); struct hw_perf_event *hwc = &event->hw; u32 reg, reg_idx, shift, val; int idx = hwc->idx; @@ -120,15 +155,15 @@ static void hisi_l3c_pmu_write_ds(struct perf_event *event, u32 ds_cfg) reg_idx = idx % 4; shift = 8 * reg_idx; - val = readl(l3c_pmu->base + reg); + val = hisi_l3c_pmu_event_readl(hwc, reg); val &= ~(L3C_DATSRC_MASK << shift); val |= ds_cfg << shift; - writel(val, l3c_pmu->base + reg); + hisi_l3c_pmu_event_writel(hwc, reg, val); } static void hisi_l3c_pmu_config_ds(struct perf_event *event) { - struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; u32 ds_cfg = hisi_get_datasrc_cfg(event); u32 ds_skt = hisi_get_datasrc_skt(event); @@ -138,15 +173,15 @@ static void hisi_l3c_pmu_config_ds(struct perf_event *event) if (ds_skt) { u32 val; - val = readl(l3c_pmu->base + L3C_DATSRC_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_DATSRC_CTRL); val |= L3C_DATSRC_SKT_EN; - writel(val, l3c_pmu->base + L3C_DATSRC_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_DATSRC_CTRL, val); } } static void hisi_l3c_pmu_clear_ds(struct perf_event *event) { - struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; u32 ds_cfg = hisi_get_datasrc_cfg(event); u32 ds_skt = hisi_get_datasrc_skt(event); @@ -156,51 +191,51 @@ static void hisi_l3c_pmu_clear_ds(struct perf_event *event) if (ds_skt) { u32 val; - val = readl(l3c_pmu->base + L3C_DATSRC_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_DATSRC_CTRL); val &= ~L3C_DATSRC_SKT_EN; - writel(val, l3c_pmu->base + L3C_DATSRC_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_DATSRC_CTRL, val); } } static void hisi_l3c_pmu_config_core_tracetag(struct perf_event *event) { - struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; u32 core = hisi_get_tt_core(event); if (core) { u32 val; /* Config and enable core information */ - writel(core, l3c_pmu->base + L3C_CORE_CTRL); - val = readl(l3c_pmu->base + L3C_PERF_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_CORE_CTRL, core); + val = hisi_l3c_pmu_event_readl(hwc, L3C_PERF_CTRL); val |= L3C_CORE_EN; - writel(val, l3c_pmu->base + L3C_PERF_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_PERF_CTRL, val); /* Enable core-tracetag statistics */ - val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_TRACETAG_CTRL); val |= L3C_TRACETAG_CORE_EN; - writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_TRACETAG_CTRL, val); } } static void hisi_l3c_pmu_clear_core_tracetag(struct perf_event *event) { - struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); + struct hw_perf_event *hwc = &event->hw; u32 core = hisi_get_tt_core(event); if (core) { u32 val; /* Clear core information */ - writel(L3C_COER_NONE, l3c_pmu->base + L3C_CORE_CTRL); - val = readl(l3c_pmu->base + L3C_PERF_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_CORE_CTRL, L3C_COER_NONE); + val = hisi_l3c_pmu_event_readl(hwc, L3C_PERF_CTRL); val &= ~L3C_CORE_EN; - writel(val, l3c_pmu->base + L3C_PERF_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_PERF_CTRL, val); /* Disable core-tracetag statistics */ - val = readl(l3c_pmu->base + L3C_TRACETAG_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_TRACETAG_CTRL); val &= ~L3C_TRACETAG_CORE_EN; - writel(val, l3c_pmu->base + L3C_TRACETAG_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_TRACETAG_CTRL, val); } } @@ -239,18 +274,19 @@ static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx) static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu, struct hw_perf_event *hwc) { - return readq(l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(hwc->idx)); + return hisi_l3c_pmu_event_readq(hwc, hisi_l3c_pmu_get_counter_offset(hwc->idx)); } static void hisi_l3c_pmu_write_counter(struct hisi_pmu *l3c_pmu, struct hw_perf_event *hwc, u64 val) { - writeq(val, l3c_pmu->base + hisi_l3c_pmu_get_counter_offset(hwc->idx)); + hisi_l3c_pmu_event_writeq(hwc, hisi_l3c_pmu_get_counter_offset(hwc->idx), val); } static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx, u32 type) { + struct hw_perf_event *hwc = &l3c_pmu->pmu_events.hw_events[idx]->hw; u32 reg, reg_idx, shift, val; /* @@ -265,10 +301,10 @@ static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx, shift = 8 * reg_idx; /* Write event code to L3C_EVENT_TYPEx Register */ - val = readl(l3c_pmu->base + reg); + val = hisi_l3c_pmu_event_readl(hwc, reg); val &= ~(L3C_EVTYPE_NONE << shift); val |= (type << shift); - writel(val, l3c_pmu->base + reg); + hisi_l3c_pmu_event_writel(hwc, reg, val); } static void hisi_l3c_pmu_start_counters(struct hisi_pmu *l3c_pmu) @@ -303,9 +339,9 @@ static void hisi_l3c_pmu_enable_counter(struct hisi_pmu *l3c_pmu, u32 val; /* Enable counter index in L3C_EVENT_CTRL register */ - val = readl(l3c_pmu->base + L3C_EVENT_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_EVENT_CTRL); val |= (1 << hwc->idx); - writel(val, l3c_pmu->base + L3C_EVENT_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_EVENT_CTRL, val); } static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu, @@ -314,9 +350,9 @@ static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu, u32 val; /* Clear counter index in L3C_EVENT_CTRL register */ - val = readl(l3c_pmu->base + L3C_EVENT_CTRL); + val = hisi_l3c_pmu_event_readl(hwc, L3C_EVENT_CTRL); val &= ~(1 << hwc->idx); - writel(val, l3c_pmu->base + L3C_EVENT_CTRL); + hisi_l3c_pmu_event_writel(hwc, L3C_EVENT_CTRL, val); } static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu, @@ -324,10 +360,10 @@ static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu, { u32 val; - val = readl(l3c_pmu->base + L3C_INT_MASK); + val = hisi_l3c_pmu_event_readl(hwc, L3C_INT_MASK); /* Write 0 to enable interrupt */ val &= ~(1 << hwc->idx); - writel(val, l3c_pmu->base + L3C_INT_MASK); + hisi_l3c_pmu_event_writel(hwc, L3C_INT_MASK, val); } static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu, @@ -335,10 +371,10 @@ static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu, { u32 val; - val = readl(l3c_pmu->base + L3C_INT_MASK); + val = hisi_l3c_pmu_event_readl(hwc, L3C_INT_MASK); /* Write 1 to mask interrupt */ val |= (1 << hwc->idx); - writel(val, l3c_pmu->base + L3C_INT_MASK); + hisi_l3c_pmu_event_writel(hwc, L3C_INT_MASK, val); } static u32 hisi_l3c_pmu_get_int_status(struct hisi_pmu *l3c_pmu) @@ -348,7 +384,9 @@ static u32 hisi_l3c_pmu_get_int_status(struct hisi_pmu *l3c_pmu) static void hisi_l3c_pmu_clear_int_status(struct hisi_pmu *l3c_pmu, int idx) { - writel(1 << idx, l3c_pmu->base + L3C_INT_CLEAR); + struct hw_perf_event *hwc = &l3c_pmu->pmu_events.hw_events[idx]->hw; + + hisi_l3c_pmu_event_writel(hwc, L3C_INT_CLEAR, 1 << idx); } static int hisi_l3c_pmu_init_data(struct platform_device *pdev, @@ -474,7 +512,7 @@ static const struct hisi_pmu_dev_info hisi_l3c_pmu_v2 = { static const struct hisi_uncore_ops hisi_uncore_l3c_ops = { .write_evtype = hisi_l3c_pmu_write_evtype, - .get_event_idx = hisi_uncore_pmu_get_event_idx, + .get_event_idx = hisi_l3c_pmu_get_event_idx, .start_counters = hisi_l3c_pmu_start_counters, .stop_counters = hisi_l3c_pmu_stop_counters, .enable_counter = hisi_l3c_pmu_enable_counter, From a285be2f2c98ed056dbe3865eb3e8062f64d571c Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Wed, 16 Apr 2025 15:04:47 +0800 Subject: [PATCH 067/227] drivers/perf: hisi: Add support for L3C PMU v3 driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC1F9Z CVE: NA ---------------------------------------------------------------------- This patch adds support for L3C PMU v3. The v3 L3C PMU supports an extended events space which can be controlled in a second address space with a separate overflow interrupt. The offset of the control/event registers keep the same. The extended events with original ones together cover the monitoring of all the L3C transactions. The extended events is specified with `ext[=1]` option for the driver to distinguish like: perf stat -e hisi_sccl0_l3c0_0/event=,ext/ Currently only event option using config bit [7, 0]. There's still lots of field unused. Make ext using config 16 and reserve bit [15, 8] for event option for future extension. The hw_perf_event::event_base is initialized to the base MMIO address of the event and will be used for later control, overflow handling and counts readout. We still make use of the Uncore PMU framework for handling the events and interrupt migration on CPU hotplug. The framework's cpuhp callback will handle the event migration and interrupt migration of orginial event, if PMU supports extended events then the interrupt of extended events is migrated to the same CPU choosed by the framework. A new HID of HISI0215 is used for this version of L3C PMU. Signed-off-by: Yicong Yang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 350 +++++++++++++++++-- 1 file changed, 328 insertions(+), 22 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index 9ae7563ec98e5..f4afd8c7bf379 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -55,24 +55,73 @@ #define L3C_V1_NR_EVENTS 0x59 #define L3C_V2_NR_EVENTS 0xFF +HISI_PMU_EVENT_ATTR_EXTRACTOR(ext, config, 16, 16); HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_req, config1, 10, 8); HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_cfg, config1, 15, 11); HISI_PMU_EVENT_ATTR_EXTRACTOR(datasrc_skt, config1, 16, 16); HISI_PMU_EVENT_ATTR_EXTRACTOR(tt_core, config2, 15, 0); +struct hisi_l3c_pmu { + struct hisi_pmu l3c_pmu; + unsigned long feature; +#define L3C_PMU_FEAT_EXT 0x1 + + /* MMIO and IRQ resources for extension events */ + void __iomem *ext_base; + int ext_irq; +}; + +#define to_hisi_l3c_pmu(_l3c_pmu) \ + container_of(_l3c_pmu, struct hisi_l3c_pmu, l3c_pmu) + +/* + * The hardware counter idx used in counter enable/disable, + * interrupt enable/disable and status check, etc. + */ +#define L3C_HW_IDX(_idx) ((_idx) % L3C_NR_COUNTERS) + static int hisi_l3c_pmu_get_event_idx(struct perf_event *event) { struct hisi_pmu *l3c_pmu = to_hisi_pmu(event->pmu); unsigned long *used_mask = l3c_pmu->pmu_events.used_mask; u32 num_counters = l3c_pmu->num_counters; + struct hisi_l3c_pmu *hisi_l3c_pmu; int idx; - idx = find_first_zero_bit(used_mask, num_counters); + hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); + + /* + * For an L3C PMU that supports extension events, we can monitor + * maximum 2 * num_counters events. Thus use bit [0, num_counters - 1] + * for normal events and bit [num_counters, 2 * num_counters - 1] for + * extension events. The idx allocation will keep unchanged for normal + * events and we can also use the idx to distinguish whether it's an + * extension event or not. + * + * Since normal events and extension events locates on the different + * address space, save the base address to the event->hw.event_base. + */ + if (hisi_get_ext(event)) { + if (!(hisi_l3c_pmu->feature & L3C_PMU_FEAT_EXT)) + return -EOPNOTSUPP; + + event->hw.event_base = (unsigned long)hisi_l3c_pmu->ext_base; + idx = find_next_zero_bit(used_mask, num_counters, L3C_NR_COUNTERS); + } else { + event->hw.event_base = (unsigned long)l3c_pmu->base; + idx = find_next_zero_bit(used_mask, L3C_NR_COUNTERS, 0); + if (idx == L3C_NR_COUNTERS) + idx = num_counters; + } + if (idx == num_counters) return -EAGAIN; set_bit(idx, used_mask); - event->hw.event_base = (unsigned long)l3c_pmu->base; + + WARN_ON(hisi_get_ext(event) && idx < L3C_NR_COUNTERS); + WARN_ON(!hisi_get_ext(event) && idx >= L3C_NR_COUNTERS); + return idx; } @@ -142,7 +191,7 @@ static void hisi_l3c_pmu_write_ds(struct perf_event *event, u32 ds_cfg) { struct hw_perf_event *hwc = &event->hw; u32 reg, reg_idx, shift, val; - int idx = hwc->idx; + int idx = L3C_HW_IDX(hwc->idx); /* * Select the appropriate datasource register(L3C_DATSRC_TYPE0/1). @@ -268,7 +317,7 @@ static void hisi_l3c_pmu_disable_filter(struct perf_event *event) */ static u32 hisi_l3c_pmu_get_counter_offset(int cntr_idx) { - return (L3C_CNTR0_LOWER + (cntr_idx * 8)); + return (L3C_CNTR0_LOWER + (L3C_HW_IDX(cntr_idx) * 8)); } static u64 hisi_l3c_pmu_read_counter(struct hisi_pmu *l3c_pmu, @@ -289,6 +338,8 @@ static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx, struct hw_perf_event *hwc = &l3c_pmu->pmu_events.hw_events[idx]->hw; u32 reg, reg_idx, shift, val; + idx = L3C_HW_IDX(idx); + /* * Select the appropriate event select register(L3C_EVENT_TYPE0/1). * There are 2 event select registers for the 8 hardware counters. @@ -309,28 +360,48 @@ static void hisi_l3c_pmu_write_evtype(struct hisi_pmu *l3c_pmu, int idx, static void hisi_l3c_pmu_start_counters(struct hisi_pmu *l3c_pmu) { + struct hisi_l3c_pmu *hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); + unsigned long *used_mask = l3c_pmu->pmu_events.used_mask; u32 val; /* * Set perf_enable bit in L3C_PERF_CTRL register to start counting * for all enabled counters. */ - val = readl(l3c_pmu->base + L3C_PERF_CTRL); - val |= L3C_PERF_CTRL_EN; - writel(val, l3c_pmu->base + L3C_PERF_CTRL); + if (find_first_bit(used_mask, l3c_pmu->num_counters) < L3C_NR_COUNTERS) { + val = readl(l3c_pmu->base + L3C_PERF_CTRL); + val |= L3C_PERF_CTRL_EN; + writel(val, l3c_pmu->base + L3C_PERF_CTRL); + } + + if (find_next_bit(used_mask, l3c_pmu->num_counters, L3C_NR_COUNTERS) != l3c_pmu->num_counters) { + val = readl(hisi_l3c_pmu->ext_base + L3C_PERF_CTRL); + val |= L3C_PERF_CTRL_EN; + writel(val, hisi_l3c_pmu->ext_base + L3C_PERF_CTRL); + } } static void hisi_l3c_pmu_stop_counters(struct hisi_pmu *l3c_pmu) { + struct hisi_l3c_pmu *hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); + unsigned long *used_mask = l3c_pmu->pmu_events.used_mask; u32 val; /* * Clear perf_enable bit in L3C_PERF_CTRL register to stop counting * for all enabled counters. */ - val = readl(l3c_pmu->base + L3C_PERF_CTRL); - val &= ~(L3C_PERF_CTRL_EN); - writel(val, l3c_pmu->base + L3C_PERF_CTRL); + if (find_first_bit(used_mask, l3c_pmu->num_counters) < L3C_NR_COUNTERS) { + val = readl(l3c_pmu->base + L3C_PERF_CTRL); + val &= ~(L3C_PERF_CTRL_EN); + writel(val, l3c_pmu->base + L3C_PERF_CTRL); + } + + if (find_next_bit(used_mask, l3c_pmu->num_counters, L3C_NR_COUNTERS) != l3c_pmu->num_counters) { + val = readl(hisi_l3c_pmu->ext_base + L3C_PERF_CTRL); + val &= ~(L3C_PERF_CTRL_EN); + writel(val, hisi_l3c_pmu->ext_base + L3C_PERF_CTRL); + } } static void hisi_l3c_pmu_enable_counter(struct hisi_pmu *l3c_pmu, @@ -340,7 +411,7 @@ static void hisi_l3c_pmu_enable_counter(struct hisi_pmu *l3c_pmu, /* Enable counter index in L3C_EVENT_CTRL register */ val = hisi_l3c_pmu_event_readl(hwc, L3C_EVENT_CTRL); - val |= (1 << hwc->idx); + val |= (1 << L3C_HW_IDX(hwc->idx)); hisi_l3c_pmu_event_writel(hwc, L3C_EVENT_CTRL, val); } @@ -351,7 +422,7 @@ static void hisi_l3c_pmu_disable_counter(struct hisi_pmu *l3c_pmu, /* Clear counter index in L3C_EVENT_CTRL register */ val = hisi_l3c_pmu_event_readl(hwc, L3C_EVENT_CTRL); - val &= ~(1 << hwc->idx); + val &= ~(1 << L3C_HW_IDX(hwc->idx)); hisi_l3c_pmu_event_writel(hwc, L3C_EVENT_CTRL, val); } @@ -362,7 +433,7 @@ static void hisi_l3c_pmu_enable_counter_int(struct hisi_pmu *l3c_pmu, val = hisi_l3c_pmu_event_readl(hwc, L3C_INT_MASK); /* Write 0 to enable interrupt */ - val &= ~(1 << hwc->idx); + val &= ~(1 << L3C_HW_IDX(hwc->idx)); hisi_l3c_pmu_event_writel(hwc, L3C_INT_MASK, val); } @@ -373,20 +444,27 @@ static void hisi_l3c_pmu_disable_counter_int(struct hisi_pmu *l3c_pmu, val = hisi_l3c_pmu_event_readl(hwc, L3C_INT_MASK); /* Write 1 to mask interrupt */ - val |= (1 << hwc->idx); + val |= (1 << L3C_HW_IDX(hwc->idx)); hisi_l3c_pmu_event_writel(hwc, L3C_INT_MASK, val); } static u32 hisi_l3c_pmu_get_int_status(struct hisi_pmu *l3c_pmu) { - return readl(l3c_pmu->base + L3C_INT_STATUS); + struct hisi_l3c_pmu *hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); + u32 status, status_ext = 0; + + status = readl(l3c_pmu->base + L3C_INT_STATUS); + if (hisi_l3c_pmu->feature & L3C_PMU_FEAT_EXT) + status_ext = readl(hisi_l3c_pmu->ext_base + L3C_INT_STATUS); + + return status | (status_ext << L3C_NR_COUNTERS); } static void hisi_l3c_pmu_clear_int_status(struct hisi_pmu *l3c_pmu, int idx) { struct hw_perf_event *hwc = &l3c_pmu->pmu_events.hw_events[idx]->hw; - hisi_l3c_pmu_event_writel(hwc, L3C_INT_CLEAR, 1 << idx); + hisi_l3c_pmu_event_writel(hwc, L3C_INT_CLEAR, 1 << L3C_HW_IDX(idx)); } static int hisi_l3c_pmu_init_data(struct platform_device *pdev, @@ -423,6 +501,41 @@ static int hisi_l3c_pmu_init_data(struct platform_device *pdev, return 0; } +static int hisi_l3c_pmu_init_ext(struct hisi_pmu *l3c_pmu, struct platform_device *pdev) +{ + struct hisi_l3c_pmu *hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); + char *irqname; + int ret, irq; + + hisi_l3c_pmu->ext_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(hisi_l3c_pmu->ext_base)) + return PTR_ERR(hisi_l3c_pmu->ext_base); + + irq = platform_get_irq(pdev, 1); + /* + * We may don't need to handle -EPROBDEFER since we should have already + * handle it when probling irq[0]. + */ + if (irq < 0) + return irq; + + irqname = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s ext", dev_name(&pdev->dev)); + if (!irqname) + return -ENOMEM; + + ret = devm_request_irq(&pdev->dev, irq, hisi_uncore_pmu_isr, + IRQF_NOBALANCING | IRQF_NO_THREAD, + irqname, l3c_pmu); + if (ret < 0) { + dev_err(&pdev->dev, + "Fail to request EXT IRQ: %d ret: %d.\n", irq, ret); + return ret; + } + + hisi_l3c_pmu->ext_irq = irq; + return 0; +} + static struct attribute *hisi_l3c_pmu_v1_format_attr[] = { HISI_PMU_FORMAT_ATTR(event, "config:0-7"), NULL, @@ -447,6 +560,19 @@ static const struct attribute_group hisi_l3c_pmu_v2_format_group = { .attrs = hisi_l3c_pmu_v2_format_attr, }; +static struct attribute *hisi_l3c_pmu_v3_format_attr[] = { + HISI_PMU_FORMAT_ATTR(event, "config:0-7"), + HISI_PMU_FORMAT_ATTR(ext, "config:16"), + HISI_PMU_FORMAT_ATTR(tt_req, "config1:8-10"), + HISI_PMU_FORMAT_ATTR(tt_core, "config2:0-15"), + NULL +}; + +static const struct attribute_group hisi_l3c_pmu_v3_format_group = { + .name = "format", + .attrs = hisi_l3c_pmu_v3_format_attr, +}; + static struct attribute *hisi_l3c_pmu_v1_events_attr[] = { HISI_PMU_EVENT_ATTR(rd_cpipe, 0x00), HISI_PMU_EVENT_ATTR(wr_cpipe, 0x01), @@ -482,6 +608,105 @@ static const struct attribute_group hisi_l3c_pmu_v2_events_group = { .attrs = hisi_l3c_pmu_v2_events_attr, }; +struct hisi_l3c_pmu_v3_event { + unsigned long event_id; + bool ext; +}; + +static ssize_t hisi_l3c_pmu_event_show(struct device *dev, + struct device_attribute *attr, char *page) +{ + struct hisi_l3c_pmu_v3_event *event; + struct dev_ext_attribute *eattr; + + eattr = container_of(attr, struct dev_ext_attribute, attr); + event = eattr->var; + + if (!event->ext) + return sysfs_emit(page, "event=0x%lx\n", event->event_id); + else + return sysfs_emit(page, "event=0x%lx,ext=1\n", event->event_id); +} + +#define HISI_L3C_PMU_EVENT_ATTR(_name, _event, _ext) \ +static struct hisi_l3c_pmu_v3_event hisi_l3c_##_name = { _event, _ext }; \ +static struct dev_ext_attribute hisi_l3c_##_name##_attr = \ + { __ATTR(_name, 0444, hisi_l3c_pmu_event_show, NULL), (void *) &hisi_l3c_##_name } + +HISI_L3C_PMU_EVENT_ATTR(rd_cpipe, 0x00, true); +HISI_L3C_PMU_EVENT_ATTR(rd_hit_cpipe, 0x01, true); +HISI_L3C_PMU_EVENT_ATTR(wr_cpipe, 0x02, true); +HISI_L3C_PMU_EVENT_ATTR(wr_hit_cpipe, 0x03, true); +HISI_L3C_PMU_EVENT_ATTR(io_rd_cpipe, 0x04, true); +HISI_L3C_PMU_EVENT_ATTR(io_rd_hit_cpipe, 0x05, true); +HISI_L3C_PMU_EVENT_ATTR(io_wr_cpipe, 0x06, true); +HISI_L3C_PMU_EVENT_ATTR(io_wr_hit_cpipe, 0x07, true); +HISI_L3C_PMU_EVENT_ATTR(victim_num, 0x0c, true); +HISI_L3C_PMU_EVENT_ATTR(rd_spipe, 0x18, false); +HISI_L3C_PMU_EVENT_ATTR(rd_hit_spipe, 0x19, false); +HISI_L3C_PMU_EVENT_ATTR(wr_spipe, 0x1a, false); +HISI_L3C_PMU_EVENT_ATTR(wr_hit_spipe, 0x1b, false); +HISI_L3C_PMU_EVENT_ATTR(io_rd_spipe, 0x1c, false); +HISI_L3C_PMU_EVENT_ATTR(io_rd_hit_spipe, 0x1d, false); +HISI_L3C_PMU_EVENT_ATTR(io_wr_spipe, 0x1e, false); +HISI_L3C_PMU_EVENT_ATTR(io_wr_hit_spipe, 0x1f, false); +HISI_L3C_PMU_EVENT_ATTR(cycles, 0x7f, false); +HISI_L3C_PMU_EVENT_ATTR(l3t_comp_sum, 0x80, false); +HISI_L3C_PMU_EVENT_ATTR(l3t_rdnotram, 0x83, true); +HISI_L3C_PMU_EVENT_ATTR(l3c_ref, 0xbc, false); +HISI_L3C_PMU_EVENT_ATTR(l3c2ring, 0xbd, true); + +static struct attribute *hisi_l3c_pmu_v3_events_attr[] = { + &hisi_l3c_rd_cpipe_attr.attr.attr, + &hisi_l3c_rd_hit_cpipe_attr.attr.attr, + &hisi_l3c_wr_cpipe_attr.attr.attr, + &hisi_l3c_wr_hit_cpipe_attr.attr.attr, + &hisi_l3c_io_rd_cpipe_attr.attr.attr, + &hisi_l3c_io_rd_hit_cpipe_attr.attr.attr, + &hisi_l3c_io_wr_cpipe_attr.attr.attr, + &hisi_l3c_io_wr_hit_cpipe_attr.attr.attr, + &hisi_l3c_victim_num_attr.attr.attr, + &hisi_l3c_rd_spipe_attr.attr.attr, + &hisi_l3c_rd_hit_spipe_attr.attr.attr, + &hisi_l3c_wr_spipe_attr.attr.attr, + &hisi_l3c_wr_hit_spipe_attr.attr.attr, + &hisi_l3c_io_rd_spipe_attr.attr.attr, + &hisi_l3c_io_rd_hit_spipe_attr.attr.attr, + &hisi_l3c_io_wr_spipe_attr.attr.attr, + &hisi_l3c_io_wr_hit_spipe_attr.attr.attr, + &hisi_l3c_cycles_attr.attr.attr, + &hisi_l3c_l3t_comp_sum_attr.attr.attr, + &hisi_l3c_l3t_rdnotram_attr.attr.attr, + &hisi_l3c_l3c_ref_attr.attr.attr, + &hisi_l3c_l3c2ring_attr.attr.attr, + NULL +}; + +static umode_t hisi_l3c_pmu_v3_events_visible(struct kobject *kobj, + struct attribute *attr, int unused) +{ + struct device *dev = kobj_to_dev(kobj); + struct pmu *pmu = dev_get_drvdata(dev); + struct hisi_pmu *l3c_pmu = to_hisi_pmu(pmu); + struct hisi_l3c_pmu *hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); + struct hisi_l3c_pmu_v3_event *event; + struct dev_ext_attribute *ext_attr; + + ext_attr = container_of(attr, struct dev_ext_attribute, attr.attr); + event = ext_attr->var; + + if (!event->ext || (hisi_l3c_pmu->feature & L3C_PMU_FEAT_EXT)) + return attr->mode; + + return 0; +} + +static const struct attribute_group hisi_l3c_pmu_v3_events_group = { + .name = "events", + .is_visible = hisi_l3c_pmu_v3_events_visible, + .attrs = hisi_l3c_pmu_v3_events_attr, +}; + static const struct attribute_group *hisi_l3c_pmu_v1_attr_groups[] = { &hisi_l3c_pmu_v1_format_group, &hisi_l3c_pmu_v1_events_group, @@ -498,6 +723,14 @@ static const struct attribute_group *hisi_l3c_pmu_v2_attr_groups[] = { NULL }; +static const struct attribute_group *hisi_l3c_pmu_v3_attr_groups[] = { + &hisi_l3c_pmu_v3_format_group, + &hisi_l3c_pmu_v3_events_group, + &hisi_pmu_cpumask_attr_group, + &hisi_pmu_identifier_group, + NULL +}; + static const struct hisi_pmu_dev_info hisi_l3c_pmu_v1 = { .attr_groups = hisi_l3c_pmu_v1_attr_groups, .counter_bits = 48, @@ -510,6 +743,13 @@ static const struct hisi_pmu_dev_info hisi_l3c_pmu_v2 = { .check_event = L3C_V2_NR_EVENTS, }; +static const struct hisi_pmu_dev_info hisi_l3c_pmu_v3 = { + .attr_groups = hisi_l3c_pmu_v3_attr_groups, + .counter_bits = 64, + .check_event = L3C_V2_NR_EVENTS, + .private = (void *) L3C_PMU_FEAT_EXT, +}; + static const struct hisi_uncore_ops hisi_uncore_l3c_ops = { .write_evtype = hisi_l3c_pmu_write_evtype, .get_event_idx = hisi_l3c_pmu_get_event_idx, @@ -530,6 +770,7 @@ static const struct hisi_uncore_ops hisi_uncore_l3c_ops = { static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev, struct hisi_pmu *l3c_pmu) { + struct hisi_l3c_pmu *hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); int ret; ret = hisi_l3c_pmu_init_data(pdev, l3c_pmu); @@ -548,27 +789,50 @@ static int hisi_l3c_pmu_dev_probe(struct platform_device *pdev, l3c_pmu->dev = &pdev->dev; l3c_pmu->on_cpu = -1; + if ((unsigned long)l3c_pmu->dev_info->private & L3C_PMU_FEAT_EXT) { + ret = hisi_l3c_pmu_init_ext(l3c_pmu, pdev); + if (ret) { + dev_warn(&pdev->dev, "ext event is unavailable, ret = %d\n", ret); + } else { + /* + * The extension events have their own counters with the + * same number of the normal events counters. So we can + * have at maximum num_counters * 2 events monitored. + */ + l3c_pmu->num_counters <<= 1; + + hisi_l3c_pmu->feature |= L3C_PMU_FEAT_EXT; + } + } + return 0; } static int hisi_l3c_pmu_probe(struct platform_device *pdev) { + struct hisi_l3c_pmu *hisi_l3c_pmu; struct hisi_pmu *l3c_pmu; char *name; int ret; - l3c_pmu = devm_kzalloc(&pdev->dev, sizeof(*l3c_pmu), GFP_KERNEL); - if (!l3c_pmu) + hisi_l3c_pmu = devm_kzalloc(&pdev->dev, sizeof(*hisi_l3c_pmu), GFP_KERNEL); + if (!hisi_l3c_pmu) return -ENOMEM; + l3c_pmu = &hisi_l3c_pmu->l3c_pmu; platform_set_drvdata(pdev, l3c_pmu); ret = hisi_l3c_pmu_dev_probe(pdev, l3c_pmu); if (ret) return ret; - name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_l3c%d", - l3c_pmu->topo.sccl_id, l3c_pmu->topo.ccl_id); + if (l3c_pmu->topo.sub_id >= 0) + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_l3c%d_%d", + l3c_pmu->topo.sccl_id, l3c_pmu->topo.ccl_id, + l3c_pmu->topo.sub_id); + else + name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "hisi_sccl%d_l3c%d", + l3c_pmu->topo.sccl_id, l3c_pmu->topo.ccl_id); if (!name) return -ENOMEM; @@ -604,6 +868,7 @@ static int hisi_l3c_pmu_remove(struct platform_device *pdev) static const struct acpi_device_id hisi_l3c_pmu_acpi_match[] = { { "HISI0213", (kernel_ulong_t)&hisi_l3c_pmu_v1 }, { "HISI0214", (kernel_ulong_t)&hisi_l3c_pmu_v2 }, + { "HISI0215", (kernel_ulong_t)&hisi_l3c_pmu_v3 }, {} }; MODULE_DEVICE_TABLE(acpi, hisi_l3c_pmu_acpi_match); @@ -618,14 +883,55 @@ static struct platform_driver hisi_l3c_pmu_driver = { .remove = hisi_l3c_pmu_remove, }; +static int hisi_l3c_pmu_online_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct hisi_pmu *l3c_pmu = hlist_entry_safe(node, struct hisi_pmu, node); + struct hisi_l3c_pmu *hisi_l3c_pmu; + int ret; + + hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); + + /* + * Invoking the framework's online function for doing the core logic + * of CPU, interrupt and perf context migrating. Then return directly + * if we don't support L3C_PMU_FEAT_EXT. Otherwise migrate the ext_irq + * using the migrated CPU. + * + * Same logic for CPU offline. + */ + ret = hisi_uncore_pmu_online_cpu(cpu, node); + if (!(hisi_l3c_pmu->feature & L3C_PMU_FEAT_EXT) || + l3c_pmu->on_cpu >= nr_cpu_ids) + return ret; + + WARN_ON(irq_set_affinity(hisi_l3c_pmu->ext_irq, cpumask_of(l3c_pmu->on_cpu))); + return ret; +} + +static int hisi_l3c_pmu_offline_cpu(unsigned int cpu, struct hlist_node *node) +{ + struct hisi_pmu *l3c_pmu = hlist_entry_safe(node, struct hisi_pmu, node); + struct hisi_l3c_pmu *hisi_l3c_pmu; + int ret; + + hisi_l3c_pmu = to_hisi_l3c_pmu(l3c_pmu); + ret = hisi_uncore_pmu_offline_cpu(cpu, node); + if (!(hisi_l3c_pmu->feature & L3C_PMU_FEAT_EXT) || + l3c_pmu->on_cpu >= nr_cpu_ids) + return ret; + + WARN_ON(irq_set_affinity(hisi_l3c_pmu->ext_irq, cpumask_of(l3c_pmu->on_cpu))); + return ret; +} + static int __init hisi_l3c_pmu_module_init(void) { int ret; ret = cpuhp_setup_state_multi(CPUHP_AP_PERF_ARM_HISI_L3_ONLINE, "AP_PERF_ARM_HISI_L3_ONLINE", - hisi_uncore_pmu_online_cpu, - hisi_uncore_pmu_offline_cpu); + hisi_l3c_pmu_online_cpu, + hisi_l3c_pmu_offline_cpu); if (ret) { pr_err("L3C PMU: Error setup hotplug, ret = %d\n", ret); return ret; From 9366dbc2535c256432348a1ee9cb940980ba05f6 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Mon, 16 Jun 2025 18:28:31 +0800 Subject: [PATCH 068/227] drivers/perf: hisi: Clarifying event names and fix event ID for pa_pmu driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICFG22 ---------------------------------------------------------------------- The rx_req and tx_req events were only counting the number of flit package requests for link0. Hisilicon PA supports 4 links (0-3), and the original event names could be misinterpreted as counting the total flit packages across all links. Additionally, the tx_req event ID was incorrect. Fixes this by: 1) Adding the "link0" suffix and "pa" prefix to the event names to clearly indicate that they are specific to link0 and belong to the PA PMU. 2) Updating the tx_req event ID to 0x60 to reflect the correct identifier. These changes ensure accurate event naming and counting for link0 and flit packages. Fixes: a0ab25cd82ee ("drivers/perf: hisi: Add support for HiSilicon PA PMU driver") Signed-off-by: Junhao He Signed-off-by: Yushan Wang Signed-off-by: Qizhi Zhang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c index 7c95d116b79fb..680ebc429001c 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -320,8 +320,8 @@ static const struct attribute_group hisi_pa_pmu_v2_format_group = { }; static struct attribute *hisi_pa_pmu_v2_events_attr[] = { - HISI_PMU_EVENT_ATTR(rx_req, 0x40), - HISI_PMU_EVENT_ATTR(tx_req, 0x5c), + HISI_PMU_EVENT_ATTR(pa_rx_req_link0, 0x40), + HISI_PMU_EVENT_ATTR(pa_tx_req_link0, 0x60), HISI_PMU_EVENT_ATTR(cycle, 0x78), NULL }; From 1cb40c3ef03b97988bc70623213dc07fa6e7841a Mon Sep 17 00:00:00 2001 From: Yushan Wang Date: Mon, 16 Jun 2025 18:47:47 +0800 Subject: [PATCH 069/227] perf: Remove unstable events for uncore L3C PMU driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICFG4K ---------------------------------------------------------------------- Currently when taking event 0x80, 0x83, the value of PMU counters will be unstable if the value overflows and cause IRQs. Remove these events in sysfs then. Fixes: 89711c8962cc ("drivers/perf: hisi: Add support for L3C PMU v3") Signed-off-by: Yushan Wang Signed-off-by: Qizhi Zhang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c index f4afd8c7bf379..6950ccfa0a33e 100644 --- a/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_l3c_pmu.c @@ -651,8 +651,6 @@ HISI_L3C_PMU_EVENT_ATTR(io_rd_hit_spipe, 0x1d, false); HISI_L3C_PMU_EVENT_ATTR(io_wr_spipe, 0x1e, false); HISI_L3C_PMU_EVENT_ATTR(io_wr_hit_spipe, 0x1f, false); HISI_L3C_PMU_EVENT_ATTR(cycles, 0x7f, false); -HISI_L3C_PMU_EVENT_ATTR(l3t_comp_sum, 0x80, false); -HISI_L3C_PMU_EVENT_ATTR(l3t_rdnotram, 0x83, true); HISI_L3C_PMU_EVENT_ATTR(l3c_ref, 0xbc, false); HISI_L3C_PMU_EVENT_ATTR(l3c2ring, 0xbd, true); @@ -675,8 +673,6 @@ static struct attribute *hisi_l3c_pmu_v3_events_attr[] = { &hisi_l3c_io_wr_spipe_attr.attr.attr, &hisi_l3c_io_wr_hit_spipe_attr.attr.attr, &hisi_l3c_cycles_attr.attr.attr, - &hisi_l3c_l3t_comp_sum_attr.attr.attr, - &hisi_l3c_l3t_rdnotram_attr.attr.attr, &hisi_l3c_l3c_ref_attr.attr.attr, &hisi_l3c_l3c2ring_attr.attr.attr, NULL From 7055e9c590f525788de49c8ea40f94dd9aea2d55 Mon Sep 17 00:00:00 2001 From: Junhao He Date: Mon, 16 Jun 2025 21:25:59 +0800 Subject: [PATCH 070/227] drivers/perf: hisi: Fixes the incorrect bitmask limit for the CPA event sysfs interface driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICFG6F ---------------------------------------------------------------------- However, when users input events in the range of 0x1ff to 0xffff, the driver does not return an error. Therefore, the bitmask limit should be adjusted to bitmask(0-7) to ensure proper functionality. before the patch: [root@localhost ~]# perf stat -e hisi_sicl0_cpa0/event=0x1FF/ sleep 1 Performance counter stats for 'system wide': hisi_sicl0_cpa0/event=0x1FF/ after the patch: [root@localhost ~]# perf stat -e hisi_sicl0_cpa0/event=0x1FF/ sleep 1 event syntax error: '..cpa0/event=0x1FF/' \___ value too big for format, maximum is 255 Fixes: 6b79738b6ed9 ("drivers/perf: hisi: Add Support for CPA PMU") Signed-off-by: Junhao He Signed-off-by: Yushan Wang Signed-off-by: Qizhi Zhang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c index 006afb4d12085..cfc3ed2a79af5 100644 --- a/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_cpa_pmu.c @@ -202,7 +202,7 @@ static int hisi_cpa_pmu_init_data(struct platform_device *pdev, } static struct attribute *hisi_cpa_pmu_format_attr[] = { - HISI_PMU_FORMAT_ATTR(event, "config:0-15"), + HISI_PMU_FORMAT_ATTR(event, "config:0-7"), NULL }; From 65f4bb49be9fb128950c4a94fc7a118a379d4e8b Mon Sep 17 00:00:00 2001 From: Junhao He Date: Tue, 17 Jun 2025 17:17:31 +0800 Subject: [PATCH 071/227] drivers/perf: hisi: Add events and rename event "cycle" for pa_pmu driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICFTY7 ---------------------------------------------------------------------- 1) Add events to count the number of request flit packages for links 1-3 and data flit packages for all links (0-3). 2) rename event "cycle" to pa_cycles to clearly indicate that it belongs to the PA PMU Fixes: a0ab25cd82ee ("drivers/perf: hisi: Add support for HiSilicon PA PMU driver") Signed-off-by: Junhao He Signed-off-by: Yushan Wang Signed-off-by: Qizhi Zhang Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/hisilicon/hisi_uncore_pa_pmu.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c index 680ebc429001c..10e10179a13fd 100644 --- a/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c +++ b/drivers/perf/hisilicon/hisi_uncore_pa_pmu.c @@ -321,8 +321,22 @@ static const struct attribute_group hisi_pa_pmu_v2_format_group = { static struct attribute *hisi_pa_pmu_v2_events_attr[] = { HISI_PMU_EVENT_ATTR(pa_rx_req_link0, 0x40), + HISI_PMU_EVENT_ATTR(pa_rx_req_link1, 0x41), + HISI_PMU_EVENT_ATTR(pa_rx_req_link2, 0x42), + HISI_PMU_EVENT_ATTR(pa_rx_req_link3, 0x43), + HISI_PMU_EVENT_ATTR(pa_rx_data_link0, 0x44), + HISI_PMU_EVENT_ATTR(pa_rx_data_link1, 0x45), + HISI_PMU_EVENT_ATTR(pa_rx_data_link2, 0x46), + HISI_PMU_EVENT_ATTR(pa_rx_data_link3, 0x47), HISI_PMU_EVENT_ATTR(pa_tx_req_link0, 0x60), - HISI_PMU_EVENT_ATTR(cycle, 0x78), + HISI_PMU_EVENT_ATTR(pa_tx_req_link1, 0x61), + HISI_PMU_EVENT_ATTR(pa_tx_req_link2, 0x62), + HISI_PMU_EVENT_ATTR(pa_tx_req_link3, 0x63), + HISI_PMU_EVENT_ATTR(pa_tx_data_link0, 0x64), + HISI_PMU_EVENT_ATTR(pa_tx_data_link1, 0x65), + HISI_PMU_EVENT_ATTR(pa_tx_data_link2, 0x66), + HISI_PMU_EVENT_ATTR(pa_tx_data_link3, 0x67), + HISI_PMU_EVENT_ATTR(pa_cycles, 0x78), NULL }; From 4906e7134f116ae7bb9799e8ae579d32cc5e5f56 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 8 May 2025 15:42:21 +0800 Subject: [PATCH 072/227] Add gpio-hisi softdepend driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC6B79 CVE: NA -------------------------------- Add gpio-hisi for module soft depend to ensure gpio driver is loaded fist and I2C driver can obtain bus recovery gpio pin resources. Fixes: bdc031294c4d ("i2c: hisi: Add gpio bus recovery support") Signed-off-by: Devyn Liu Signed-off-by: lujunhua --- drivers/i2c/busses/i2c-hisi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index e13e4b3f59f91..2d5892066a704 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -613,3 +613,4 @@ module_platform_driver(hisi_i2c_driver); MODULE_AUTHOR("Yicong Yang "); MODULE_DESCRIPTION("HiSilicon I2C Controller Driver"); MODULE_LICENSE("GPL"); +MODULE_SOFTDEP("pre: gpio-hisi"); From 41055ae12341ebbde653b327416d88f0bed8ab55 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 28 Mar 2024 18:55:52 +0800 Subject: [PATCH 073/227] i2c: hisi: Add I2C controller reset and initialization proccess in bus recovery action driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9KYFI CVE: NA ----------------------------------------------- The I2C controller hardware manual specifies the bus recovery operation for I2C. It is recommended to reset the I2C controller and reinitialize the I2C bus configuration in bus recovery operation, which can more effectively ensure the recovery of the I2C bus. So we add I2C controller reset and initialization proccess in bus recovery action. Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/i2c/busses/i2c-hisi.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index 2d5892066a704..24672f425e13c 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -461,6 +461,26 @@ static void hisi_i2c_configure_bus(struct hisi_i2c_controller *ctlr) #ifdef CONFIG_ACPI #define HISI_I2C_PIN_MUX_METHOD "PMUX" +#define HISI_I2C_SOFT_RESET_METHOD "SRST" + +/** + * i2c_hisi_soft_reset - Do I2C master soft reset method through ACPI + * @dev: device need to be reset + * + * The function invokes the specific ACPI method "SRST" for trigger a soft + * reset of I2C controller in order to help on I2C controller recover from + * the abnormal state after bus recovery process. + */ +static void i2c_hisi_soft_reset(struct device *dev) +{ + acpi_handle handle = ACPI_HANDLE(dev); + acpi_status status; + unsigned long long data; + + status = acpi_evaluate_integer(handle, HISI_I2C_SOFT_RESET_METHOD, NULL, &data); + dev_info(dev, "I2C controller reset %s", ACPI_FAILURE(status) ? "failed" : + "succeed"); +} /** * i2c_dw_acpi_pin_mux_change - Change the I2C controller's pin mux through ACPI @@ -498,6 +518,13 @@ static void i2c_hisi_unprepare_recovery(struct i2c_adapter *adap) struct hisi_i2c_controller *ctlr = i2c_get_adapdata(adap); i2c_hisi_pin_mux_change(ctlr->dev, false); + i2c_hisi_soft_reset(ctlr->dev); + + /* + * After a soft reset, the device configuration return to default + * values and require reinitialization. + */ + hisi_i2c_configure_bus(ctlr); } static void hisi_i2c_init_recovery_info(struct hisi_i2c_controller *ctlr) @@ -509,7 +536,8 @@ static void hisi_i2c_init_recovery_info(struct hisi_i2c_controller *ctlr) if (acpi_disabled) return; - if (!adev || !acpi_has_method(adev->handle, HISI_I2C_PIN_MUX_METHOD)) + if (!adev || !acpi_has_method(adev->handle, HISI_I2C_PIN_MUX_METHOD) || + !acpi_has_method(adev->handle, HISI_I2C_SOFT_RESET_METHOD)) return; gpio = devm_gpiod_get_optional(ctlr->dev, "scl", GPIOD_OUT_HIGH); From f67733cb7afd954aa494f92347595e7052873e9a Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Sun, 28 Apr 2024 18:35:06 +0800 Subject: [PATCH 074/227] i2c: hisi: Correct the description comment for PIN_MUX METHOD driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9KYFI CVE: NA ----------------------------------------------- Correct the description comment for PIN_MUX METHOD and rename it to hisi instead of dw. Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/i2c/busses/i2c-hisi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-hisi.c b/drivers/i2c/busses/i2c-hisi.c index 24672f425e13c..b5a659b8b0a12 100644 --- a/drivers/i2c/busses/i2c-hisi.c +++ b/drivers/i2c/busses/i2c-hisi.c @@ -483,7 +483,7 @@ static void i2c_hisi_soft_reset(struct device *dev) } /** - * i2c_dw_acpi_pin_mux_change - Change the I2C controller's pin mux through ACPI + * i2c_hisi_pin_mux_change - Change the I2C controller's pin mux through ACPI * @dev: device owns the SCL/SDA pin * @to_gpio: true to switch to GPIO, false to switch to SCL/SDA * From f5d1be445b7769715604d751878d2ed114452526 Mon Sep 17 00:00:00 2001 From: Lino Sanfilippo Date: Thu, 1 Feb 2024 12:36:45 +0100 Subject: [PATCH 075/227] tpm,tpm_tis: Avoid warning splat at shutdown mainline inclusion from mainline-v6.9-rc1 commit b7ab4bbd0188f3985b821fa09456b11105a8dedf category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9G5IU CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b7ab4bbd0188f3985b821fa09456b11105a8dedf ---------------------------------------------------------------------- If interrupts are not activated the work struct 'free_irq_work' is not initialized. This results in a warning splat at module shutdown. Fix this by always initializing the work regardless of whether interrupts are activated or not. cc: stable@vger.kernel.org Fixes: 481c2d14627d ("tpm,tpm_tis: Disable interrupts after 1000 unhandled IRQs") Reported-by: Jarkko Sakkinen Closes: https://lore.kernel.org/all/CX32RFOMJUQ0.3R4YCL9MDCB96@kernel.org/ Signed-off-by: Lino Sanfilippo Signed-off-by: Jarkko Sakkinen Signed-off-by: lujunhua --- drivers/char/tpm/tpm_tis_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index b3452259d6e0b..6dde52c4f1d67 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -779,7 +779,6 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int rc; u32 int_status; - rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL, tis_int_handler, IRQF_ONESHOT | flags, dev_name(&chip->dev), chip); @@ -989,6 +988,7 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, priv->phy_ops = phy_ops; priv->locality_count = 0; mutex_init(&priv->locality_count_mutex); + INIT_WORK(&priv->free_irq_work, tpm_tis_free_irq_func); dev_set_drvdata(&chip->dev, priv); From 6569f89551db6f6898598feab2c65177ccdab081 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Wed, 10 Apr 2024 10:02:03 +0800 Subject: [PATCH 076/227] spi: hisi-kunpeng: Delete the dump interface of data registers in debugfs maillist inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9KP0Z CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git/commit/?h=for-6.9&id=7430764f5a85d30314aeef2d5438dff1fb0b1d68 ------------------------------------------ Due to the reading of FIFO during the dump of data registers in debugfs, if SPI transmission is in progress, it will be affected and may result in transmission failure. Therefore, the dump interface of data registers in debugfs is removed. Fixes: 2b2142f247eb ("spi: hisi-kunpeng: Add debugfs support") Signed-off-by: Devyn Liu Reviewed-by: Jay Fang Signed-off-by: Junhua Lu --- drivers/spi/spi-hisi-kunpeng.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 525cc0143a305..54730e93fba45 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -151,8 +151,6 @@ static const struct debugfs_reg32 hisi_spi_regs[] = { HISI_SPI_DBGFS_REG("ENR", HISI_SPI_ENR), HISI_SPI_DBGFS_REG("FIFOC", HISI_SPI_FIFOC), HISI_SPI_DBGFS_REG("IMR", HISI_SPI_IMR), - HISI_SPI_DBGFS_REG("DIN", HISI_SPI_DIN), - HISI_SPI_DBGFS_REG("DOUT", HISI_SPI_DOUT), HISI_SPI_DBGFS_REG("SR", HISI_SPI_SR), HISI_SPI_DBGFS_REG("RISR", HISI_SPI_RISR), HISI_SPI_DBGFS_REG("ISR", HISI_SPI_ISR), From 3d4046e77377d0e4dde09f14c2430726ee737abf Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 9 May 2024 19:15:13 +0800 Subject: [PATCH 077/227] gpiolib: acpi: Fix failed in acpi_gpiochip_find() by adding parent node match driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I9KYEZ CVE: NA -------------------------------- Previous patch modified the standard used by acpi_gpiochip_find() to match device nodes. Using the device node set in gc->gpiodev->d- ev instead of gc->parent. However, there is a situation in gpio-dwapb where the GPIO device driver will set gc->fwnode for each port corresponding to a child node under a GPIO device, so gc->gpiodev->dev will be assigned the value of each child node in gpiochip_add_data(). When other drivers request GPIO pin resources through the GPIO device node provided by ACPI (corresponding to the parent node), the change of the matching object to gc->gpiodev->dev in acpi_gpiochip_find() only allows finding the value of each port (child node), resulting in a failed request. Reapply the condition of using gc->parent for match in acpi_gpio- chip_find() in the code can compatible with the problem of gpio-dwapb, and will not affect the two cases mentioned in the patch: 1. There is no setting for gc->fwnode. 2. The case that depends on using gc->fwnode for match. Fixes: 5062e4c14b75 ("gpiolib: acpi: use the fwnode in acpi_gpiochip_find()") Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/gpio/gpiolib-acpi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 27e3fb9938049..44e8438206da2 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -95,10 +95,8 @@ static bool acpi_gpio_deferred_req_irqs_done; static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) { - if (!gc->parent) - return false; - - return ACPI_HANDLE(gc->parent) == data; + return device_match_acpi_handle(&gc->gpiodev->dev, data) || + (gc->parent && device_match_acpi_handle(gc->parent, data)); } /** From 2855ab894645bad15836122532aed6c66d5503bd Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 11 Apr 2024 20:54:59 +0800 Subject: [PATCH 078/227] spi: hisi-kunpeng: Add validation for the minimum value of speed_hz driver inclusion category: cleanup bugzilla: https://gitee.com/openeuler/kernel/issues/I9O7EH CVE: NA -------------------------------- We get the configuration of max_frequency from the firmware, which is used to calculate the clk_div together with the set speed. If the speed set by the user is too small, it may cause the clk_div to be too large to exceed the variable range. Therefore, we limit the minimum value of the speed configuration to the value obtained by (master->max_speed_hz/CLK_DIV_MAX). Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/spi/spi-hisi-kunpeng.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 54730e93fba45..5dbf71ee8281b 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -495,6 +495,7 @@ static int hisi_spi_probe(struct platform_device *pdev) master->transfer_one = hisi_spi_transfer_one; master->handle_err = hisi_spi_handle_err; master->dev.fwnode = dev->fwnode; + master->min_speed_hz = DIV_ROUND_UP(host->max_speed_hz, CLK_DIV_MAX); hisi_spi_hw_init(hs); From 27a7f2282cd86e6456e74389f04d62cff6fdc837 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Thu, 11 Apr 2024 21:27:44 +0800 Subject: [PATCH 079/227] spi: Add verification for the max_frequency provided by the firmware driver inclusion category: cleanup bugzilla: https://gitee.com/openeuler/kernel/issues/I9O7EH CVE: NA -------------------------------- Add verification for the max_frequency provided by the firmware. If the value is 0, the parameter is regarded as invalid and an error code is returned. Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/spi/spi-hisi-kunpeng.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 5dbf71ee8281b..6d862b10328ba 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -481,6 +481,12 @@ static int hisi_spi_probe(struct platform_device *pdev) return -EINVAL; } + if (host->max_speed_hz == 0) { + dev_err(dev, "invalid max SPI clocking speed, max-freq=%u\n", + host->max_speed_hz); + return -EINVAL; + } + ret = device_property_read_u16(dev, "num-cs", &master->num_chipselect); if (ret) From e1e0b06324842c0cda81df1121ff3c80f080b4f8 Mon Sep 17 00:00:00 2001 From: hbmm Date: Wed, 2 Jul 2025 15:55:13 +0800 Subject: [PATCH 080/227] Revert "spi: Add verification for the max_frequency provided by the firmware" This reverts commit 8c9aeaf8e93de629208374d05813546a9b3a2e44. --- drivers/spi/spi-hisi-kunpeng.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 6d862b10328ba..5dbf71ee8281b 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -481,12 +481,6 @@ static int hisi_spi_probe(struct platform_device *pdev) return -EINVAL; } - if (host->max_speed_hz == 0) { - dev_err(dev, "invalid max SPI clocking speed, max-freq=%u\n", - host->max_speed_hz); - return -EINVAL; - } - ret = device_property_read_u16(dev, "num-cs", &master->num_chipselect); if (ret) From 6d53a106f5037281a87c513ce6a286f16d917dd1 Mon Sep 17 00:00:00 2001 From: hbmm Date: Wed, 2 Jul 2025 16:06:12 +0800 Subject: [PATCH 081/227] spi: Add verification for the max_frequency provided by the firmware driver inclusion category: cleanup bugzilla: https://gitee.com/openeuler/kernel/issues/I9O7EH CVE: NA -------------------------------- Add verification for the max_frequency provided by the firmware. If the value is 0, the parameter is regarded as invalid and an error code is returned. Signed-off-by: Devyn Liu Signed-off-by: Junhua Lu --- drivers/spi/spi-hisi-kunpeng.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-hisi-kunpeng.c b/drivers/spi/spi-hisi-kunpeng.c index 5dbf71ee8281b..87b162db475e3 100644 --- a/drivers/spi/spi-hisi-kunpeng.c +++ b/drivers/spi/spi-hisi-kunpeng.c @@ -481,6 +481,12 @@ static int hisi_spi_probe(struct platform_device *pdev) return -EINVAL; } + if (master->max_speed_hz == 0) { + dev_err(dev, "invalid max SPI clocking speed, max-freq=%u\n", + master->max_speed_hz); + return -EINVAL; + } + ret = device_property_read_u16(dev, "num-cs", &master->num_chipselect); if (ret) @@ -495,7 +501,7 @@ static int hisi_spi_probe(struct platform_device *pdev) master->transfer_one = hisi_spi_transfer_one; master->handle_err = hisi_spi_handle_err; master->dev.fwnode = dev->fwnode; - master->min_speed_hz = DIV_ROUND_UP(host->max_speed_hz, CLK_DIV_MAX); + master->min_speed_hz = DIV_ROUND_UP(master->max_speed_hz, CLK_DIV_MAX); hisi_spi_hw_init(hs); From c7ffeb5fa1e015ead36cda8c1e998b9be4df3fa1 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Wed, 19 Apr 2023 10:33:42 +0200 Subject: [PATCH 082/227] ACPI: thermal: Move to dedicated function sysfs extra attr creation The ACPI thermal driver creates extra sysfs attributes in its own directory pointing to the thermal zone it is related to and add a pointer to the sysfs ACPI thermal device from the thermal zone sysfs entry. This is very specific to this ACPI thermal driver, let's encapsulate the related creation/deletion code to group it inside a function we can identify later for removal if needed. Signed-off-by: Daniel Lezcano [ rjw: Subject adjustment, removal of trailing white space ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal.c | 45 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 809e12b941235..7e24554fe00f3 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -783,6 +783,32 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = { .critical = acpi_thermal_zone_device_critical, }; +static int acpi_thermal_zone_sysfs_add(struct acpi_thermal *tz) +{ + struct device *tzdev = thermal_zone_device(tz->thermal_zone); + int ret; + + ret = sysfs_create_link(&tz->device->dev.kobj, + &tzdev->kobj, "thermal_zone"); + if (ret) + return ret; + + ret = sysfs_create_link(&tzdev->kobj, + &tz->device->dev.kobj, "device"); + if (ret) + sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); + + return ret; +} + +static void acpi_thermal_zone_sysfs_remove(struct acpi_thermal *tz) +{ + struct device *tzdev = thermal_zone_device(tz->thermal_zone); + + sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); + sysfs_remove_link(&tzdev->kobj, "device"); +} + static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) { int trips = 0; @@ -816,21 +842,15 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (IS_ERR(tz->thermal_zone)) return -ENODEV; - result = sysfs_create_link(&tz->device->dev.kobj, - &tz->thermal_zone->device.kobj, "thermal_zone"); + result = acpi_thermal_zone_sysfs_add(tz); if (result) goto unregister_tzd; - result = sysfs_create_link(&tz->thermal_zone->device.kobj, - &tz->device->dev.kobj, "device"); - if (result) - goto remove_tz_link; - status = acpi_bus_attach_private_data(tz->device->handle, tz->thermal_zone); if (ACPI_FAILURE(status)) { result = -ENODEV; - goto remove_dev_link; + goto remove_links; } result = thermal_zone_device_enable(tz->thermal_zone); @@ -844,10 +864,15 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) acpi_bus_detach: acpi_bus_detach_private_data(tz->device->handle); +<<<<<<< HEAD remove_dev_link: sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); remove_tz_link: sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); +======= +remove_links: + acpi_thermal_zone_sysfs_remove(tz); +>>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) unregister_tzd: thermal_zone_device_unregister(tz->thermal_zone); @@ -856,8 +881,12 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) { +<<<<<<< HEAD sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); +======= + acpi_thermal_zone_sysfs_remove(tz); +>>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) thermal_zone_device_unregister(tz->thermal_zone); tz->thermal_zone = NULL; acpi_bus_detach_private_data(tz->device->handle); From 23d2daf21f85447f43dd4a99ad8706322bc3138b Mon Sep 17 00:00:00 2001 From: hbmm Date: Thu, 3 Jul 2025 11:26:41 +0800 Subject: [PATCH 083/227] Revert "ACPI: thermal: Move to dedicated function sysfs extra attr creation" This reverts commit e9897b65a72dd007cb522d788df1550018e8e88d. --- drivers/acpi/thermal.c | 45 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 7e24554fe00f3..809e12b941235 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -783,32 +783,6 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = { .critical = acpi_thermal_zone_device_critical, }; -static int acpi_thermal_zone_sysfs_add(struct acpi_thermal *tz) -{ - struct device *tzdev = thermal_zone_device(tz->thermal_zone); - int ret; - - ret = sysfs_create_link(&tz->device->dev.kobj, - &tzdev->kobj, "thermal_zone"); - if (ret) - return ret; - - ret = sysfs_create_link(&tzdev->kobj, - &tz->device->dev.kobj, "device"); - if (ret) - sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); - - return ret; -} - -static void acpi_thermal_zone_sysfs_remove(struct acpi_thermal *tz) -{ - struct device *tzdev = thermal_zone_device(tz->thermal_zone); - - sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); - sysfs_remove_link(&tzdev->kobj, "device"); -} - static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) { int trips = 0; @@ -842,15 +816,21 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (IS_ERR(tz->thermal_zone)) return -ENODEV; - result = acpi_thermal_zone_sysfs_add(tz); + result = sysfs_create_link(&tz->device->dev.kobj, + &tz->thermal_zone->device.kobj, "thermal_zone"); if (result) goto unregister_tzd; + result = sysfs_create_link(&tz->thermal_zone->device.kobj, + &tz->device->dev.kobj, "device"); + if (result) + goto remove_tz_link; + status = acpi_bus_attach_private_data(tz->device->handle, tz->thermal_zone); if (ACPI_FAILURE(status)) { result = -ENODEV; - goto remove_links; + goto remove_dev_link; } result = thermal_zone_device_enable(tz->thermal_zone); @@ -864,15 +844,10 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) acpi_bus_detach: acpi_bus_detach_private_data(tz->device->handle); -<<<<<<< HEAD remove_dev_link: sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); remove_tz_link: sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); -======= -remove_links: - acpi_thermal_zone_sysfs_remove(tz); ->>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) unregister_tzd: thermal_zone_device_unregister(tz->thermal_zone); @@ -881,12 +856,8 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) { -<<<<<<< HEAD sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); -======= - acpi_thermal_zone_sysfs_remove(tz); ->>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) thermal_zone_device_unregister(tz->thermal_zone); tz->thermal_zone = NULL; acpi_bus_detach_private_data(tz->device->handle); From ba930d974f0bd8178fa20775495af3564cf852e7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Dec 2021 17:37:10 +0100 Subject: [PATCH 084/227] ACPI: Use acpi_fetch_acpi_dev() instead of acpi_bus_get_device() Modify the ACPI code to use acpi_fetch_acpi_dev() instead of acpi_bus_get_device() where applicable. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede --- drivers/acpi/acpi_video.c | 5 ++--- drivers/acpi/device_pm.c | 31 +++++++++++++------------------ drivers/acpi/dock.c | 5 ++--- drivers/acpi/pci_link.c | 12 ++++-------- drivers/acpi/pci_root.c | 10 ++++------ drivers/acpi/power.c | 7 +++---- drivers/acpi/processor_driver.c | 10 +++++++--- drivers/acpi/processor_idle.c | 2 +- drivers/acpi/property.c | 11 +++++------ drivers/acpi/resource.c | 4 ++-- drivers/acpi/thermal.c | 9 ++++----- drivers/acpi/video_detect.c | 6 ++---- drivers/acpi/x86/s2idle.c | 4 ++-- 13 files changed, 51 insertions(+), 65 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index c8eb69d3e1d61..e87f4bf82d8b0 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1765,13 +1765,12 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context, { struct acpi_device *device = context; struct acpi_device *sibling; - int result; if (handle == device->handle) return AE_CTRL_TERMINATE; - result = acpi_bus_get_device(handle, &sibling); - if (result) + sibling = acpi_fetch_acpi_dev(handle); + if (!sibling) return AE_OK; if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 0028b6b51c877..60a8d7a5ff03f 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -285,14 +285,12 @@ EXPORT_SYMBOL(acpi_device_set_power); int acpi_bus_set_power(acpi_handle handle, int state) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - if (result) - return result; + if (device) + return acpi_device_set_power(device, state); - return acpi_device_set_power(device, state); + return -ENODEV; } EXPORT_SYMBOL(acpi_bus_set_power); @@ -410,21 +408,20 @@ EXPORT_SYMBOL_GPL(acpi_device_update_power); int acpi_bus_update_power(acpi_handle handle, int *state_p) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? result : acpi_device_update_power(device, state_p); + if (device) + return acpi_device_update_power(device, state_p); + + return -ENODEV; } EXPORT_SYMBOL_GPL(acpi_bus_update_power); bool acpi_bus_power_manageable(acpi_handle handle) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? false : device->flags.power_manageable; + return device && device->flags.power_manageable; } EXPORT_SYMBOL(acpi_bus_power_manageable); @@ -543,11 +540,9 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) bool acpi_bus_can_wakeup(acpi_handle handle) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? false : device->wakeup.flags.valid; + return device && device->wakeup.flags.valid; } EXPORT_SYMBOL(acpi_bus_can_wakeup); diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 7cf92158008fa..ece9109db0586 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -489,10 +489,9 @@ static ssize_t docked_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dock_station *dock_station = dev->platform_data; - struct acpi_device *adev = NULL; + struct acpi_device *adev = acpi_fetch_acpi_dev(dock_station->handle); - acpi_bus_get_device(dock_station->handle, &adev); - return snprintf(buf, PAGE_SIZE, "%u\n", acpi_device_enumerated(adev)); + return sysfs_emit(buf, "%u\n", acpi_device_enumerated(adev)); } static DEVICE_ATTR_RO(docked); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index cb7b900d9466c..d54fb8e54671d 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -606,12 +606,10 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link) int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name) { - int result; - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_link *link; - result = acpi_bus_get_device(handle, &device); - if (result) { + if (!device) { acpi_handle_err(handle, "Invalid link device\n"); return -1; } @@ -658,12 +656,10 @@ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, */ int acpi_pci_link_free_irq(acpi_handle handle) { - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_link *link; - acpi_status result; - result = acpi_bus_get_device(handle, &device); - if (result) { + if (!device) { acpi_handle_err(handle, "Invalid link device\n"); return -1; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 223aa010dd8da..d85b619108dae 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -65,11 +65,10 @@ static struct acpi_scan_handler pci_root_handler = { */ int acpi_is_root_bridge(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); int ret; - struct acpi_device *device; - ret = acpi_bus_get_device(handle, &device); - if (ret) + if (!device) return 0; ret = acpi_match_device_ids(device, root_device_ids); @@ -226,11 +225,10 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags) struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_root *root; - struct acpi_device *device; - if (acpi_bus_get_device(handle, &device) || - acpi_match_device_ids(device, root_device_ids)) + if (!device || acpi_match_device_ids(device, root_device_ids)) return NULL; root = acpi_driver_data(device); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index c95eedd58f5bf..fe22366b4b385 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -81,9 +81,9 @@ struct acpi_power_resource *to_power_resource(struct acpi_device *device) static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - if (acpi_bus_get_device(handle, &device)) + if (!device) return NULL; return to_power_resource(device); @@ -914,14 +914,13 @@ static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource struct acpi_device *acpi_add_power_resource(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_power_resource *resource; - struct acpi_device *device = NULL; union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; acpi_status status; int result; - acpi_bus_get_device(handle, &device); if (device) return device; diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 77541f939be3e..368a9edefd0cb 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -98,8 +98,13 @@ static int acpi_soft_cpu_online(unsigned int cpu) struct acpi_processor *pr = per_cpu(processors, cpu); struct acpi_device *device; - if (!pr || acpi_bus_get_device(pr->handle, &device)) + if (!pr) + return 0; + + device = acpi_fetch_acpi_dev(pr->handle); + if (!device) return 0; + /* * CPU got physically hotplugged and onlined for the first time: * Initialize missing things. @@ -125,9 +130,8 @@ static int acpi_soft_cpu_online(unsigned int cpu) static int acpi_soft_cpu_dead(unsigned int cpu) { struct acpi_processor *pr = per_cpu(processors, cpu); - struct acpi_device *device; - if (!pr || acpi_bus_get_device(pr->handle, &device)) + if (!pr || !acpi_fetch_acpi_dev(pr->handle)) return 0; acpi_processor_reevaluate_tstate(pr, true); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 5fbb435b58122..756ffc9d10b7c 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1132,7 +1132,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) status = acpi_get_parent(handle, &pr_ahandle); while (ACPI_SUCCESS(status)) { - acpi_bus_get_device(pr_ahandle, &d); + d = acpi_fetch_acpi_dev(pr_ahandle); handle = pr_ahandle; if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 9ab7f7184343a..617e52dc415d7 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -694,9 +694,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (index) return -ENOENT; - ret = acpi_bus_get_device(obj->reference.handle, &device); - if (ret) - return ret == -ENODEV ? -EINVAL : ret; + device = acpi_fetch_acpi_dev(obj->reference.handle); + if (!device) + return -EINVAL; if (!args) return 0; @@ -729,9 +729,8 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (element->type == ACPI_TYPE_LOCAL_REFERENCE) { struct fwnode_handle *ref_fwnode; - ret = acpi_bus_get_device(element->reference.handle, - &device); - if (ret) + device = acpi_fetch_acpi_dev(element->reference.handle); + if (!device) return -EINVAL; nargs = 0; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 57f34aa4f4295..27ea1317e33a6 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -982,9 +982,9 @@ static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth, { struct resource *res = context; struct acpi_device **consumer = (struct acpi_device **) ret; - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); - if (acpi_bus_get_device(handle, &adev)) + if (!adev) return AE_OK; if (acpi_dev_consumes_res(adev, res)) { diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 809e12b941235..5482e28c5bc68 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -693,7 +693,6 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; struct acpi_device *dev; - acpi_status status; acpi_handle handle; int i; int j; @@ -711,8 +710,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, for (i = 0; i < tz->trips.passive.devices.count; i++) { handle = tz->trips.passive.devices.handles[i]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_FAILURE(status) || dev != device) + dev = acpi_fetch_acpi_dev(handle); + if (dev != device) continue; if (bind) result = @@ -737,8 +736,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, j < tz->trips.active[i].devices.count; j++) { handle = tz->trips.active[i].devices.handles[j]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_FAILURE(status) || dev != device) + dev = acpi_fetch_acpi_dev(handle); + if (dev != device) continue; if (bind) result = thermal_zone_bind_cooling_device diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index a5cb9e1d48bcc..d55c143a49d7c 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -59,18 +59,16 @@ static void acpi_video_parse_cmdline(void) static acpi_status find_video(acpi_handle handle, u32 lvl, void *context, void **rv) { + struct acpi_device *acpi_dev = acpi_fetch_acpi_dev(handle); long *cap = context; struct pci_dev *dev; - struct acpi_device *acpi_dev; static const struct acpi_device_id video_ids[] = { {ACPI_VIDEO_HID, 0}, {"", 0}, }; - if (acpi_bus_get_device(handle, &acpi_dev)) - return AE_OK; - if (!acpi_match_device_ids(acpi_dev, video_ids)) { + if (acpi_dev && !acpi_match_device_ids(acpi_dev, video_ids)) { dev = acpi_get_pci_dev(handle); if (!dev) return AE_OK; diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index ada989670d9b8..ee3deea964fd4 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -294,9 +294,9 @@ static void lpi_check_constraints(void) for (i = 0; i < lpi_constraints_table_size; ++i) { acpi_handle handle = lpi_constraints_table[i].handle; - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); - if (!handle || acpi_bus_get_device(handle, &adev)) + if (!adev) continue; acpi_handle_debug(handle, From f217cfd7bcc8da5a218c531dbf9046ff863b7455 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 1 Apr 2025 17:08:49 +0800 Subject: [PATCH 085/227] hwmon: (acpi_power_meter) Fix fail to load module on platform without _PMD method mainline inclusion from mainline-v6.12-rc7 commit fabb1f813ec05975fd3428e72a62ef9f855fd3b4 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBVB4K CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=fabb1f813ec05975fd3428e72a62ef9f855fd3b4 --------------------------------------------------------------------- According to the ACPI specification, the _PMD method is optional. The acpi_power_meter driver shouldn't fail to load if the platform has no _PMD method. Signed-off-by: Huisong Li Message-ID: <20241112021228.22914-1-lihuisong@huawei.com> [groeck: Reworded commit description] Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao --- drivers/hwmon/acpi_power_meter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 1336f77106177..7b68a7d4bcb4f 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -682,8 +682,9 @@ static int setup_attrs(struct acpi_power_meter_resource *resource) { int res = 0; + /* _PMD method is optional. */ res = read_domain_devices(resource); - if (res) + if (res != -ENODEV) return res; if (resource->caps.flags & POWER_METER_CAN_MEASURE) { From 8f5f533f747dec5e8c7a921b19cfb8162b9924c9 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 1 Apr 2025 17:08:50 +0800 Subject: [PATCH 086/227] hwmon: (acpi_power_meter) Fix uninitialized variables mainline inclusion from mainline-v6.13-rc2 commit 7532e68f5d8f05353765d7585a791a14985d68b7 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBVB4K CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7532e68f5d8f05353765d7585a791a14985d68b7 --------------------------------------------------------------------- The 'power1_alarm' attribute uses the 'power' and 'cap' in the acpi_power_meter_resource structure. Currently, these two fields are just updated when user query 'power' and 'cap' attribute. If user directly query the 'power1_alarm' attribute without queryng above two attributes, driver will use uninitialized variables to judge. So this patch adds the setting of alarm state and update 'cap' in the notification callback and update 'power' and 'cap' if needed to show the real alarm state. Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250109081708.27366-2-lihuisong@huawei.com Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao --- drivers/hwmon/acpi_power_meter.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 7b68a7d4bcb4f..8793c6fddfce6 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -84,6 +84,7 @@ struct acpi_power_meter_resource { u64 power; u64 cap; u64 avg_interval; + bool power_alarm; int sensors_valid; unsigned long sensors_last_updated; struct sensor_device_attribute sensors[NUM_SENSORS]; @@ -396,6 +397,9 @@ static ssize_t show_val(struct device *dev, struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_power_meter_resource *resource = acpi_dev->driver_data; u64 val = 0; + int ret; + + guard(mutex)(&resource->lock); switch (attr->index) { case 0: @@ -423,10 +427,17 @@ static ssize_t show_val(struct device *dev, val = 0; break; case 6: - if (resource->power > resource->cap) - val = 1; - else - val = 0; + ret = update_meter(resource); + if (ret) + return ret; + /* need to update cap if not to support the notification. */ + if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) { + ret = update_cap(resource); + if (ret) + return ret; + } + val = resource->power_alarm || resource->power > resource->cap; + resource->power_alarm = resource->power > resource->cap; break; case 7: case 8: @@ -850,12 +861,20 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event) sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME); break; case METER_NOTIFY_CAP: + mutex_lock(&resource->lock); + res = update_cap(resource); + if (res) + dev_err_once(&device->dev, "update cap failed when capping value is changed.\n"); + mutex_unlock(&resource->lock); sysfs_notify(&device->dev.kobj, NULL, POWER_CAP_NAME); break; case METER_NOTIFY_INTERVAL: sysfs_notify(&device->dev.kobj, NULL, POWER_AVG_INTERVAL_NAME); break; case METER_NOTIFY_CAPPING: + mutex_lock(&resource->lock); + resource->power_alarm = true; + mutex_unlock(&resource->lock); sysfs_notify(&device->dev.kobj, NULL, POWER_ALARM_NAME); dev_info(&device->dev, "Capping in progress.\n"); break; From b2dd90e87575108fb8259eb067702f4f54f2d645 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 1 Apr 2025 17:08:51 +0800 Subject: [PATCH 087/227] hwmon: (acpi_power_meter) Fix update the power trip points on failure mainline inclusion from mainline-v6.13-rc2 commit 02f1a5911550bd6b39526d18282b10d441e04ed1 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBVB4K CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=02f1a5911550bd6b39526d18282b10d441e04ed1 -------------------------------------------------------------------- The power trip points maintained in local should not be updated when '_PTP' method fails to evaluate. Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250109081708.27366-3-lihuisong@huawei.com Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao --- drivers/hwmon/acpi_power_meter.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 8793c6fddfce6..85e02707a6d99 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -293,8 +293,8 @@ static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + unsigned long temp, trip_bk; int res; - unsigned long temp; res = kstrtoul(buf, 10, &temp); if (res) @@ -302,13 +302,15 @@ static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, temp = DIV_ROUND_CLOSEST(temp, 1000); - mutex_lock(&resource->lock); + guard(mutex)(&resource->lock); + + trip_bk = resource->trip[attr->index - 7]; resource->trip[attr->index - 7] = temp; res = set_acpi_trip(resource); - mutex_unlock(&resource->lock); - - if (res) + if (res) { + resource->trip[attr->index - 7] = trip_bk; return res; + } return count; } From 762963fb9bf29a10fb34c91a6ac66b51b23c8cbc Mon Sep 17 00:00:00 2001 From: Kazuhiro Abe Date: Tue, 1 Apr 2025 17:08:52 +0800 Subject: [PATCH 088/227] hwmon: (acpi_power_meter) Fix a check for the return value of read_domain_devices(). mainline inclusion from mainline-v6.13-rc7 commit 8d6bf2e1055fa2cca4bf233f46d4d1e2086cc5ff category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBVB4K CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=8d6bf2e1055fa2cca4bf233f46d4d1e2086cc5ff --------------------------------------------------------------------- After commit fabb1f813ec0 ("hwmon: (acpi_power_meter) Fix fail to load module on platform without _PMD method"), the acpi_power_meter driver fails to load if the platform has _PMD method. To address this, add a check for successful read_domain_devices(). Tested on Nvidia Grace machine. Fixes: fabb1f813ec0 ("hwmon: (acpi_power_meter) Fix fail to load module on platform without _PMD method") Signed-off-by: Kazuhiro Abe Link: https://lore.kernel.org/r/20250115073532.3211000-1-fj1078ii@aa.jp.fujitsu.com [groeck: Dropped unnecessary () from expression] Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao --- drivers/hwmon/acpi_power_meter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 85e02707a6d99..c98dd81a88744 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -697,7 +697,7 @@ static int setup_attrs(struct acpi_power_meter_resource *resource) /* _PMD method is optional. */ res = read_domain_devices(resource); - if (res != -ENODEV) + if (res && res != -ENODEV) return res; if (resource->caps.flags & POWER_METER_CAN_MEASURE) { From ed7e356e53709f844acbb9cb6dff944cc3c10f0f Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 1 Apr 2025 17:08:53 +0800 Subject: [PATCH 089/227] hwmon: (acpi_power_meter) Fix the fake power alarm reporting mainline inclusion from mainline-v6.14-rc1 commit 0ea627381eb527a0ebd262c690c3992085b87ff4 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBVB4K CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0ea627381eb527a0ebd262c690c3992085b87ff4 --------------------------------------------------------------------- We encountered a problem that a fake power alarm is reported to user on the platform unsupported notifications at the second step below: 1> Query 'power1_alarm' attribute when the power capping occurs. 2> Query 'power1_alarm' attribute when the power capping is over and the current average power is less then power cap value. The root cause is that the resource->power_alarm is set to true at the first step. And power meter use this old value to show the power alarm state instead of the current the comparison value. Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250220030832.2976-1-lihuisong@huawei.com Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao --- drivers/hwmon/acpi_power_meter.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index c98dd81a88744..642ffe2b39ad2 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -437,9 +437,13 @@ static ssize_t show_val(struct device *dev, ret = update_cap(resource); if (ret) return ret; + resource->power_alarm = resource->power > resource->cap; + val = resource->power_alarm; + } else { + val = resource->power_alarm || + resource->power > resource->cap; + resource->power_alarm = resource->power > resource->cap; } - val = resource->power_alarm || resource->power > resource->cap; - resource->power_alarm = resource->power > resource->cap; break; case 7: case 8: From e934d19c36658df76a95652a5775a123866e1f38 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 15 Apr 2025 14:53:49 +0800 Subject: [PATCH 090/227] ACPI: IPMI: Add helper to wait for when SMI is selected mainline inclusion from mainline-v6.9-rc2 commit 670e98a34a9e44cd384bafbda681c8c8e072b714 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBZCVP CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=670e98a34a9e44cd384bafbda681c8c8e072b714 ---------------------------------------------------------------------- On Dell servers, many APCI methods of acpi_power_meter module evaluate variables inside IPMI region, so the region handler needs to be installed. In addition to that, the handler needs to be fully functional, and that depends on SMI being selected. So add a helper to let acpi_power_meter know when the handler is installed and ready to be used. Signed-off-by: Kai-Heng Feng Acked-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20240320084317.366853-1-kai.heng.feng@canonical.com Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao --- drivers/acpi/acpi_ipmi.c | 23 ++++++++++++++++++++++- include/acpi/acpi_bus.h | 5 +++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/acpi_ipmi.c b/drivers/acpi/acpi_ipmi.c index a5fe2926bf506..19119c410d396 100644 --- a/drivers/acpi/acpi_ipmi.c +++ b/drivers/acpi/acpi_ipmi.c @@ -22,6 +22,8 @@ MODULE_LICENSE("GPL"); /* the IPMI timeout is 5s */ #define IPMI_TIMEOUT (5000) #define ACPI_IPMI_MAX_MSG_LENGTH 64 +/* 2s should be suffient for SMI being selected */ +#define ACPI_IPMI_SMI_SELECTION_TIMEOUT (2 * HZ) struct acpi_ipmi_device { /* the device list attached to driver_data.ipmi_devices */ @@ -54,6 +56,7 @@ struct ipmi_driver_data { * to this selected global IPMI system interface. */ struct acpi_ipmi_device *selected_smi; + struct completion smi_selection_done; }; struct acpi_ipmi_msg { @@ -465,8 +468,10 @@ static void ipmi_register_bmc(int iface, struct device *dev) if (temp->handle == handle) goto err_lock; } - if (!driver_data.selected_smi) + if (!driver_data.selected_smi) { driver_data.selected_smi = ipmi_device; + complete(&driver_data.smi_selection_done); + } list_add_tail(&ipmi_device->head, &driver_data.ipmi_devices); mutex_unlock(&driver_data.ipmi_lock); @@ -581,6 +586,20 @@ acpi_ipmi_space_handler(u32 function, acpi_physical_address address, return status; } +int acpi_wait_for_acpi_ipmi(void) +{ + long ret; + + ret = wait_for_completion_interruptible_timeout(&driver_data.smi_selection_done, + ACPI_IPMI_SMI_SELECTION_TIMEOUT); + + if (ret <= 0) + return -ETIMEDOUT; + + return 0; +} +EXPORT_SYMBOL_GPL(acpi_wait_for_acpi_ipmi); + static int __init acpi_ipmi_init(void) { int result; @@ -589,6 +608,8 @@ static int __init acpi_ipmi_init(void) if (acpi_disabled) return 0; + init_completion(&driver_data.smi_selection_done); + status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, ACPI_ADR_SPACE_IPMI, &acpi_ipmi_space_handler, diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 7a85ae6b7b005..a87a6d557e467 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -732,11 +732,16 @@ static inline void acpi_bus_put_acpi_device(struct acpi_device *adev) { acpi_dev_put(adev); } + +int acpi_wait_for_acpi_ipmi(void); + #else /* CONFIG_ACPI */ static inline int register_acpi_bus_type(void *bus) { return 0; } static inline int unregister_acpi_bus_type(void *bus) { return 0; } +static inline int acpi_wait_for_acpi_ipmi(void) { return 0; } + #endif /* CONFIG_ACPI */ #endif /*__ACPI_BUS_H__*/ From 66f1d158227fb6c7d8f91e45183267b10239f519 Mon Sep 17 00:00:00 2001 From: Kai-Heng Feng Date: Tue, 15 Apr 2025 14:53:50 +0800 Subject: [PATCH 091/227] hwmon: (acpi_power_meter) Ensure IPMI space handler is ready on Dell systems mainline inclusion from mainline-v6.9-rc2 commit 71113b9be4259d4bd396f22034653a9035ba07fb category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBZCVP CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=71113b9be4259d4bd396f22034653a9035ba07fb --------------------------------------------------------------------- The following error can be observed at boot: [ 3.717920] ACPI Error: No handler for Region [SYSI] (00000000ab9e62c5) [IPMI] (20230628/evregion-130) [ 3.717928] ACPI Error: Region IPMI (ID=7) has no handler (20230628/exfldio-261) [ 3.717936] No Local Variables are initialized for Method [_GHL] [ 3.717938] No Arguments are initialized for method [_GHL] [ 3.717940] ACPI Error: Aborting method \_SB.PMI0._GHL due to previous error (AE_NOT_EXIST) (20230628/psparse-529) [ 3.717949] ACPI Error: Aborting method \_SB.PMI0._PMC due to previous error (AE_NOT_EXIST) (20230628/psparse-529) [ 3.717957] ACPI: \_SB_.PMI0: _PMC evaluation failed: AE_NOT_EXIST On Dell systems several methods of acpi_power_meter access variables in IPMI region [0], so wait until IPMI space handler is installed by acpi_ipmi and also wait until SMI is selected to make the space handler fully functional. Since the dependency is inside BIOS's ASL code and it's not discoverable, so use this fixup is a hack to workaround BIOS issue. [0] https://www.dell.com/support/manuals/en-us/redhat-enterprise-linux-v8.0/rhel8_rn_pub/advanced-configuration-and-power-interface-acpi-error-messages-displayed-in-dmesg?guid=guid-0d5ae482-1977-42cf-b417-3ed5c3f5ee62 Signed-off-by: Kai-Heng Feng Link: https://lore.kernel.org/r/20240320084317.366853-2-kai.heng.feng@canonical.com [groeck: Simplified added code] Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao --- drivers/hwmon/acpi_power_meter.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 642ffe2b39ad2..73b6f377fe90e 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -913,6 +913,22 @@ static int acpi_power_meter_add(struct acpi_device *device) strcpy(acpi_device_class(device), ACPI_POWER_METER_CLASS); device->driver_data = resource; +#if IS_REACHABLE(CONFIG_ACPI_IPMI) + /* + * On Dell systems several methods of acpi_power_meter access + * variables in IPMI region, so wait until IPMI space handler is + * installed by acpi_ipmi and also wait until SMI is selected to make + * the space handler fully functional. + */ + if (dmi_match(DMI_SYS_VENDOR, "Dell Inc.")) { + struct acpi_device *ipi_device = acpi_dev_get_first_match_dev("IPI0001", NULL, -1); + + if (ipi_device && acpi_wait_for_acpi_ipmi()) + dev_warn(&device->dev, "Waiting for ACPI IPMI timeout"); + acpi_dev_put(ipi_device); + } +#endif + res = read_capabilities(resource); if (res) goto exit_free; From 7242c9e6bc5b556549fc18cd0d0a55f31f7d28d7 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 15 Apr 2025 14:53:51 +0800 Subject: [PATCH 092/227] hwmon: Fix the missing of 'average' word in hwmon_power_attr_templates mainline inclusion from mainline-v6.14-rc1 commit 232427772fc123942d110c18269cfbdf40edae18 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBZCVP CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=232427772fc123942d110c18269cfbdf40edae18 --------------------------------------------------------------------- The string "power%d_interval_max" and "power%d_interval_min" in the hwmon_power_attr_templates[] are corresponding to the sysfs interface name of hwmon_power_average_interval_max and hwmon_power_average_interval_min. But the 'average' word is missing in two strings. Fortunately, there is no driver to use it yet. Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250304074640.2770353-1-lihuisong@huawei.com Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao --- drivers/hwmon/hwmon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index fd3b277d340a9..e5527eddb5fee 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -519,8 +519,8 @@ static const char * const hwmon_power_attr_templates[] = { [hwmon_power_enable] = "power%d_enable", [hwmon_power_average] = "power%d_average", [hwmon_power_average_interval] = "power%d_average_interval", - [hwmon_power_average_interval_max] = "power%d_interval_max", - [hwmon_power_average_interval_min] = "power%d_interval_min", + [hwmon_power_average_interval_max] = "power%d_average_interval_max", + [hwmon_power_average_interval_min] = "power%d_average_interval_min", [hwmon_power_average_highest] = "power%d_average_highest", [hwmon_power_average_lowest] = "power%d_average_lowest", [hwmon_power_average_max] = "power%d_average_max", From cbd840cd609f5d51b013b5730d37f0a7820d0953 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 15 Apr 2025 14:53:52 +0800 Subject: [PATCH 093/227] hwmon: (acpi_power_meter) Replace the deprecated hwmon_device_register mainline inclusion from mainline-v6.14-rc1 commit 16746ce8adfe04f9ff8df75c1133286ba93c0e17 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBZCVP CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=16746ce8adfe04f9ff8df75c1133286ba93c0e17 --------------------------------------------------------------------- When load this mode, we can see the following log: "power_meter ACPI000D:00: hwmon_device_register() is deprecated. Please convert the driver to use hwmon_device_register_with_info()." So replace hwmon_device_register with hwmon_device_register_with_info. These attributes, 'power_accuracy', 'power_cap_hyst', 'power_average_min' and 'power_average_max', should have been placed in hwmon_chip_info as power data type. But these attributes are displayed as string format on the following case: a) power1_accuracy --> display like '90.0%' b) power1_cap_hyst --> display 'unknown' when its value is 0xFFFFFFFF c) power1_average_min/max --> display 'unknown' when its value is negative. To avoid any changes in the display of these sysfs interfaces, we can't modifiy the type of these attributes in hwmon core and have to put them to extra_groups. Please note that the path of these sysfs interfaces are modified accordingly if use hwmon_device_register_with_info(): old: all sysfs interfaces are under acpi device, namely, /sys/class/hwmon/hwmonX/device/ now: all sysfs interfaces are under hwmon device, namely, /sys/class/hwmon/hwmonX/ The new ABI does not guarantee that the underlying path remains the same. But we have to accept this change so as to replace the deprecated API. Fortunately, some userspace application, like libsensors, would scan the two path and handles this automatically. So we can accept this change so as to drop the deprecated message. Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250319020638.59925-1-lihuisong@huawei.com [groeck: Fixed some multi-line alignment issues; reverted to 32-bit arithmetic in power1_accuracy_show() fixed bad return code from power_meter_is_visible()] Signed-off-by: Guenter Roeck Signed-off-by: Xinghai Cen Signed-off-by: huwentao Conflicts: drivers/hwmon/acpi_power_meter.c --- drivers/hwmon/acpi_power_meter.c | 860 +++++++++++++++---------------- 1 file changed, 425 insertions(+), 435 deletions(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 73b6f377fe90e..11f6e074c984e 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -87,25 +87,14 @@ struct acpi_power_meter_resource { bool power_alarm; int sensors_valid; unsigned long sensors_last_updated; - struct sensor_device_attribute sensors[NUM_SENSORS]; - int num_sensors; +#define POWER_METER_TRIP_AVERAGE_MIN_IDX 0 +#define POWER_METER_TRIP_AVERAGE_MAX_IDX 1 s64 trip[2]; int num_domain_devices; struct acpi_device **domain_devices; struct kobject *holders_dir; }; -struct sensor_template { - char *label; - ssize_t (*show)(struct device *dev, - struct device_attribute *devattr, - char *buf); - ssize_t (*set)(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count); - int index; -}; - /* Averaging interval */ static int update_avg_interval(struct acpi_power_meter_resource *resource) { @@ -124,62 +113,6 @@ static int update_avg_interval(struct acpi_power_meter_resource *resource) return 0; } -static ssize_t show_avg_interval(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - - mutex_lock(&resource->lock); - update_avg_interval(resource); - mutex_unlock(&resource->lock); - - return sprintf(buf, "%llu\n", resource->avg_interval); -} - -static ssize_t set_avg_interval(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - int res; - unsigned long temp; - unsigned long long data; - acpi_status status; - - res = kstrtoul(buf, 10, &temp); - if (res) - return res; - - if (temp > resource->caps.max_avg_interval || - temp < resource->caps.min_avg_interval) - return -EINVAL; - arg0.integer.value = temp; - - mutex_lock(&resource->lock); - status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI", - &args, &data); - if (ACPI_SUCCESS(status)) - resource->avg_interval = temp; - mutex_unlock(&resource->lock); - - if (ACPI_FAILURE(status)) { - acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI", - status); - return -EINVAL; - } - - /* _PAI returns 0 on success, nonzero otherwise */ - if (data) - return -EINVAL; - - return count; -} - /* Cap functions */ static int update_cap(struct acpi_power_meter_resource *resource) { @@ -198,61 +131,6 @@ static int update_cap(struct acpi_power_meter_resource *resource) return 0; } -static ssize_t show_cap(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - - mutex_lock(&resource->lock); - update_cap(resource); - mutex_unlock(&resource->lock); - - return sprintf(buf, "%llu\n", resource->cap * 1000); -} - -static ssize_t set_cap(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - int res; - unsigned long temp; - unsigned long long data; - acpi_status status; - - res = kstrtoul(buf, 10, &temp); - if (res) - return res; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - if (temp > resource->caps.max_cap || temp < resource->caps.min_cap) - return -EINVAL; - arg0.integer.value = temp; - - mutex_lock(&resource->lock); - status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL", - &args, &data); - if (ACPI_SUCCESS(status)) - resource->cap = temp; - mutex_unlock(&resource->lock); - - if (ACPI_FAILURE(status)) { - acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL", - status); - return -EINVAL; - } - - /* _SHL returns 0 on success, nonzero otherwise */ - if (data) - return -EINVAL; - - return count; -} - /* Power meter trip points */ static int set_acpi_trip(struct acpi_power_meter_resource *resource) { @@ -287,34 +165,6 @@ static int set_acpi_trip(struct acpi_power_meter_resource *resource) return 0; } -static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - unsigned long temp, trip_bk; - int res; - - res = kstrtoul(buf, 10, &temp); - if (res) - return res; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - - guard(mutex)(&resource->lock); - - trip_bk = resource->trip[attr->index - 7]; - resource->trip[attr->index - 7] = temp; - res = set_acpi_trip(resource); - if (res) { - resource->trip[attr->index - 7] = trip_bk; - return res; - } - - return count; -} - /* Power meter */ static int update_meter(struct acpi_power_meter_resource *resource) { @@ -341,206 +191,6 @@ static int update_meter(struct acpi_power_meter_resource *resource) return 0; } -static ssize_t show_power(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - - mutex_lock(&resource->lock); - update_meter(resource); - mutex_unlock(&resource->lock); - - if (resource->power == UNKNOWN_POWER) - return -ENODATA; - - return sprintf(buf, "%llu\n", resource->power * 1000); -} - -/* Miscellaneous */ -static ssize_t show_str(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - acpi_string val; - int ret; - - mutex_lock(&resource->lock); - switch (attr->index) { - case 0: - val = resource->model_number; - break; - case 1: - val = resource->serial_number; - break; - case 2: - val = resource->oem_info; - break; - default: - WARN(1, "Implementation error: unexpected attribute index %d\n", - attr->index); - val = ""; - break; - } - ret = sprintf(buf, "%s\n", val); - mutex_unlock(&resource->lock); - return ret; -} - -static ssize_t show_val(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - u64 val = 0; - int ret; - - guard(mutex)(&resource->lock); - - switch (attr->index) { - case 0: - val = resource->caps.min_avg_interval; - break; - case 1: - val = resource->caps.max_avg_interval; - break; - case 2: - val = resource->caps.min_cap * 1000; - break; - case 3: - val = resource->caps.max_cap * 1000; - break; - case 4: - if (resource->caps.hysteresis == UNKNOWN_HYSTERESIS) - return sprintf(buf, "unknown\n"); - - val = resource->caps.hysteresis * 1000; - break; - case 5: - if (resource->caps.flags & POWER_METER_IS_BATTERY) - val = 1; - else - val = 0; - break; - case 6: - ret = update_meter(resource); - if (ret) - return ret; - /* need to update cap if not to support the notification. */ - if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) { - ret = update_cap(resource); - if (ret) - return ret; - resource->power_alarm = resource->power > resource->cap; - val = resource->power_alarm; - } else { - val = resource->power_alarm || - resource->power > resource->cap; - resource->power_alarm = resource->power > resource->cap; - } - break; - case 7: - case 8: - if (resource->trip[attr->index - 7] < 0) - return sprintf(buf, "unknown\n"); - - val = resource->trip[attr->index - 7] * 1000; - break; - default: - WARN(1, "Implementation error: unexpected attribute index %d\n", - attr->index); - break; - } - - return sprintf(buf, "%llu\n", val); -} - -static ssize_t show_accuracy(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - unsigned int acc = resource->caps.accuracy; - - return sprintf(buf, "%u.%u%%\n", acc / 1000, acc % 1000); -} - -static ssize_t show_name(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME); -} - -#define RO_SENSOR_TEMPLATE(_label, _show, _index) \ - { \ - .label = _label, \ - .show = _show, \ - .index = _index, \ - } - -#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index) \ - { \ - .label = _label, \ - .show = _show, \ - .set = _set, \ - .index = _index, \ - } - -/* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */ -static struct sensor_template meter_attrs[] = { - RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0), - RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0), - RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0), - RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1), - RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5), - RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval, - set_avg_interval, 0), - {}, -}; - -static struct sensor_template misc_cap_attrs[] = { - RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2), - RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3), - RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4), - RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6), - {}, -}; - -static struct sensor_template ro_cap_attrs[] = { - RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0), - {}, -}; - -static struct sensor_template rw_cap_attrs[] = { - RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0), - {}, -}; - -static struct sensor_template trip_attrs[] = { - RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7), - RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8), - {}, -}; - -static struct sensor_template misc_attrs[] = { - RO_SENSOR_TEMPLATE("name", show_name, 0), - RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0), - RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2), - RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1), - {}, -}; - -#undef RO_SENSOR_TEMPLATE -#undef RW_SENSOR_TEMPLATE - /* Read power domain data */ static void remove_domain_devices(struct acpi_power_meter_resource *resource) { @@ -644,109 +294,434 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource) return res; } -/* Registration and deregistration */ -static int register_attrs(struct acpi_power_meter_resource *resource, - struct sensor_template *attrs) +static int set_trip(struct acpi_power_meter_resource *resource, u16 trip_idx, + unsigned long trip) { - struct device *dev = &resource->acpi_dev->dev; - struct sensor_device_attribute *sensors = - &resource->sensors[resource->num_sensors]; - int res = 0; + unsigned long trip_bk; + int ret; - while (attrs->label) { - sensors->dev_attr.attr.name = attrs->label; - sensors->dev_attr.attr.mode = 0444; - sensors->dev_attr.show = attrs->show; - sensors->index = attrs->index; + trip = DIV_ROUND_CLOSEST(trip, 1000); + trip_bk = resource->trip[trip_idx]; - if (attrs->set) { - sensors->dev_attr.attr.mode |= 0200; - sensors->dev_attr.store = attrs->set; - } + resource->trip[trip_idx] = trip; + ret = set_acpi_trip(resource); + if (ret) { + dev_err(&resource->acpi_dev->dev, "set %s failed.\n", + (trip_idx == POWER_METER_TRIP_AVERAGE_MIN_IDX) ? + "power1_average_min" : "power1_average_max"); + resource->trip[trip_idx] = trip_bk; + } - sysfs_attr_init(&sensors->dev_attr.attr); - res = device_create_file(dev, &sensors->dev_attr); - if (res) { - sensors->dev_attr.attr.name = NULL; - goto error; - } - sensors++; - resource->num_sensors++; - attrs++; + return ret; +} + +static int set_cap(struct acpi_power_meter_resource *resource, + unsigned long cap) +{ + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long data; + acpi_status status; + + cap = DIV_ROUND_CLOSEST(cap, 1000); + if (cap > resource->caps.max_cap || cap < resource->caps.min_cap) + return -EINVAL; + + arg0.integer.value = cap; + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL", + &args, &data); + if (ACPI_FAILURE(status)) { + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL", + status); + return -EINVAL; } + resource->cap = cap; -error: - return res; + /* _SHL returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return 0; } -static void remove_attrs(struct acpi_power_meter_resource *resource) +static int set_avg_interval(struct acpi_power_meter_resource *resource, + unsigned long val) { - int i; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long data; + acpi_status status; - for (i = 0; i < resource->num_sensors; i++) { - if (!resource->sensors[i].dev_attr.attr.name) - continue; - device_remove_file(&resource->acpi_dev->dev, - &resource->sensors[i].dev_attr); + if (val > resource->caps.max_avg_interval || + val < resource->caps.min_avg_interval) + return -EINVAL; + + arg0.integer.value = val; + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI", + &args, &data); + if (ACPI_FAILURE(status)) { + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI", + status); + return -EINVAL; } + resource->avg_interval = val; - remove_domain_devices(resource); + /* _PAI returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; - resource->num_sensors = 0; + return 0; } -static int setup_attrs(struct acpi_power_meter_resource *resource) +static int get_power_alarm_state(struct acpi_power_meter_resource *resource, + long *val) { - int res = 0; + int ret; - /* _PMD method is optional. */ - res = read_domain_devices(resource); - if (res && res != -ENODEV) - return res; + ret = update_meter(resource); + if (ret) + return ret; - if (resource->caps.flags & POWER_METER_CAN_MEASURE) { - res = register_attrs(resource, meter_attrs); - if (res) - goto error; + /* need to update cap if not to support the notification. */ + if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) { + ret = update_cap(resource); + if (ret) + return ret; + resource->power_alarm = resource->power > resource->cap; + *val = resource->power_alarm; + } else { + *val = resource->power_alarm || resource->power > resource->cap; + resource->power_alarm = resource->power > resource->cap; } - if (resource->caps.flags & POWER_METER_CAN_CAP) { - if (!can_cap_in_hardware()) { - dev_warn(&resource->acpi_dev->dev, - "Ignoring unsafe software power cap!\n"); - goto skip_unsafe_cap; + return 0; +} + +static umode_t power_meter_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct acpi_power_meter_resource *res = data; + + if (type != hwmon_power) + return 0; + + switch (attr) { + case hwmon_power_average: + case hwmon_power_average_interval_min: + case hwmon_power_average_interval_max: + if (res->caps.flags & POWER_METER_CAN_MEASURE) + return 0444; + break; + case hwmon_power_average_interval: + if (res->caps.flags & POWER_METER_CAN_MEASURE) + return 0644; + break; + case hwmon_power_cap_min: + case hwmon_power_cap_max: + case hwmon_power_alarm: + if (res->caps.flags & POWER_METER_CAN_CAP && can_cap_in_hardware()) + return 0444; + break; + case hwmon_power_cap: + if (res->caps.flags & POWER_METER_CAN_CAP && can_cap_in_hardware()) { + if (res->caps.configurable_cap) + return 0644; + else + return 0444; } + break; + default: + break; + } + + return 0; +} - if (resource->caps.configurable_cap) - res = register_attrs(resource, rw_cap_attrs); - else - res = register_attrs(resource, ro_cap_attrs); +static int power_meter_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + int ret = 0; - if (res) - goto error; + if (type != hwmon_power) + return -EINVAL; - res = register_attrs(resource, misc_cap_attrs); - if (res) - goto error; + guard(mutex)(&res->lock); + + switch (attr) { + case hwmon_power_average: + ret = update_meter(res); + if (ret) + return ret; + if (res->power == UNKNOWN_POWER) + return -ENODATA; + *val = res->power * 1000; + break; + case hwmon_power_average_interval_min: + *val = res->caps.min_avg_interval; + break; + case hwmon_power_average_interval_max: + *val = res->caps.max_avg_interval; + break; + case hwmon_power_average_interval: + ret = update_avg_interval(res); + if (ret) + return ret; + *val = (res)->avg_interval; + break; + case hwmon_power_cap_min: + *val = res->caps.min_cap * 1000; + break; + case hwmon_power_cap_max: + *val = res->caps.max_cap * 1000; + break; + case hwmon_power_alarm: + ret = get_power_alarm_state(res, val); + if (ret) + return ret; + break; + case hwmon_power_cap: + ret = update_cap(res); + if (ret) + return ret; + *val = res->cap * 1000; + break; + default: + break; } -skip_unsafe_cap: - if (resource->caps.flags & POWER_METER_CAN_TRIP) { - res = register_attrs(resource, trip_attrs); - if (res) - goto error; + return 0; +} + +static int power_meter_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + int ret; + + if (type != hwmon_power) + return -EINVAL; + + guard(mutex)(&res->lock); + switch (attr) { + case hwmon_power_cap: + ret = set_cap(res, val); + break; + case hwmon_power_average_interval: + ret = set_avg_interval(res, val); + break; + default: + ret = -EOPNOTSUPP; } - res = register_attrs(resource, misc_attrs); - if (res) - goto error; + return ret; +} - return res; -error: - remove_attrs(resource); - return res; +static const struct hwmon_channel_info * const power_meter_info[] = { + HWMON_CHANNEL_INFO(power, HWMON_P_AVERAGE | + HWMON_P_AVERAGE_INTERVAL | HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX | HWMON_P_CAP | HWMON_P_CAP_MIN | + HWMON_P_CAP_MAX | HWMON_P_ALARM), + NULL +}; + +static const struct hwmon_ops power_meter_ops = { + .is_visible = power_meter_is_visible, + .read = power_meter_read, + .write = power_meter_write, +}; + +static const struct hwmon_chip_info power_meter_chip_info = { + .ops = &power_meter_ops, + .info = power_meter_info, +}; + +static ssize_t power1_average_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + unsigned long trip; + int ret; + + ret = kstrtoul(buf, 10, &trip); + if (ret) + return ret; + + mutex_lock(&res->lock); + ret = set_trip(res, POWER_METER_TRIP_AVERAGE_MAX_IDX, trip); + mutex_unlock(&res->lock); + + return ret == 0 ? count : ret; +} + +static ssize_t power1_average_min_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + unsigned long trip; + int ret; + + ret = kstrtoul(buf, 10, &trip); + if (ret) + return ret; + + mutex_lock(&res->lock); + ret = set_trip(res, POWER_METER_TRIP_AVERAGE_MIN_IDX, trip); + mutex_unlock(&res->lock); + + return ret == 0 ? count : ret; +} + +static ssize_t power1_average_min_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + if (res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] < 0) + return sysfs_emit(buf, "unknown\n"); + + return sysfs_emit(buf, "%lld\n", + res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] * 1000); +} + +static ssize_t power1_average_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + if (res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] < 0) + return sysfs_emit(buf, "unknown\n"); + + return sysfs_emit(buf, "%lld\n", + res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] * 1000); +} + +static ssize_t power1_cap_hyst_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + if (res->caps.hysteresis == UNKNOWN_HYSTERESIS) + return sysfs_emit(buf, "unknown\n"); + + return sysfs_emit(buf, "%llu\n", res->caps.hysteresis * 1000); +} + +static ssize_t power1_accuracy_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + unsigned int acc = res->caps.accuracy; + + return sysfs_emit(buf, "%u.%u%%\n", acc / 1000, acc % 1000); +} + +static ssize_t power1_is_battery_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", + res->caps.flags & POWER_METER_IS_BATTERY ? 1 : 0); +} + +static ssize_t power1_model_number_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", res->model_number); +} + +static ssize_t power1_oem_info_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", res->oem_info); +} + +static ssize_t power1_serial_number_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", res->serial_number); } +/* depend on POWER_METER_CAN_TRIP */ +static DEVICE_ATTR_RW(power1_average_max); +static DEVICE_ATTR_RW(power1_average_min); + +/* depend on POWER_METER_CAN_CAP */ +static DEVICE_ATTR_RO(power1_cap_hyst); + +/* depend on POWER_METER_CAN_MEASURE */ +static DEVICE_ATTR_RO(power1_accuracy); +static DEVICE_ATTR_RO(power1_is_battery); + +static DEVICE_ATTR_RO(power1_model_number); +static DEVICE_ATTR_RO(power1_oem_info); +static DEVICE_ATTR_RO(power1_serial_number); + +static umode_t power_extra_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + if (attr == &dev_attr_power1_is_battery.attr || + attr == &dev_attr_power1_accuracy.attr) { + if ((res->caps.flags & POWER_METER_CAN_MEASURE) == 0) + return 0; + } + + if (attr == &dev_attr_power1_cap_hyst.attr) { + if ((res->caps.flags & POWER_METER_CAN_CAP) == 0) { + return 0; + } else if (!can_cap_in_hardware()) { + dev_warn(&res->acpi_dev->dev, + "Ignoring unsafe software power cap!\n"); + return 0; + } + } + + if (attr == &dev_attr_power1_average_max.attr || + attr == &dev_attr_power1_average_min.attr) { + if ((res->caps.flags & POWER_METER_CAN_TRIP) == 0) + return 0; + } + + return attr->mode; +} + +static struct attribute *power_extra_attrs[] = { + &dev_attr_power1_average_max.attr, + &dev_attr_power1_average_min.attr, + &dev_attr_power1_cap_hyst.attr, + &dev_attr_power1_accuracy.attr, + &dev_attr_power1_is_battery.attr, + &dev_attr_power1_model_number.attr, + &dev_attr_power1_oem_info.attr, + &dev_attr_power1_serial_number.attr, + NULL +}; + +static const struct attribute_group power_extra_group = { + .attrs = power_extra_attrs, + .is_visible = power_extra_is_visible, +}; + +__ATTRIBUTE_GROUPS(power_extra); + static void free_capabilities(struct acpi_power_meter_resource *resource) { acpi_string *str; @@ -855,13 +830,23 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event) case METER_NOTIFY_CONFIG: mutex_lock(&resource->lock); free_capabilities(resource); + remove_domain_devices(resource); + hwmon_device_unregister(resource->hwmon_dev); res = read_capabilities(resource); - mutex_unlock(&resource->lock); if (res) - break; - - remove_attrs(resource); - setup_attrs(resource); + dev_err_once(&device->dev, "read capabilities failed.\n"); + res = read_domain_devices(resource); + if (res && res != -ENODEV) + dev_err_once(&device->dev, "read domain devices failed.\n"); + resource->hwmon_dev = + hwmon_device_register_with_info(&device->dev, + ACPI_POWER_METER_NAME, + resource, + &power_meter_chip_info, + power_extra_groups); + if (IS_ERR(resource->hwmon_dev)) + dev_err_once(&device->dev, "register hwmon device failed.\n"); + mutex_unlock(&resource->lock); break; case METER_NOTIFY_TRIP: sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME); @@ -924,7 +909,7 @@ static int acpi_power_meter_add(struct acpi_device *device) struct acpi_device *ipi_device = acpi_dev_get_first_match_dev("IPI0001", NULL, -1); if (ipi_device && acpi_wait_for_acpi_ipmi()) - dev_warn(&device->dev, "Waiting for ACPI IPMI timeout"); + dev_warn(&device->dev, "Waiting for ACPI IPMI timeout"); acpi_dev_put(ipi_device); } #endif @@ -935,11 +920,16 @@ static int acpi_power_meter_add(struct acpi_device *device) resource->trip[0] = resource->trip[1] = -1; - res = setup_attrs(resource); - if (res) + /* _PMD method is optional. */ + res = read_domain_devices(resource); + if (res && res != -ENODEV) goto exit_free_capability; - resource->hwmon_dev = hwmon_device_register(&device->dev); + resource->hwmon_dev = + hwmon_device_register_with_info(&device->dev, + ACPI_POWER_METER_NAME, resource, + &power_meter_chip_info, + power_extra_groups); if (IS_ERR(resource->hwmon_dev)) { res = PTR_ERR(resource->hwmon_dev); goto exit_remove; @@ -949,7 +939,7 @@ static int acpi_power_meter_add(struct acpi_device *device) goto exit; exit_remove: - remove_attrs(resource); + remove_domain_devices(resource); exit_free_capability: free_capabilities(resource); exit_free: @@ -968,7 +958,7 @@ static int acpi_power_meter_remove(struct acpi_device *device) resource = acpi_driver_data(device); hwmon_device_unregister(resource->hwmon_dev); - remove_attrs(resource); + remove_domain_devices(resource); free_capabilities(resource); kfree(resource); From 8986c674208708af15db4a7c425420f187bfa652 Mon Sep 17 00:00:00 2001 From: Jie Zhan Date: Thu, 3 Jul 2025 11:19:02 +0800 Subject: [PATCH 094/227] cpufreq: Support per-policy performance boost The boost control currently applies to the whole system. However, users may prefer to boost a subset of cores in order to provide prioritized performance to workloads running on the boosted cores. Enable per-policy boost by adding a 'boost' sysfs interface under each policy path. This can be found at: /sys/devices/system/cpu/cpufreq/policy<*>/boost Same to the global boost switch, writing 1/0 to the per-policy 'boost' enables/disables boost on a cpufreq policy respectively. The user view of global and per-policy boost controls should be: 1. Enabling global boost initially enables boost on all policies, and per-policy boost can then be enabled or disabled individually, given that the platform does support so. 2. Disabling global boost makes the per-policy boost interface illegal. Signed-off-by: Jie Zhan Reviewed-by: Wei Xu Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki Signed-off-by: Qi Xi --- drivers/cpufreq/cpufreq.c | 43 +++++++++++++++++++++++++++++++++++++++ include/linux/cpufreq.h | 3 +++ 2 files changed, 46 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index c2227be7bad88..0d5942f3f0df4 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -81,6 +81,7 @@ static void cpufreq_governor_limits(struct cpufreq_policy *policy); static int cpufreq_set_policy(struct cpufreq_policy *policy, struct cpufreq_governor *new_gov, unsigned int new_pol); +static bool cpufreq_boost_supported(void); /* * Two notifier lists: the "policy" list is involved in the @@ -618,6 +619,40 @@ static ssize_t store_boost(struct kobject *kobj, struct kobj_attribute *attr, } define_one_global_rw(boost); +static ssize_t show_local_boost(struct cpufreq_policy *policy, char *buf) +{ + return sysfs_emit(buf, "%d\n", policy->boost_enabled); +} + +static ssize_t store_local_boost(struct cpufreq_policy *policy, + const char *buf, size_t count) +{ + int ret, enable; + + ret = kstrtoint(buf, 10, &enable); + if (ret || enable < 0 || enable > 1) + return -EINVAL; + + if (!cpufreq_driver->boost_enabled) + return -EINVAL; + + if (policy->boost_enabled == enable) + return count; + + cpus_read_lock(); + ret = cpufreq_driver->set_boost(policy, enable); + cpus_read_unlock(); + + if (ret) + return ret; + + policy->boost_enabled = enable; + + return count; +} + +static struct freq_attr local_boost = __ATTR(boost, 0644, show_local_boost, store_local_boost); + static struct cpufreq_governor *find_governor(const char *str_governor) { struct cpufreq_governor *t; @@ -1057,6 +1092,12 @@ static int cpufreq_add_dev_interface(struct cpufreq_policy *policy) return ret; } + if (cpufreq_boost_supported()) { + ret = sysfs_create_file(&policy->kobj, &local_boost.attr); + if (ret) + return ret; + } + return 0; } @@ -2687,6 +2728,8 @@ int cpufreq_boost_trigger_state(int state) ret = cpufreq_driver->set_boost(policy, state); if (ret) goto err_reset_state; + + policy->boost_enabled = state; } cpus_read_unlock(); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 3e2818678cc34..896ca659e66a8 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -134,6 +134,9 @@ struct cpufreq_policy { */ bool dvfs_possible_from_any_cpu; + /* Per policy boost enabled flag. */ + bool boost_enabled; + /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ unsigned int cached_target_freq; unsigned int cached_resolved_idx; From e668982b984499f495b3d603f581ce57d4815187 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Thu, 3 Jul 2025 11:19:03 +0800 Subject: [PATCH 095/227] cpufreq: Fix per-policy boost behavior on SoCs using cpufreq_boost_set_sw() stable inclusion from stable-v6.6.23 commit 9d47d2e7f82dec7cab09996bdb9062786eb827f6 bugzilla: https://gitee.com/openeuler/kernel/issues/I9MPZ8 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=9d47d2e7f82dec7cab09996bdb9062786eb827f6 -------------------------------- [ Upstream commit f37a4d6b4a2c77414e8b9d25dd5ee31537ce9b00 ] In the existing code, per-policy flags don't have any impact i.e. if cpufreq_driver boost is enabled and boost is disabled for one or more of the policies, the cpufreq driver will behave as if boost is enabled. Fix this by incorporating per-policy boost flag in the policy->max computation used in cpufreq_frequency_table_cpuinfo and setting the default per-policy boost to mirror the cpufreq_driver boost flag. Fixes: 218a06a79d9a ("cpufreq: Support per-policy performance boost") Reported-by: Dietmar Eggemann Reviewed-by: Viresh Kumar Reviewed-by: Dhruva Gole Signed-off-by: Sibi Sankar Tested-by:Yipeng Zou Reviewed-by: Yipeng Zou Signed-off-by: Rafael J. Wysocki Signed-off-by: Sasha Levin Signed-off-by: ZhangPeng Signed-off-by: Qi Xi --- drivers/cpufreq/cpufreq.c | 18 ++++++++++++------ drivers/cpufreq/freq_table.c | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 0d5942f3f0df4..6fdee87494c83 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -639,14 +639,16 @@ static ssize_t store_local_boost(struct cpufreq_policy *policy, if (policy->boost_enabled == enable) return count; + policy->boost_enabled = enable; + cpus_read_lock(); ret = cpufreq_driver->set_boost(policy, enable); cpus_read_unlock(); - if (ret) + if (ret) { + policy->boost_enabled = !policy->boost_enabled; return ret; - - policy->boost_enabled = enable; + } return count; } @@ -1411,6 +1413,9 @@ static int cpufreq_online(unsigned int cpu) goto out_free_policy; } + /* Let the per-policy boost flag mirror the cpufreq_driver boost during init */ + policy->boost_enabled = cpufreq_boost_enabled() && policy_has_boost_freq(policy); + /* * The initialization has succeeded and the policy is online. * If there is a problem with its frequency table, take it @@ -2725,11 +2730,12 @@ int cpufreq_boost_trigger_state(int state) cpus_read_lock(); for_each_active_policy(policy) { + policy->boost_enabled = state; ret = cpufreq_driver->set_boost(policy, state); - if (ret) + if (ret) { + policy->boost_enabled = !policy->boost_enabled; goto err_reset_state; - - policy->boost_enabled = state; + } } cpus_read_unlock(); diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 67e56cf638efb..924b75bcb0613 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -40,7 +40,7 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, cpufreq_for_each_valid_entry(pos, table) { freq = pos->frequency; - if (!cpufreq_boost_enabled() + if ((!cpufreq_boost_enabled() || !policy->boost_enabled) && (pos->flags & CPUFREQ_BOOST_FREQ)) continue; From 2733f4a5b1cb07971e248591deca4333d89fca84 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 3 Jul 2025 11:19:04 +0800 Subject: [PATCH 096/227] cpufreq: Allow drivers to advertise boost enabled stable inclusion from stable-v6.6.41 commit e408184365c70f8a273f1a281e7a33416e742687 bugzilla: https://gitee.com/openeuler/kernel/issues/IAHMJO Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=e408184365c70f8a273f1a281e7a33416e742687 -------------------------------- commit 102fa9c4b439ca3bd93d13fb53f5b7592d96a109 upstream. The behavior introduced in commit f37a4d6b4a2c ("cpufreq: Fix per-policy boost behavior on SoCs using cpufreq_boost_set_sw()") sets up the boost policy incorrectly when boost has been enabled by the platform firmware initially even if a driver sets the policy up. This is because policy_has_boost_freq() assumes that there is a frequency table set up by the driver and that the boost frequencies are advertised in that table. This assumption doesn't work for acpi-cpufreq or amd-pstate. Only use this check to enable boost if it's not already enabled instead of also disabling it if alreayd enabled. Fixes: f37a4d6b4a2c ("cpufreq: Fix per-policy boost behavior on SoCs using cpufreq_boost_set_sw()") Link: https://patch.msgid.link/20240626204723.6237-1-mario.limonciello@amd.com Reviewed-by: Sibi Sankar Reviewed-by: Dhruva Gole Acked-by: Viresh Kumar Reviewed-by: Gautham R. Shenoy Suggested-by: Viresh Kumar Suggested-by: Gautham R. Shenoy Signed-off-by: Mario Limonciello Cc: All applicable Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman Signed-off-by: ZhangPeng Signed-off-by: Qi Xi --- drivers/cpufreq/cpufreq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 6fdee87494c83..2c0d534627d33 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1414,7 +1414,8 @@ static int cpufreq_online(unsigned int cpu) } /* Let the per-policy boost flag mirror the cpufreq_driver boost during init */ - policy->boost_enabled = cpufreq_boost_enabled() && policy_has_boost_freq(policy); + if (cpufreq_boost_enabled() && policy_has_boost_freq(policy)) + policy->boost_enabled = true; /* * The initialization has succeeded and the policy is online. From dd1d249c92d79dc65eeef4b1739d568542d1e8b4 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Thu, 3 Jul 2025 11:19:05 +0800 Subject: [PATCH 097/227] cpufreq: Introduce a more generic way to set default per-policy boost flag mainline inclusion from mainline-v6.7-rc5 commit dd016f379ebc2d43a9405742d1a6066577509bd7 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBQYEH CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=dd016f379ebc2d43a9405742d1a6066577509bd7 --------------------------------------------------------------------- In cpufreq_online() of cpufreq.c, the per-policy boost flag is already set to mirror the cpufreq_driver boost during init but using freq_table to judge if the policy has boost frequency. There are two drawbacks to this approach: 1. It doesn't work for the cpufreq drivers that do not use a frequency table. For now, acpi-cpufreq and amd-pstate have to enable boost in policy initialization. And cppc_cpufreq never set policy to boost when going online no matter what the cpufreq_driver boost flag is. 2. If the CPU goes offline when cpufreq_driver boost is enabled and then goes online when cpufreq_driver boost is disabled, the per-policy boost flag will incorrectly remain true. Running set_boost at the end of the online process is a more generic way for all cpufreq drivers. Signed-off-by: Lifeng Zheng Link: https://patch.msgid.link/20250117101457.1530653-3-zhenglifeng1@huawei.com Acked-by: Viresh Kumar [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Xinghai Cen Signed-off-by: Qi Xi --- drivers/cpufreq/cpufreq.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 2c0d534627d33..017ae0a585be2 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1413,10 +1413,6 @@ static int cpufreq_online(unsigned int cpu) goto out_free_policy; } - /* Let the per-policy boost flag mirror the cpufreq_driver boost during init */ - if (cpufreq_boost_enabled() && policy_has_boost_freq(policy)) - policy->boost_enabled = true; - /* * The initialization has succeeded and the policy is online. * If there is a problem with its frequency table, take it @@ -1569,6 +1565,18 @@ static int cpufreq_online(unsigned int cpu) if (cpufreq_thermal_control_enabled(cpufreq_driver)) policy->cdev = of_cpufreq_cooling_register(policy); + /* Let the per-policy boost flag mirror the cpufreq_driver boost during init */ + if (policy->boost_enabled != cpufreq_boost_enabled()) { + policy->boost_enabled = cpufreq_boost_enabled(); + ret = cpufreq_driver->set_boost(policy, policy->boost_enabled); + if (ret) { + /* If the set_boost fails, the online operation is not affected */ + pr_info("%s: CPU%d: Cannot %s BOOST\n", __func__, policy->cpu, + policy->boost_enabled ? "enable" : "disable"); + policy->boost_enabled = !policy->boost_enabled; + } + } + pr_debug("initialization complete\n"); return 0; From 5bfffb57d0e6446b482e5e5bbafe46f9033b32fc Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Thu, 3 Jul 2025 11:19:06 +0800 Subject: [PATCH 098/227] cpufreq: CPPC: Fix wrong max_freq in policy initialization mainline inclusion from mainline-v6.7-rc5 commit 03d8b4e76266e11662c5e544854b737843173e2d category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBQYEH CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=03d8b4e76266e11662c5e544854b737843173e2d --------------------------------------------------------------------- In policy initialization, policy->max and policy->cpuinfo.max_freq are always set to the value calculated from caps->nominal_perf. This will cause the frequency stay on base frequency even if the policy is already boosted when a CPU is going online. Fix this by using policy->boost_enabled to determine which value should be set. Signed-off-by: Lifeng Zheng Acked-by: Viresh Kumar Link: https://patch.msgid.link/20250117101457.1530653-4-zhenglifeng1@huawei.com [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Xinghai Cen Signed-off-by: Qi Xi --- drivers/cpufreq/cppc_cpufreq.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 07d644bb71b95..6f6e7d8f5b322 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -544,8 +544,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) */ policy->min = cppc_cpufreq_perf_to_khz(cpu_data, caps->lowest_nonlinear_perf); - policy->max = cppc_cpufreq_perf_to_khz(cpu_data, - caps->nominal_perf); + policy->max = cppc_cpufreq_perf_to_khz(cpu_data, policy->boost_enabled ? + caps->highest_perf : caps->nominal_perf); /* * Set cpuinfo.min_freq to Lowest to make the full range of performance @@ -554,8 +554,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) */ policy->cpuinfo.min_freq = cppc_cpufreq_perf_to_khz(cpu_data, caps->lowest_perf); - policy->cpuinfo.max_freq = cppc_cpufreq_perf_to_khz(cpu_data, - caps->nominal_perf); + policy->cpuinfo.max_freq = policy->max; policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu); policy->shared_type = cpu_data->shared_type; From c7c8bf072211b6c3f28aaac9432051f66087906f Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Thu, 3 Jul 2025 11:19:07 +0800 Subject: [PATCH 099/227] cpufreq: ACPI: Defer setting boost MSRs When acpi-cpufreq is loaded, boost is enabled on every CPU (by setting an MSR) before the driver is registered with cpufreq. This can be very time consuming, because it is done with a CPU hotplug startup callback, and cpuhp_setup_state() schedules the callback (cpufreq_boost_online()) to run on each CPU one at a time, waiting for each to run before calling the next. If cpufreq_register_driver() fails--if, for example, there are no ACPI P-states present--this is wasted time. Since cpufreq already sets up a CPU hotplug startup callback if and when acpi-cpufreq is registered, set the boost MSRs in acpi_cpufreq_cpu_init(), which is called by the cpufreq cpuhp callback. This allows acpi-cpufreq to exit quickly if it is loaded but not needed. On one system with 192 CPUs, this patch speeds up boot by about 30 seconds. Signed-off-by: Stuart Hayes Signed-off-by: Rafael J. Wysocki Signed-off-by: Qi Xi --- drivers/cpufreq/acpi-cpufreq.c | 31 +++---------------------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 28467d83c7452..8b5c84c9dc1ee 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -530,15 +530,6 @@ static void free_acpi_perf_data(void) free_percpu(acpi_perf_data); } -static int cpufreq_boost_online(unsigned int cpu) -{ - /* - * On the CPU_UP path we simply keep the boost-disable flag - * in sync with the current global state. - */ - return boost_set_msr(acpi_cpufreq_driver.boost_enabled); -} - static int cpufreq_boost_down_prep(unsigned int cpu) { /* @@ -892,6 +883,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (perf->states[0].core_frequency * 1000 != freq_table[0].frequency) pr_warn(FW_WARN "P-state 0 is not max freq\n"); + set_boost(policy, acpi_cpufreq_driver.boost_enabled); + return result; err_unreg: @@ -911,6 +904,7 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy) pr_debug("%s\n", __func__); + cpufreq_boost_down_prep(policy->cpu); policy->fast_switch_possible = false; policy->driver_data = NULL; acpi_processor_unregister_performance(data->acpi_perf_cpu); @@ -967,25 +961,9 @@ static void __init acpi_cpufreq_boost_init(void) acpi_cpufreq_driver.set_boost = set_boost; acpi_cpufreq_driver.boost_enabled = boost_state(0); - /* - * This calls the online callback on all online cpu and forces all - * MSRs to the same value. - */ - ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "cpufreq/acpi:online", - cpufreq_boost_online, cpufreq_boost_down_prep); - if (ret < 0) { - pr_err("acpi_cpufreq: failed to register hotplug callbacks\n"); - return; - } acpi_cpufreq_online = ret; } -static void acpi_cpufreq_boost_exit(void) -{ - if (acpi_cpufreq_online > 0) - cpuhp_remove_state_nocalls(acpi_cpufreq_online); -} - static int __init acpi_cpufreq_init(void) { int ret; @@ -1027,7 +1005,6 @@ static int __init acpi_cpufreq_init(void) ret = cpufreq_register_driver(&acpi_cpufreq_driver); if (ret) { free_acpi_perf_data(); - acpi_cpufreq_boost_exit(); } return ret; } @@ -1036,8 +1013,6 @@ static void __exit acpi_cpufreq_exit(void) { pr_debug("%s\n", __func__); - acpi_cpufreq_boost_exit(); - cpufreq_unregister_driver(&acpi_cpufreq_driver); free_acpi_perf_data(); From d3e33e3576031edaee45a6c84ebb7caca49f31ec Mon Sep 17 00:00:00 2001 From: Stuart Hayes Date: Thu, 3 Jul 2025 11:19:08 +0800 Subject: [PATCH 100/227] cpufreq: ACPI: Only set boost MSRs on supported CPUs Stop trying to set boost MSRs on CPUs that don't support boost. This corrects a bug in the recent patch "Defer setting boost MSRs". Fixes: 13fdbc8b8da6 ("cpufreq: ACPI: Defer setting boost MSRs") Signed-off-by: Stuart Hayes Reported-by: Borislav Petkov (AMD) Tested-by: Borislav Petkov (AMD) Signed-off-by: Rafael J. Wysocki Signed-off-by: Qi Xi --- drivers/cpufreq/acpi-cpufreq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 8b5c84c9dc1ee..0def3221a2e56 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -883,7 +883,8 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (perf->states[0].core_frequency * 1000 != freq_table[0].frequency) pr_warn(FW_WARN "P-state 0 is not max freq\n"); - set_boost(policy, acpi_cpufreq_driver.boost_enabled); + if (acpi_cpufreq_driver.set_boost) + set_boost(policy, acpi_cpufreq_driver.boost_enabled); return result; From 65d7c0c8b4bf44f26ee4c01f01579fe10b2b558b Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 3 Jul 2025 11:19:09 +0800 Subject: [PATCH 101/227] cpufreq: ACPI: Mark boost policy as enabled when setting boost stable inclusion from stable-v6.6.41 commit 2ca2fd474d862c9b144ea4ed100ee4591f52d574 bugzilla: https://gitee.com/openeuler/kernel/issues/IAHMJO Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=2ca2fd474d862c9b144ea4ed100ee4591f52d574 -------------------------------- commit d92467ad9d9ee63a700934b9228a989ef671d511 upstream. When boost is set for CPUs using acpi-cpufreq, the policy is not updated which can cause boost to be incorrectly not reported. Fixes: 218a06a79d9a ("cpufreq: Support per-policy performance boost") Link: https://patch.msgid.link/20240626204723.6237-2-mario.limonciello@amd.com Suggested-by: Viresh Kumar Suggested-by: Gautham R. Shenoy Signed-off-by: Mario Limonciello Reviewed-by: Gautham R. Shenoy Acked-by: Viresh Kumar Cc: All applicable Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman Signed-off-by: ZhangPeng Signed-off-by: Qi Xi --- drivers/cpufreq/acpi-cpufreq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 0def3221a2e56..17933355947c4 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -883,8 +883,10 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (perf->states[0].core_frequency * 1000 != freq_table[0].frequency) pr_warn(FW_WARN "P-state 0 is not max freq\n"); - if (acpi_cpufreq_driver.set_boost) + if (acpi_cpufreq_driver.set_boost) { set_boost(policy, acpi_cpufreq_driver.boost_enabled); + policy->boost_enabled = acpi_cpufreq_driver.boost_enabled; + } return result; From 5614364f0296e916650a4c6e2a544d37b23bdff0 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Thu, 3 Jul 2025 11:19:10 +0800 Subject: [PATCH 102/227] cpufreq: ACPI: Remove set_boost in acpi_cpufreq_cpu_init() mainline inclusion from mainline-v6.7-rc5 commit 2b16c631832df6cf8782fb1fdc7df8a4f03f4f16 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBQYEH CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2b16c631832df6cf8782fb1fdc7df8a4f03f4f16 --------------------------------------------------------------------- At the end of cpufreq_online() in cpufreq.c, set_boost is executed and the per-policy boost flag is set to mirror the cpufreq_driver boost, so it is not necessary to run set_boost in acpi_cpufreq_cpu_init(). Signed-off-by: Lifeng Zheng Acked-by: Viresh Kumar Link: https://patch.msgid.link/20250117101457.1530653-5-zhenglifeng1@huawei.com [ rjw: Changelog edits ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Xinghai Cen Signed-off-by: Qi Xi --- drivers/cpufreq/acpi-cpufreq.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index 17933355947c4..dadf8860cc2d8 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -883,11 +883,6 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) if (perf->states[0].core_frequency * 1000 != freq_table[0].frequency) pr_warn(FW_WARN "P-state 0 is not max freq\n"); - if (acpi_cpufreq_driver.set_boost) { - set_boost(policy, acpi_cpufreq_driver.boost_enabled); - policy->boost_enabled = acpi_cpufreq_driver.boost_enabled; - } - return result; err_unreg: From 2474530d7884753735ec266ed02ba2dfefc8f147 Mon Sep 17 00:00:00 2001 From: Aboorva Devarajan Date: Thu, 3 Jul 2025 11:19:11 +0800 Subject: [PATCH 103/227] cpufreq: prevent NULL dereference in cpufreq_online() mainline inclusion from mainline-v6.7-rc5 commit 0813fd2e14ca6ecd4e6ba005a9766f08e26020d7 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBQYEH CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0813fd2e14ca6ecd4e6ba005a9766f08e26020d7 --------------------------------------------------------------------- Ensure cpufreq_driver->set_boost is non-NULL before using it in cpufreq_online() to prevent a potential NULL pointer dereference. Reported-by: Gautam Menghani Closes: https://lore.kernel.org/all/c9e56c5f54cc33338762c94e9bed7b5a0d5de812.camel@linux.ibm.com/ Fixes: da59223d340c ("cpufreq: Introduce a more generic way to set default per-policy boost flag") Suggested-by: Viresh Kumar Signed-off-by: Aboorva Devarajan Link: https://patch.msgid.link/20250205181347.2079272-1-aboorvad@linux.ibm.com [ rjw: Minor edits in the subject and changelog ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Xinghai Cen Signed-off-by: Qi Xi --- drivers/cpufreq/cpufreq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 017ae0a585be2..717eedd7868e2 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1566,7 +1566,8 @@ static int cpufreq_online(unsigned int cpu) policy->cdev = of_cpufreq_cooling_register(policy); /* Let the per-policy boost flag mirror the cpufreq_driver boost during init */ - if (policy->boost_enabled != cpufreq_boost_enabled()) { + if (cpufreq_driver->set_boost && + policy->boost_enabled != cpufreq_boost_enabled()) { policy->boost_enabled = cpufreq_boost_enabled(); ret = cpufreq_driver->set_boost(policy, policy->boost_enabled); if (ret) { From b225c0c7d897371cc8126314dfe0db9ce1067187 Mon Sep 17 00:00:00 2001 From: Jie Zhan Date: Thu, 3 Jul 2025 11:19:12 +0800 Subject: [PATCH 104/227] cpufreq: governor: Fix negative 'idle_time' handling in dbs_update() mainline inclusion from mainline-v6.14-rc3 commit 3698dd6b139dc37b35a9ad83d9330c1f99666c02 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC29P3 CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commi... --------------------------------------------------------------------- We observed an issue that the CPU frequency can't raise up with a 100% CPU load when NOHZ is off and the 'conservative' governor is selected. 'idle_time' can be negative if it's obtained from get_cpu_idle_time_jiffy() when NOHZ is off. This was found and explained in commit 9485e4ca0b48 ("cpufreq: governor: Fix handling of special cases in dbs_update()"). However, commit 7592019634f8 ("cpufreq: governors: Fix long idle detection logic in load calculation") introduced a comparison between 'idle_time' and 'samling_rate' to detect a long idle interval. While 'idle_time' is converted to int before comparison, it's actually promoted to unsigned again when compared with an unsigned 'sampling_rate'. Hence, this leads to wrong idle interval detection when it's in fact 100% busy and sets policy_dbs->idle_periods to a very large value. 'conservative' adjusts the frequency to minimum because of the large 'idle_periods', such that the frequency can't raise up. 'Ondemand' doesn't use policy_dbs->idle_periods so it fortunately avoids the issue. Correct negative 'idle_time' to 0 before any use of it in dbs_update(). Fixes: 7592019634f8 ("cpufreq: governors: Fix long idle detection logic in load calculation") Signed-off-by: Jie Zhan Reviewed-by: Chen Yu Link: https://patch.msgid.link/20250213035510.2402076-1-zhanjie9@hisilicon.com Signed-off-by: Rafael J. Wysocki Signed-off-by: Xinghai Cen Signed-off-by: Qi Xi --- drivers/cpufreq/cpufreq_governor.c | 45 +++++++++++++++--------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 55c80319d2684..5981e3ef9ce0e 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -145,7 +145,23 @@ unsigned int dbs_update(struct cpufreq_policy *policy) time_elapsed = update_time - j_cdbs->prev_update_time; j_cdbs->prev_update_time = update_time; - idle_time = cur_idle_time - j_cdbs->prev_cpu_idle; + /* + * cur_idle_time could be smaller than j_cdbs->prev_cpu_idle if + * it's obtained from get_cpu_idle_time_jiffy() when NOHZ is + * off, where idle_time is calculated by the difference between + * time elapsed in jiffies and "busy time" obtained from CPU + * statistics. If a CPU is 100% busy, the time elapsed and busy + * time should grow with the same amount in two consecutive + * samples, but in practice there could be a tiny difference, + * making the accumulated idle time decrease sometimes. Hence, + * in this case, idle_time should be regarded as 0 in order to + * make the further process correct. + */ + if (cur_idle_time > j_cdbs->prev_cpu_idle) + idle_time = cur_idle_time - j_cdbs->prev_cpu_idle; + else + idle_time = 0; + j_cdbs->prev_cpu_idle = cur_idle_time; if (ignore_nice) { @@ -162,7 +178,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy) * calls, so the previous load value can be used then. */ load = j_cdbs->prev_load; - } else if (unlikely((int)idle_time > 2 * sampling_rate && + } else if (unlikely(idle_time > 2 * sampling_rate && j_cdbs->prev_load)) { /* * If the CPU had gone completely idle and a task has @@ -189,30 +205,15 @@ unsigned int dbs_update(struct cpufreq_policy *policy) load = j_cdbs->prev_load; j_cdbs->prev_load = 0; } else { - if (time_elapsed >= idle_time) { + if (time_elapsed > idle_time) load = 100 * (time_elapsed - idle_time) / time_elapsed; - } else { - /* - * That can happen if idle_time is returned by - * get_cpu_idle_time_jiffy(). In that case - * idle_time is roughly equal to the difference - * between time_elapsed and "busy time" obtained - * from CPU statistics. Then, the "busy time" - * can end up being greater than time_elapsed - * (for example, if jiffies_64 and the CPU - * statistics are updated by different CPUs), - * so idle_time may in fact be negative. That - * means, though, that the CPU was busy all - * the time (on the rough average) during the - * last sampling interval and 100 can be - * returned as the load. - */ - load = (int)idle_time < 0 ? 100 : 0; - } + else + load = 0; + j_cdbs->prev_load = load; } - if (unlikely((int)idle_time > 2 * sampling_rate)) { + if (unlikely(idle_time > 2 * sampling_rate)) { unsigned int periods = idle_time / sampling_rate; if (periods < idle_periods) From c0f77501703a53ab9d6bfd25eb30ebd83fa799e5 Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Thu, 3 Jul 2025 11:19:13 +0800 Subject: [PATCH 105/227] cpufreq: Fix re-boost issue after hotplugging a CPU mainline inclusion from mainline-v6.7-rc5 commit 1608f0230510489d74a2e24e47054233b7e4678a category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBQYEH CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=1608f0230510489d74a2e24e47054233b7e4678a --------------------------------------------------------------------- It turns out that CPUX will stay on the base frequency after performing these operations: 1. boost all CPUs: echo 1 > /sys/devices/system/cpu/cpufreq/boost 2. offline one CPU: echo 0 > /sys/devices/system/cpu/cpuX/online 3. deboost all CPUs: echo 0 > /sys/devices/system/cpu/cpufreq/boost 4. online CPUX: echo 1 > /sys/devices/system/cpu/cpuX/online 5. boost all CPUs again: echo 1 > /sys/devices/system/cpu/cpufreq/boost This is because max_freq_req of the policy is not updated during the online process, and the value of max_freq_req before the last offline is retained. When the CPU is boosted again, freq_qos_update_request() will do nothing because the old value is the same as the new one. This causes the CPU to stay at the base frequency. Updating max_freq_req in cpufreq_online() will solve this problem. Signed-off-by: Lifeng Zheng Acked-by: Viresh Kumar Link: https://patch.msgid.link/20250117101457.1530653-2-zhenglifeng1@huawei.com [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki Signed-off-by: Xinghai Cen Signed-off-by: Qi Xi --- drivers/cpufreq/cpufreq.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 717eedd7868e2..99fa68e530ae8 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1476,6 +1476,10 @@ static int cpufreq_online(unsigned int cpu) blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_CREATE_POLICY, policy); + } else { + ret = freq_qos_update_request(policy->max_freq_req, policy->max); + if (ret < 0) + goto out_destroy_policy; } if (cpufreq_driver->get && has_target()) { From b931c3320dbf829b949799453e3d144fa59832e8 Mon Sep 17 00:00:00 2001 From: hbmm Date: Thu, 3 Jul 2025 15:41:08 +0800 Subject: [PATCH 106/227] Revert "gpiolib: acpi: Fix failed in acpi_gpiochip_find() by adding parent node match" This reverts commit 3d4046e77377d0e4dde09f14c2430726ee737abf. --- drivers/gpio/gpiolib-acpi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 44e8438206da2..27e3fb9938049 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -95,8 +95,10 @@ static bool acpi_gpio_deferred_req_irqs_done; static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) { - return device_match_acpi_handle(&gc->gpiodev->dev, data) || - (gc->parent && device_match_acpi_handle(gc->parent, data)); + if (!gc->parent) + return false; + + return ACPI_HANDLE(gc->parent) == data; } /** From 6bf70f427ca58e3b5ecdea6adcbb9ba21fe17d21 Mon Sep 17 00:00:00 2001 From: hbmm Date: Thu, 3 Jul 2025 15:42:10 +0800 Subject: [PATCH 107/227] Revert "ACPI: Use acpi_fetch_acpi_dev() instead of acpi_bus_get_device()" This reverts commit ba930d974f0bd8178fa20775495af3564cf852e7. --- drivers/acpi/acpi_video.c | 5 +++-- drivers/acpi/device_pm.c | 31 ++++++++++++++++++------------- drivers/acpi/dock.c | 5 +++-- drivers/acpi/pci_link.c | 12 ++++++++---- drivers/acpi/pci_root.c | 10 ++++++---- drivers/acpi/power.c | 7 ++++--- drivers/acpi/processor_driver.c | 10 +++------- drivers/acpi/processor_idle.c | 2 +- drivers/acpi/property.c | 11 ++++++----- drivers/acpi/resource.c | 4 ++-- drivers/acpi/thermal.c | 9 +++++---- drivers/acpi/video_detect.c | 6 ++++-- drivers/acpi/x86/s2idle.c | 4 ++-- 13 files changed, 65 insertions(+), 51 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index e87f4bf82d8b0..c8eb69d3e1d61 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1765,12 +1765,13 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context, { struct acpi_device *device = context; struct acpi_device *sibling; + int result; if (handle == device->handle) return AE_CTRL_TERMINATE; - sibling = acpi_fetch_acpi_dev(handle); - if (!sibling) + result = acpi_bus_get_device(handle, &sibling); + if (result) return AE_OK; if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 60a8d7a5ff03f..0028b6b51c877 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -285,12 +285,14 @@ EXPORT_SYMBOL(acpi_device_set_power); int acpi_bus_set_power(acpi_handle handle, int state) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); + struct acpi_device *device; + int result; - if (device) - return acpi_device_set_power(device, state); + result = acpi_bus_get_device(handle, &device); + if (result) + return result; - return -ENODEV; + return acpi_device_set_power(device, state); } EXPORT_SYMBOL(acpi_bus_set_power); @@ -408,20 +410,21 @@ EXPORT_SYMBOL_GPL(acpi_device_update_power); int acpi_bus_update_power(acpi_handle handle, int *state_p) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); - - if (device) - return acpi_device_update_power(device, state_p); + struct acpi_device *device; + int result; - return -ENODEV; + result = acpi_bus_get_device(handle, &device); + return result ? result : acpi_device_update_power(device, state_p); } EXPORT_SYMBOL_GPL(acpi_bus_update_power); bool acpi_bus_power_manageable(acpi_handle handle) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); + struct acpi_device *device; + int result; - return device && device->flags.power_manageable; + result = acpi_bus_get_device(handle, &device); + return result ? false : device->flags.power_manageable; } EXPORT_SYMBOL(acpi_bus_power_manageable); @@ -540,9 +543,11 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) bool acpi_bus_can_wakeup(acpi_handle handle) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); + struct acpi_device *device; + int result; - return device && device->wakeup.flags.valid; + result = acpi_bus_get_device(handle, &device); + return result ? false : device->wakeup.flags.valid; } EXPORT_SYMBOL(acpi_bus_can_wakeup); diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index ece9109db0586..7cf92158008fa 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -489,9 +489,10 @@ static ssize_t docked_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dock_station *dock_station = dev->platform_data; - struct acpi_device *adev = acpi_fetch_acpi_dev(dock_station->handle); + struct acpi_device *adev = NULL; - return sysfs_emit(buf, "%u\n", acpi_device_enumerated(adev)); + acpi_bus_get_device(dock_station->handle, &adev); + return snprintf(buf, PAGE_SIZE, "%u\n", acpi_device_enumerated(adev)); } static DEVICE_ATTR_RO(docked); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index d54fb8e54671d..cb7b900d9466c 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -606,10 +606,12 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link) int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); + int result; + struct acpi_device *device; struct acpi_pci_link *link; - if (!device) { + result = acpi_bus_get_device(handle, &device); + if (result) { acpi_handle_err(handle, "Invalid link device\n"); return -1; } @@ -656,10 +658,12 @@ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, */ int acpi_pci_link_free_irq(acpi_handle handle) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); + struct acpi_device *device; struct acpi_pci_link *link; + acpi_status result; - if (!device) { + result = acpi_bus_get_device(handle, &device); + if (result) { acpi_handle_err(handle, "Invalid link device\n"); return -1; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index d85b619108dae..223aa010dd8da 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -65,10 +65,11 @@ static struct acpi_scan_handler pci_root_handler = { */ int acpi_is_root_bridge(acpi_handle handle) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); int ret; + struct acpi_device *device; - if (!device) + ret = acpi_bus_get_device(handle, &device); + if (ret) return 0; ret = acpi_match_device_ids(device, root_device_ids); @@ -225,10 +226,11 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags) struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_root *root; + struct acpi_device *device; - if (!device || acpi_match_device_ids(device, root_device_ids)) + if (acpi_bus_get_device(handle, &device) || + acpi_match_device_ids(device, root_device_ids)) return NULL; root = acpi_driver_data(device); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index fe22366b4b385..c95eedd58f5bf 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -81,9 +81,9 @@ struct acpi_power_resource *to_power_resource(struct acpi_device *device) static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); + struct acpi_device *device; - if (!device) + if (acpi_bus_get_device(handle, &device)) return NULL; return to_power_resource(device); @@ -914,13 +914,14 @@ static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource struct acpi_device *acpi_add_power_resource(acpi_handle handle) { - struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_power_resource *resource; + struct acpi_device *device = NULL; union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; acpi_status status; int result; + acpi_bus_get_device(handle, &device); if (device) return device; diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 368a9edefd0cb..77541f939be3e 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -98,13 +98,8 @@ static int acpi_soft_cpu_online(unsigned int cpu) struct acpi_processor *pr = per_cpu(processors, cpu); struct acpi_device *device; - if (!pr) - return 0; - - device = acpi_fetch_acpi_dev(pr->handle); - if (!device) + if (!pr || acpi_bus_get_device(pr->handle, &device)) return 0; - /* * CPU got physically hotplugged and onlined for the first time: * Initialize missing things. @@ -130,8 +125,9 @@ static int acpi_soft_cpu_online(unsigned int cpu) static int acpi_soft_cpu_dead(unsigned int cpu) { struct acpi_processor *pr = per_cpu(processors, cpu); + struct acpi_device *device; - if (!pr || !acpi_fetch_acpi_dev(pr->handle)) + if (!pr || acpi_bus_get_device(pr->handle, &device)) return 0; acpi_processor_reevaluate_tstate(pr, true); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 756ffc9d10b7c..5fbb435b58122 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1132,7 +1132,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) status = acpi_get_parent(handle, &pr_ahandle); while (ACPI_SUCCESS(status)) { - d = acpi_fetch_acpi_dev(pr_ahandle); + acpi_bus_get_device(pr_ahandle, &d); handle = pr_ahandle; if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 617e52dc415d7..9ab7f7184343a 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -694,9 +694,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (index) return -ENOENT; - device = acpi_fetch_acpi_dev(obj->reference.handle); - if (!device) - return -EINVAL; + ret = acpi_bus_get_device(obj->reference.handle, &device); + if (ret) + return ret == -ENODEV ? -EINVAL : ret; if (!args) return 0; @@ -729,8 +729,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (element->type == ACPI_TYPE_LOCAL_REFERENCE) { struct fwnode_handle *ref_fwnode; - device = acpi_fetch_acpi_dev(element->reference.handle); - if (!device) + ret = acpi_bus_get_device(element->reference.handle, + &device); + if (ret) return -EINVAL; nargs = 0; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 27ea1317e33a6..57f34aa4f4295 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -982,9 +982,9 @@ static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth, { struct resource *res = context; struct acpi_device **consumer = (struct acpi_device **) ret; - struct acpi_device *adev = acpi_fetch_acpi_dev(handle); + struct acpi_device *adev; - if (!adev) + if (acpi_bus_get_device(handle, &adev)) return AE_OK; if (acpi_dev_consumes_res(adev, res)) { diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index d4608f61673ad..7e24554fe00f3 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -693,6 +693,7 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; struct acpi_device *dev; + acpi_status status; acpi_handle handle; int i; int j; @@ -710,8 +711,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, for (i = 0; i < tz->trips.passive.devices.count; i++) { handle = tz->trips.passive.devices.handles[i]; - dev = acpi_fetch_acpi_dev(handle); - if (dev != device) + status = acpi_bus_get_device(handle, &dev); + if (ACPI_FAILURE(status) || dev != device) continue; if (bind) result = @@ -736,8 +737,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, j < tz->trips.active[i].devices.count; j++) { handle = tz->trips.active[i].devices.handles[j]; - dev = acpi_fetch_acpi_dev(handle); - if (dev != device) + status = acpi_bus_get_device(handle, &dev); + if (ACPI_FAILURE(status) || dev != device) continue; if (bind) result = thermal_zone_bind_cooling_device diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index d55c143a49d7c..a5cb9e1d48bcc 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -59,16 +59,18 @@ static void acpi_video_parse_cmdline(void) static acpi_status find_video(acpi_handle handle, u32 lvl, void *context, void **rv) { - struct acpi_device *acpi_dev = acpi_fetch_acpi_dev(handle); long *cap = context; struct pci_dev *dev; + struct acpi_device *acpi_dev; static const struct acpi_device_id video_ids[] = { {ACPI_VIDEO_HID, 0}, {"", 0}, }; + if (acpi_bus_get_device(handle, &acpi_dev)) + return AE_OK; - if (acpi_dev && !acpi_match_device_ids(acpi_dev, video_ids)) { + if (!acpi_match_device_ids(acpi_dev, video_ids)) { dev = acpi_get_pci_dev(handle); if (!dev) return AE_OK; diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index ee3deea964fd4..ada989670d9b8 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -294,9 +294,9 @@ static void lpi_check_constraints(void) for (i = 0; i < lpi_constraints_table_size; ++i) { acpi_handle handle = lpi_constraints_table[i].handle; - struct acpi_device *adev = acpi_fetch_acpi_dev(handle); + struct acpi_device *adev; - if (!adev) + if (!handle || acpi_bus_get_device(handle, &adev)) continue; acpi_handle_debug(handle, From fbf83824ea746a27a3b68e7f2dd6eca018bde45c Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Tue, 31 Oct 2023 10:59:06 +0800 Subject: [PATCH 108/227] ipmi: Add erratum 162102203 config to enable workaround for SMS message processing timeout driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9A4AE CVE: NA -------------------------------------------------------------------------- Add CONFIG_HISILICON_ERRATUM_162102203. If this config option is enabled, it will compile a workaround modification in the ipmi driver for the SMS message processing timeout issue tracking by erratum 162102203. Signed-off-by: Devyn Liu --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 0e9512b98e98c..a6f1f15bcbd35 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -130,6 +130,7 @@ CONFIG_CRYPTO_AES_ARM64_CE_BLK=y CONFIG_CRYPTO_CHACHA20_NEON=m CONFIG_CRYPTO_AES_ARM64_BS=m CONFIG_HISILICON_ERRATUM_162100125=y +CONFIG_HISILICON_ERRATUM_162102203=y CONFIG_JUMP_LABEL=y CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y From 84a1a12ab048180bd0160e9ec92f3bf2d45b25a8 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 12 Nov 2021 14:32:27 +0100 Subject: [PATCH 109/227] drm: Move nomodeset kernel parameter to the DRM subsystem The "nomodeset" kernel cmdline parameter is handled by the vgacon driver but the exported vgacon_text_force() symbol is only used by DRM drivers. It makes much more sense for the parameter logic to be in the subsystem of the drivers that are making use of it. Let's move the vgacon_text_force() function and related logic to the DRM subsystem. While doing that, rename it to drm_firmware_drivers_only() and make it return true if "nomodeset" was used and false otherwise. This is a better description of the condition that the drivers are testing for. Suggested-by: Daniel Vetter Signed-off-by: Javier Martinez Canillas Acked-by: Thomas Zimmermann Acked-by: Jani Nikula Acked-by: Pekka Paalanen Acked-by: Greg Kroah-Hartman Link: https://patchwork.freedesktop.org/patch/msgid/20211112133230.1595307-4-javierm@redhat.com Signed-off-by: chenyi --- drivers/gpu/drm/Makefile | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 +-- drivers/gpu/drm/ast/ast_drv.c | 3 +-- drivers/gpu/drm/i915/i915_module.c | 4 ++-- drivers/gpu/drm/mgag200/mgag200_drv.c | 3 +-- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 ++-- drivers/gpu/drm/qxl/qxl_drv.c | 3 +-- drivers/gpu/drm/radeon/radeon_drv.c | 3 +-- drivers/gpu/drm/tiny/bochs.c | 3 +-- drivers/gpu/drm/tiny/cirrus.c | 4 ++-- drivers/gpu/drm/vboxvideo/vbox_drv.c | 3 +-- drivers/gpu/drm/virtio/virtgpu_drv.c | 3 +-- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 3 +-- drivers/video/console/vgacon.c | 21 -------------------- gpu/drm/drm_nomodeset.c | 26 +++++++++++++++++++++++++ include/drm/drm_drv.h | 5 +++++ include/linux/console.h | 6 ------ 17 files changed, 48 insertions(+), 51 deletions(-) create mode 100644 gpu/drm/drm_nomodeset.c diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index d89c2c6aab5cf..316888251eff6 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -36,6 +36,8 @@ drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o obj-$(CONFIG_DRM_DP_AUX_BUS) += drm_dp_aux_bus.o +obj-$(CONFIG_VGA_CONSOLE) += drm_nomodeset.o + drm_vram_helper-y := drm_gem_vram_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 57943e9008710..3ea7053372959 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -30,7 +30,6 @@ #include "amdgpu_drv.h" #include -#include #include #include #include @@ -2648,7 +2647,7 @@ static int __init amdgpu_init(void) { int r; - if (vgacon_text_force()) { + if (drm_firmware_drivers_only()) DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 86d5cd7b6318c..6d8613f6fe1cd 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -26,7 +26,6 @@ * Authors: Dave Airlie */ -#include #include #include @@ -233,7 +232,7 @@ static struct pci_driver ast_pci_driver = { static int __init ast_init(void) { - if (vgacon_text_force() && ast_modeset == -1) + if (drm_firmware_drivers_only() && ast_modeset == -1) return -EINVAL; if (ast_modeset == 0) diff --git a/drivers/gpu/drm/i915/i915_module.c b/drivers/gpu/drm/i915/i915_module.c index d8b4482c69d0e..6c49ac3d4458a 100644 --- a/drivers/gpu/drm/i915/i915_module.c +++ b/drivers/gpu/drm/i915/i915_module.c @@ -4,7 +4,7 @@ * Copyright © 2021 Intel Corporation */ -#include +#include #include "gem/i915_gem_context.h" #include "gem/i915_gem_object.h" @@ -31,7 +31,7 @@ static int i915_check_nomodeset(void) if (i915_modparams.modeset == 0) use_kms = false; - if (vgacon_text_force() && i915_modparams.modeset == -1) + if (drm_firmware_drivers_only() && i915_modparams.modeset == -1) use_kms = false; if (!use_kms) { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 6b9243713b3c6..740108a006ba5 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -6,7 +6,6 @@ * Dave Airlie */ -#include #include #include #include @@ -378,7 +377,7 @@ static struct pci_driver mgag200_pci_driver = { static int __init mgag200_init(void) { - if (vgacon_text_force() && mgag200_modeset == -1) + if (drm_firmware_drivers_only() && mgag200_modeset == -1) return -EINVAL; if (mgag200_modeset == 0) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index ae00a18bd45d9..1e023596074ac 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -22,7 +22,6 @@ * Authors: Ben Skeggs */ -#include #include #include #include @@ -32,6 +31,7 @@ #include #include +#include #include #include #include @@ -1364,7 +1364,7 @@ nouveau_drm_init(void) nouveau_display_options(); if (nouveau_modeset == -1) { - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) nouveau_modeset = 0; } diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index fc47b0deb021d..e4b16421500b4 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -29,7 +29,6 @@ #include "qxl_drv.h" -#include #include #include #include @@ -295,7 +294,7 @@ static struct drm_driver qxl_driver = { static int __init qxl_init(void) { - if (vgacon_text_force() && qxl_modeset == -1) + if (drm_firmware_drivers_only() && qxl_modeset == -1) return -EINVAL; if (qxl_modeset == 0) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index b74cebca1f89e..9539cdbf58e31 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -31,7 +31,6 @@ #include -#include #include #include #include @@ -637,7 +636,7 @@ static struct pci_driver radeon_kms_pci_driver = { static int __init radeon_module_init(void) { - if (vgacon_text_force() && radeon_modeset == -1) { + if (drm_firmware_drivers_only() && radeon_modeset == -1) DRM_INFO("VGACON disable radeon kernel modesetting.\n"); radeon_modeset = 0; } diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index eb8116ff0d902..65af2bcdc5eef 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include @@ -713,7 +712,7 @@ static struct pci_driver bochs_pci_driver = { static int __init bochs_init(void) { - if (vgacon_text_force() && bochs_modeset == -1) + if (drm_firmware_drivers_only() && bochs_modeset == -1) return -EINVAL; if (bochs_modeset == 0) diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index 2a81311b22172..8dfb3ada4fc06 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -16,7 +16,6 @@ * Copyright 1999-2001 Jeff Garzik */ -#include #include #include #include @@ -636,8 +635,9 @@ static struct pci_driver cirrus_pci_driver = { static int __init cirrus_init(void) { - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) return -EINVAL; + return pci_register_driver(&cirrus_pci_driver); } diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 2b81cb259d234..1f270398de992 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -7,7 +7,6 @@ * Michael Thayer */ -#include #include #include #include @@ -197,7 +196,7 @@ static const struct drm_driver driver = { static int __init vbox_init(void) { #ifdef CONFIG_VGA_CONSOLE - if (vgacon_text_force() && vbox_modeset == -1) + if (drm_firmware_drivers_only() && vbox_modeset == -1) return -EINVAL; #endif diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index ed85a78632563..e86248acc8bcc 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -27,7 +27,6 @@ */ #include -#include #include #include @@ -102,7 +101,7 @@ static int virtio_gpu_probe(struct virtio_device *vdev) struct drm_device *dev; int ret; - if (vgacon_text_force() && virtio_gpu_modeset == -1) + if (drm_firmware_drivers_only() && virtio_gpu_modeset == -1) return -EINVAL; if (virtio_gpu_modeset == 0) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 8449d09c06f7a..03937e710ab3d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -25,7 +25,6 @@ * **************************************************************************/ -#include #include #include #include @@ -1659,7 +1658,7 @@ static int __init vmwgfx_init(void) { int ret; - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) return -EINVAL; ret = pci_register_driver(&vmw_pci_driver); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 9a49ea6b5112f..576612f18d596 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -97,30 +97,9 @@ static int vga_video_font_height; static int vga_scan_lines __read_mostly; static unsigned int vga_rolled_over; /* last vc_origin offset before wrap */ -static bool vgacon_text_mode_force; static bool vga_hardscroll_enabled; static bool vga_hardscroll_user_enable = true; -bool vgacon_text_force(void) -{ - return vgacon_text_mode_force; -} -EXPORT_SYMBOL(vgacon_text_force); - -static int __init text_mode(char *str) -{ - vgacon_text_mode_force = true; - - pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); - pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); - pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); - - return 1; -} - -/* force text mode - used by kernel modesetting */ -__setup("nomodeset", text_mode); - static int __init no_scroll(char *str) { /* diff --git a/gpu/drm/drm_nomodeset.c b/gpu/drm/drm_nomodeset.c new file mode 100644 index 0000000000000..287edfb18b5d7 --- /dev/null +++ b/gpu/drm/drm_nomodeset.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +static bool drm_nomodeset; + +bool drm_firmware_drivers_only(void) +{ + return drm_nomodeset; +} +EXPORT_SYMBOL(drm_firmware_drivers_only); + +static int __init disable_modeset(char *str) +{ + drm_nomodeset = true; + + pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); + pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); + pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); + + return 1; +} + +/* Disable kernel modesetting */ +__setup("nomodeset", disable_modeset); diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 3d8608b30a7df..d344d1e8196d2 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -606,5 +606,10 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) int drm_dev_set_unique(struct drm_device *dev, const char *name); +#ifdef CONFIG_VGA_CONSOLE +extern bool drm_firmware_drivers_only(void); +#else +static inline bool drm_firmware_drivers_only(void) { return false; } +#endif #endif diff --git a/include/linux/console.h b/include/linux/console.h index a97f277cfdfa3..7cd758a4f44ed 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -219,12 +219,6 @@ extern atomic_t ignore_console_lock_warning; #define VESA_HSYNC_SUSPEND 2 #define VESA_POWERDOWN 3 -#ifdef CONFIG_VGA_CONSOLE -extern bool vgacon_text_force(void); -#else -static inline bool vgacon_text_force(void) { return false; } -#endif - extern void console_init(void); /* For deferred console takeover */ From ecb719765719746da8269e46d14f9fd3f5d9a7c6 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 22 Dec 2021 09:28:22 +0100 Subject: [PATCH 110/227] drm: Provide PCI module-init macros Provide helper macros to register PCI-based DRM drivers. The new macros behave like module_pci_driver() with an additional test if DRM modesetting has been enabled. Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20211222082831.196562-2-javierm@redhat.com Signed-off-by: chenyi --- Documentation/gpu/drm-internals.rst | 6 ++ include/drm/drm_module.h | 95 +++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 include/drm/drm_module.h diff --git a/Documentation/gpu/drm-internals.rst b/Documentation/gpu/drm-internals.rst index 607f78f0f189a..38afed24a75c9 100644 --- a/Documentation/gpu/drm-internals.rst +++ b/Documentation/gpu/drm-internals.rst @@ -75,6 +75,12 @@ update it, its value is mostly useless. The DRM core prints it to the kernel log at initialization time and passes it to userspace through the DRM_IOCTL_VERSION ioctl. +Module Initialization +--------------------- + +.. kernel-doc:: include/drm/drm_module.h + :doc: overview + Managing Ownership of the Framebuffer Aperture ---------------------------------------------- diff --git a/include/drm/drm_module.h b/include/drm/drm_module.h new file mode 100644 index 0000000000000..eb3fd7bcbec9d --- /dev/null +++ b/include/drm/drm_module.h @@ -0,0 +1,95 @@ +/* SPDX-License-Identifier: MIT */ + +#ifndef DRM_MODULE_H +#define DRM_MODULE_H + +#include + +#include + +/** + * DOC: overview + * + * This library provides helpers registering DRM drivers during module + * initialization and shutdown. The provided helpers act like bus-specific + * module helpers, such as module_pci_driver(), but respect additional + * parameters that control DRM driver registration. + * + * Below is an example of initializing a DRM driver for a device on the + * PCI bus. + * + * .. code-block:: c + * + * struct pci_driver my_pci_drv = { + * }; + * + * drm_module_pci_driver(my_pci_drv); + * + * The generated code will test if DRM drivers are enabled and register + * the PCI driver my_pci_drv. For more complex module initialization, you + * can still use module_init() and module_exit() in your driver. + */ + +/* + * PCI drivers + */ + +static inline int __init drm_pci_register_driver(struct pci_driver *pci_drv) +{ + if (drm_firmware_drivers_only()) + return -ENODEV; + + return pci_register_driver(pci_drv); +} + +/** + * drm_module_pci_driver - Register a DRM driver for PCI-based devices + * @__pci_drv: the PCI driver structure + * + * Registers a DRM driver for devices on the PCI bus. The helper + * macro behaves like module_pci_driver() but tests the state of + * drm_firmware_drivers_only(). For more complex module initialization, + * use module_init() and module_exit() directly. + * + * Each module may only use this macro once. Calling it replaces + * module_init() and module_exit(). + */ +#define drm_module_pci_driver(__pci_drv) \ + module_driver(__pci_drv, drm_pci_register_driver, pci_unregister_driver) + +static inline int __init +drm_pci_register_driver_if_modeset(struct pci_driver *pci_drv, int modeset) +{ + if (drm_firmware_drivers_only() && modeset == -1) + return -ENODEV; + if (modeset == 0) + return -ENODEV; + + return pci_register_driver(pci_drv); +} + +static inline void __exit +drm_pci_unregister_driver_if_modeset(struct pci_driver *pci_drv, int modeset) +{ + pci_unregister_driver(pci_drv); +} + +/** + * drm_module_pci_driver_if_modeset - Register a DRM driver for PCI-based devices + * @__pci_drv: the PCI driver structure + * @__modeset: an additional parameter that disables the driver + * + * This macro is deprecated and only provided for existing drivers. For + * new drivers, use drm_module_pci_driver(). + * + * Registers a DRM driver for devices on the PCI bus. The helper macro + * behaves like drm_module_pci_driver() with an additional driver-specific + * flag. If __modeset is 0, the driver has been disabled, if __modeset is + * -1 the driver state depends on the global DRM state. For all other + * values, the PCI driver has been enabled. The default should be -1. + */ +#define drm_module_pci_driver_if_modeset(__pci_drv, __modeset) \ + module_driver(__pci_drv, drm_pci_register_driver_if_modeset, \ + drm_pci_unregister_driver_if_modeset, __modeset) + +#endif From 9c7f23ea13acf148465f6cc62c0b263c0d4f3777 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 22 Dec 2021 09:28:27 +0100 Subject: [PATCH 111/227] drm: Provide platform module-init macro Provide a helper macro to register platform DRM drivers. The new macro behaves like module_platform_driver() with an additional test if DRM modesetting has been enabled. Signed-off-by: Javier Martinez Canillas Acked-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20211222082831.196562-7-javierm@redhat.com Signed-off-by: chenyi --- include/drm/drm_module.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/include/drm/drm_module.h b/include/drm/drm_module.h index eb3fd7bcbec9d..4db1ae03d9a52 100644 --- a/include/drm/drm_module.h +++ b/include/drm/drm_module.h @@ -4,6 +4,7 @@ #define DRM_MODULE_H #include +#include #include @@ -92,4 +93,33 @@ drm_pci_unregister_driver_if_modeset(struct pci_driver *pci_drv, int modeset) module_driver(__pci_drv, drm_pci_register_driver_if_modeset, \ drm_pci_unregister_driver_if_modeset, __modeset) +/* + * Platform drivers + */ + +static inline int __init +drm_platform_driver_register(struct platform_driver *platform_drv) +{ + if (drm_firmware_drivers_only()) + return -ENODEV; + + return platform_driver_register(platform_drv); +} + +/** + * drm_module_platform_driver - Register a DRM driver for platform devices + * @__platform_drv: the platform driver structure + * + * Registers a DRM driver for devices on the platform bus. The helper + * macro behaves like module_platform_driver() but tests the state of + * drm_firmware_drivers_only(). For more complex module initialization, + * use module_init() and module_exit() directly. + * + * Each module may only use this macro once. Calling it replaces + * module_init() and module_exit(). + */ +#define drm_module_platform_driver(__platform_drv) \ + module_driver(__platform_drv, drm_platform_driver_register, \ + platform_driver_unregister) + #endif From 220d1e8c59e840d1b2ce7e469be45f969acc8adb Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 22 Dec 2021 09:28:26 +0100 Subject: [PATCH 112/227] drm/hisilicon/hibmc: Replace module initialization with DRM helpers Replace module_pci_driver() with drm_module_pci_driver(). The DRM macro respects drm_firmware_drivers_only() and fails if the flag has been set. Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20211222082831.196562-6-javierm@redhat.com Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 9fa29e542fc4b..9e4ea3080664d 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "hibmc_drm_drv.h" @@ -393,7 +394,7 @@ static struct pci_driver hibmc_pci_driver = { .driver.pm = &hibmc_pm_ops, }; -module_pci_driver(hibmc_pci_driver); +drm_module_pci_driver(hibmc_pci_driver); MODULE_DEVICE_TABLE(pci, hibmc_pci_table); MODULE_AUTHOR("RongrongZou "); From cd24d91bd9ce89e5d0dbbbb2b00d7d8d615efeb2 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 18 Oct 2022 22:43:50 -0400 Subject: [PATCH 113/227] drm: Remove drm_mode_config::fb_base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fb_base in struct drm_mode_config has been unused for a long time. Some drivers set it and some don't leading to a very confusing state where the variable can't be relied upon, because there's no indication as to which driver sets it and which doesn't. The only usage of fb_base is internal to two drivers so instead of trying to force it into all the drivers to get it into a coherent state completely remove it. Signed-off-by: Zack Rusin Reviewed-by: Laurent Pinchart Reviewed-by: Thomas Zimmermann Acked-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20221019024401.394617-1-zack@kde.org Signed-off-by: chenyi --- drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c | 2 -- drivers/gpu/drm/amd/amdgpu/dce_v10_0.c | 2 -- drivers/gpu/drm/amd/amdgpu/dce_v11_0.c | 2 -- drivers/gpu/drm/amd/amdgpu/dce_v6_0.c | 1 - drivers/gpu/drm/amd/amdgpu/dce_v8_0.c | 2 -- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 2 -- drivers/gpu/drm/ast/ast_mode.c | 2 -- drivers/gpu/drm/gma500/framebuffer.c | 6 +++--- drivers/gpu/drm/gma500/psb_drv.h | 1 + drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 16 +++------------- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 3 --- drivers/gpu/drm/msm/msm_fbdev.c | 2 -- drivers/gpu/drm/nouveau/nouveau_display.c | 1 - drivers/gpu/drm/nouveau/nv04_fbcon.c | 6 ++++-- drivers/gpu/drm/omapdrm/omap_fbdev.c | 2 -- drivers/gpu/drm/qxl/qxl_display.c | 2 -- drivers/gpu/drm/radeon/radeon_display.c | 2 -- drivers/gpu/drm/radeon/radeon_fb.c | 2 +- drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c | 1 - drivers/gpu/drm/tegra/fb.c | 1 - drivers/gpu/drm/tiny/bochs.c | 1 - include/drm/drm_mode_config.h | 2 -- 22 files changed, 12 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c index 083f9c637a82e..964dbf911a45b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vkms.c @@ -493,8 +493,6 @@ static int amdgpu_vkms_sw_init(void *handle) adev_to_drm(adev)->mode_config.preferred_depth = 24; adev_to_drm(adev)->mode_config.prefer_shadow = 1; - adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base; - r = amdgpu_display_modeset_create_props(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index d1570a462a51a..34d7089057ca5 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2798,8 +2798,6 @@ static int dce_v10_0_sw_init(void *handle) adev_to_drm(adev)->mode_config.preferred_depth = 24; adev_to_drm(adev)->mode_config.prefer_shadow = 1; - adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base; - r = amdgpu_display_modeset_create_props(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index 18a7b3bd633b5..b3c49111e1201 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2916,8 +2916,6 @@ static int dce_v11_0_sw_init(void *handle) adev_to_drm(adev)->mode_config.preferred_depth = 24; adev_to_drm(adev)->mode_config.prefer_shadow = 1; - adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base; - r = amdgpu_display_modeset_create_props(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c index c7803dc2b2d53..1ea22d3c30c8d 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c @@ -2674,7 +2674,6 @@ static int dce_v6_0_sw_init(void *handle) adev_to_drm(adev)->mode_config.max_height = 16384; adev_to_drm(adev)->mode_config.preferred_depth = 24; adev_to_drm(adev)->mode_config.prefer_shadow = 1; - adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base; r = amdgpu_display_modeset_create_props(adev); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index b200b9e722d97..07b82d4313dd1 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -2699,8 +2699,6 @@ static int dce_v8_0_sw_init(void *handle) adev_to_drm(adev)->mode_config.preferred_depth = 24; adev_to_drm(adev)->mode_config.prefer_shadow = 1; - adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base; - r = amdgpu_display_modeset_create_props(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 7385efe699f88..cc34dd7ba62c1 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3866,8 +3866,6 @@ static int amdgpu_dm_mode_config_init(struct amdgpu_device *adev) /* indicates support for immediate flip */ adev_to_drm(adev)->mode_config.async_page_flip = true; - adev_to_drm(adev)->mode_config.fb_base = adev->gmc.aper_base; - state = kzalloc(sizeof(*state), GFP_KERNEL); if (!state) return -ENOMEM; diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c index 08ed0d08d03b8..d074a4919e0f1 100644 --- a/drivers/gpu/drm/ast/ast_mode.c +++ b/drivers/gpu/drm/ast/ast_mode.c @@ -1374,7 +1374,6 @@ static const struct drm_mode_config_funcs ast_mode_config_funcs = { int ast_mode_config_init(struct ast_private *ast) { struct drm_device *dev = &ast->base; - struct pci_dev *pdev = to_pci_dev(dev->dev); int ret; ret = drmm_mode_config_init(dev); @@ -1386,7 +1385,6 @@ int ast_mode_config_init(struct ast_private *ast) dev->mode_config.min_height = 0; dev->mode_config.preferred_depth = 24; dev->mode_config.prefer_shadow = 1; - dev->mode_config.fb_base = pci_resource_start(pdev, 0); if (ast->chip == AST2100 || ast->chip == AST2200 || diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c index 0b8648396fb24..94a27cf39045a 100644 --- a/drivers/gpu/drm/gma500/framebuffer.c +++ b/drivers/gpu/drm/gma500/framebuffer.c @@ -310,7 +310,7 @@ static int psbfb_create(struct drm_fb_helper *fb_helper, info->fbops = &psbfb_unaccel_ops; - info->fix.smem_start = dev->mode_config.fb_base; + info->fix.smem_start = dev_priv->fb_base; info->fix.smem_len = size; info->fix.ywrapstep = 0; info->fix.ypanstep = 0; @@ -320,7 +320,7 @@ static int psbfb_create(struct drm_fb_helper *fb_helper, info->screen_size = size; if (dev_priv->gtt.stolen_size) { - info->apertures->ranges[0].base = dev->mode_config.fb_base; + info->apertures->ranges[0].base = dev_priv->fb_base; info->apertures->ranges[0].size = dev_priv->gtt.stolen_size; } @@ -547,7 +547,7 @@ void psb_modeset_init(struct drm_device *dev) /* set memory base */ /* Oaktrail and Poulsbo should use BAR 2*/ - pci_read_config_dword(pdev, PSB_BSM, (u32 *)&(dev->mode_config.fb_base)); + pci_read_config_dword(pdev, PSB_BSM, (u32 *)&(dev_priv->fb_base)); /* num pipes is 2 for PSB but 1 for Mrst */ for (i = 0; i < dev_priv->num_pipe; i++) diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index f2bae270ca7b1..07d6453a8fbca 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -536,6 +536,7 @@ struct drm_psb_private { uint32_t blc_adj2; struct drm_fb_helper *fb_helper; + resource_size_t fb_base; /* Panel brightness */ int brightness; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 9e4ea3080664d..1fb4489380a26 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -109,7 +109,6 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) dev->mode_config.max_width = 1920; dev->mode_config.max_height = 1200; - dev->mode_config.fb_base = priv->fb_base; dev->mode_config.preferred_depth = 32; dev->mode_config.prefer_shadow = 1; @@ -224,7 +223,7 @@ static int hibmc_hw_map(struct hibmc_drm_private *priv) { struct drm_device *dev = &priv->dev; struct pci_dev *pdev = to_pci_dev(dev->dev); - resource_size_t addr, size, ioaddr, iosize; + resource_size_t ioaddr, iosize; ioaddr = pci_resource_start(pdev, 1); iosize = pci_resource_len(pdev, 1); @@ -234,16 +233,6 @@ static int hibmc_hw_map(struct hibmc_drm_private *priv) return -ENOMEM; } - addr = pci_resource_start(pdev, 0); - size = pci_resource_len(pdev, 0); - priv->fb_map = devm_ioremap(dev->dev, addr, size); - if (!priv->fb_map) { - drm_err(dev, "Cannot map framebuffer\n"); - return -ENOMEM; - } - priv->fb_base = addr; - priv->fb_size = size; - return 0; } @@ -283,7 +272,8 @@ static int hibmc_load(struct drm_device *dev) if (ret) goto err; - ret = drmm_vram_helper_init(dev, pci_resource_start(pdev, 0), priv->fb_size); + ret = drmm_vram_helper_init(dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); if (ret) { drm_err(dev, "Error initializing VRAM MM; %d\n", ret); goto err; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index 232057676ec50..a2dd1261035a3 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -34,9 +34,6 @@ struct hibmc_vdac { struct hibmc_drm_private { /* hw */ void __iomem *mmio; - void __iomem *fb_map; - resource_size_t fb_base; - resource_size_t fb_size; /* drm */ struct drm_device dev; diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 0daaeb54ff6f0..09296864457ff 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -110,8 +110,6 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, drm_fb_helper_fill_info(fbi, helper, sizes); - dev->mode_config.fb_base = paddr; - fbi->screen_base = msm_gem_get_vaddr(bo); if (IS_ERR(fbi->screen_base)) { ret = PTR_ERR(fbi->screen_base); diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index b8667bdc04ba0..c5fa326d34fb1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -675,7 +675,6 @@ nouveau_display_create(struct drm_device *dev) drm_mode_create_dvi_i_properties(dev); dev->mode_config.funcs = &nouveau_mode_config_funcs; - dev->mode_config.fb_base = device->func->resource_addr(device, 1); dev->mode_config.min_width = 0; dev->mode_config.min_height = 0; diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c index 92f3fb6765abf..c30b8dacd86b3 100644 --- a/drivers/gpu/drm/nouveau/nv04_fbcon.c +++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c @@ -137,6 +137,8 @@ nv04_fbcon_accel_init(struct fb_info *info) struct nouveau_channel *chan = drm->channel; struct nvif_device *device = &drm->client.device; struct nvif_push *push = chan->chan.push; + struct nvkm_device *nvkm_device = nvxx_device(&drm->client.device); + resource_size_t fb_base = nvkm_device->func->resource_addr(nvkm_device, 1); int surface_fmt, pattern_fmt, rect_fmt; int ret; @@ -210,8 +212,8 @@ nv04_fbcon_accel_init(struct fb_info *info) 0x0188, chan->vram.handle); PUSH_NVSQ(push, NV042, 0x0300, surface_fmt, 0x0304, info->fix.line_length | (info->fix.line_length << 16), - 0x0308, info->fix.smem_start - dev->mode_config.fb_base, - 0x030c, info->fix.smem_start - dev->mode_config.fb_base); + 0x0308, info->fix.smem_start - fb_base, + 0x030c, info->fix.smem_start - fb_base); PUSH_NVSQ(push, NV043, 0x0000, nfbdev->rop.handle); PUSH_NVSQ(push, NV043, 0x0300, 0x55); diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 42eac6ad12bdc..fa9937eaa5281 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -176,8 +176,6 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, drm_fb_helper_fill_info(fbi, helper, sizes); - dev->mode_config.fb_base = dma_addr; - fbi->screen_buffer = omap_gem_vaddr(fbdev->bo); fbi->screen_size = fbdev->bo->size; fbi->fix.smem_start = dma_addr; diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c index dc04412784a0d..33a6904feb494 100644 --- a/drivers/gpu/drm/qxl/qxl_display.c +++ b/drivers/gpu/drm/qxl/qxl_display.c @@ -1256,8 +1256,6 @@ int qxl_modeset_init(struct qxl_device *qdev) qdev->ddev.mode_config.max_width = 8192; qdev->ddev.mode_config.max_height = 8192; - qdev->ddev.mode_config.fb_base = qdev->vram_base; - drm_mode_create_suggested_offset_properties(&qdev->ddev); qxl_mode_create_hotplug_mode_update_property(qdev); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 6337fad441df3..422c96c9483ec 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -1601,8 +1601,6 @@ int radeon_modeset_init(struct radeon_device *rdev) rdev->ddev->mode_config.preferred_depth = 24; rdev->ddev->mode_config.prefer_shadow = 1; - rdev->ddev->mode_config.fb_base = rdev->mc.aper_base; - ret = radeon_modeset_create_props(rdev); if (ret) { return ret; diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index ca382fbf7a86a..712c5cc7fb535 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -275,7 +275,7 @@ static int radeonfb_create(struct drm_fb_helper *helper, drm_fb_helper_fill_info(info, &rfbdev->helper, sizes); /* setup aperture base/size for vesafb takeover */ - info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base; + info->apertures->ranges[0].base = rdev->mc.aper_base; info->apertures->ranges[0].size = rdev->mc.aper_size; /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c index 2fdc455c4ad7f..7670c74dc4a0b 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_fbdev.c @@ -90,7 +90,6 @@ static int rockchip_drm_fbdev_create(struct drm_fb_helper *helper, offset = fbi->var.xoffset * bytes_per_pixel; offset += fbi->var.yoffset * fb->pitches[0]; - dev->mode_config.fb_base = 0; fbi->screen_base = rk_obj->kvaddr + offset; fbi->screen_size = rk_obj->base.size; fbi->fix.smem_len = rk_obj->base.size; diff --git a/drivers/gpu/drm/tegra/fb.c b/drivers/gpu/drm/tegra/fb.c index cae8b8cbe9dd0..db6ce29a942b1 100644 --- a/drivers/gpu/drm/tegra/fb.c +++ b/drivers/gpu/drm/tegra/fb.c @@ -279,7 +279,6 @@ static int tegra_fbdev_probe(struct drm_fb_helper *helper, } } - drm->mode_config.fb_base = (resource_size_t)bo->iova; info->screen_base = (void __iomem *)bo->vaddr + offset; info->screen_size = size; info->fix.smem_start = (unsigned long)(bo->iova + offset); diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index 65af2bcdc5eef..2da8019b8e3ef 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -538,7 +538,6 @@ static int bochs_kms_init(struct bochs_device *bochs) bochs->dev->mode_config.max_width = 8192; bochs->dev->mode_config.max_height = 8192; - bochs->dev->mode_config.fb_base = bochs->fb_base; bochs->dev->mode_config.preferred_depth = 24; bochs->dev->mode_config.prefer_shadow = 0; bochs->dev->mode_config.prefer_shadow_fbdev = 1; diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index 1ddf7783fdf76..e6c7d6b763b76 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h @@ -346,7 +346,6 @@ struct drm_mode_config_funcs { * @max_width: maximum fb pixel width on this device * @max_height: maximum fb pixel height on this device * @funcs: core driver provided mode setting functions - * @fb_base: base address of the framebuffer * @poll_enabled: track polling support for this device * @poll_running: track polling status for this device * @delayed_event: track delayed poll uevent deliver for this device @@ -530,7 +529,6 @@ struct drm_mode_config { int min_width, min_height; int max_width, max_height; const struct drm_mode_config_funcs *funcs; - resource_size_t fb_base; /* output poll support */ bool poll_enabled; From 12d24d84969f5aea5c99d3d826499ace57546978 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Mon, 7 Nov 2022 13:53:28 +0100 Subject: [PATCH 114/227] drm/hisilicon/hibmc: Include for readl() and writel() Include to get readl() and writel() on S390. The error message is shown below and a bug report is at [1]. drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c:75:15: error: implicit declaration of function 'readl' [-Werror=implicit-function-declaration] 75 | reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); | ^~~~~ drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c:80:9: error: implicit declaration of function 'writel' [-Werror=implicit-function-declaration] 80 | writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); | ^~~~~~ Reported-by: kernel test robot Acked-by: Daniel Vetter Signed-off-by: Thomas Zimmermann Fixes: 45b64fd9f7ae ("drm/fb-helper: Remove unnecessary include statements") Cc: Thomas Zimmermann Cc: Javier Martinez Canillas Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: David Airlie Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Link: https://lore.kernel.org/dri-devel/202211060608.qrTg8b2E-lkp@intel.com/T/#u # [1] Link: https://patchwork.freedesktop.org/patch/msgid/20221107125329.12842-3-tzimmermann@suse.de Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 04a9eac316a17..82f3239ca7b40 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -11,6 +11,8 @@ * Jianhua Li */ +#include + #include #include #include From 154205dc870c00bfe3fe364a88c12018639a51fa Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Wed, 23 Nov 2022 12:53:42 +0100 Subject: [PATCH 115/227] drm/hisilicon/hibmc: Fix preferred depth and bpp Set the preferred color depth to 24 bits and the fbdev bpp to 32 bits. This will signal XRGB8888 as default format to clients. Signed-off-by: Thomas Zimmermann Reviewed-by: Javier Martinez Canillas Acked-by: Daniel Vetter Link: https://patchwork.freedesktop.org/patch/msgid/20221123115348.2521-2-tzimmermann@suse.de Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 1fb4489380a26..e2606f2880896 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -109,7 +109,7 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) dev->mode_config.max_width = 1920; dev->mode_config.max_height = 1200; - dev->mode_config.preferred_depth = 32; + dev->mode_config.preferred_depth = 24; dev->mode_config.prefer_shadow = 1; dev->mode_config.funcs = (void *)&hibmc_mode_funcs; @@ -353,7 +353,7 @@ static int hibmc_pci_probe(struct pci_dev *pdev, goto err_unload; } - drm_fbdev_generic_setup(dev, dev->mode_config.preferred_depth); + drm_fbdev_generic_setup(dev, 32); return 0; From 9ee523c2c86553e348f8c0c748fee3733d221af3 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 26 Oct 2022 11:19:35 -0500 Subject: [PATCH 116/227] drm: Move radeon and amdgpu Kconfig options into their directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most Kconfig options to enable a driver are in the Kconfig file inside the relevant directory, move these two to the same. Signed-off-by: Andrew Davis Reviewed-by: Christian König Signed-off-by: Alex Deucher Signed-off-by: chenyi --- drivers/gpu/drm/Kconfig | 36 ------------------------------ drivers/gpu/drm/amd/amdgpu/Kconfig | 29 ++++++++++++++++++++++++ drivers/gpu/drm/radeon/Kconfig | 30 +++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 36 deletions(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index cea777ae7fb92..ac462525a9753 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -223,44 +223,8 @@ source "drivers/gpu/drm/i2c/Kconfig" source "drivers/gpu/drm/arm/Kconfig" -config DRM_RADEON - tristate "ATI Radeon" - depends on DRM && PCI && MMU - depends on AGP || !AGP - select FW_LOADER - select DRM_KMS_HELPER - select DRM_TTM - select DRM_TTM_HELPER - select POWER_SUPPLY - select HWMON - select BACKLIGHT_CLASS_DEVICE - select INTERVAL_TREE - help - Choose this option if you have an ATI Radeon graphics card. There - are both PCI and AGP versions. You don't need to choose this to - run the Radeon in plain VGA mode. - - If M is selected, the module will be called radeon. - source "drivers/gpu/drm/radeon/Kconfig" -config DRM_AMDGPU - tristate "AMD GPU" - depends on DRM && PCI && MMU - select FW_LOADER - select DRM_KMS_HELPER - select DRM_SCHED - select DRM_TTM - select DRM_TTM_HELPER - select POWER_SUPPLY - select HWMON - select BACKLIGHT_CLASS_DEVICE - select INTERVAL_TREE - help - Choose this option if you have a recent AMD Radeon graphics card. - - If M is selected, the module will be called amdgpu. - source "drivers/gpu/drm/amd/amdgpu/Kconfig" source "drivers/gpu/drm/nouveau/Kconfig" diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig index 74a8105fd2c03..cb7542d7273aa 100644 --- a/drivers/gpu/drm/amd/amdgpu/Kconfig +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig @@ -1,4 +1,33 @@ # SPDX-License-Identifier: MIT + +config DRM_AMDGPU + tristate "AMD GPU" + depends on DRM && PCI && MMU + select FW_LOADER + select DRM_DISPLAY_DP_HELPER + select DRM_DISPLAY_HDMI_HELPER + select DRM_DISPLAY_HELPER + select DRM_KMS_HELPER + select DRM_SCHED + select DRM_TTM + select DRM_TTM_HELPER + select POWER_SUPPLY + select HWMON + select BACKLIGHT_CLASS_DEVICE + select INTERVAL_TREE + select DRM_BUDDY + # amdgpu depends on ACPI_VIDEO when ACPI is enabled, for select to work + # ACPI_VIDEO's dependencies must also be selected. + select INPUT if ACPI + select ACPI_VIDEO if ACPI + # On x86 ACPI_VIDEO also needs ACPI_WMI + select X86_PLATFORM_DEVICES if ACPI && X86 + select ACPI_WMI if ACPI && X86 + help + Choose this option if you have a recent AMD Radeon graphics card. + + If M is selected, the module will be called amdgpu. + config DRM_AMDGPU_SI bool "Enable amdgpu support for SI parts" depends on DRM_AMDGPU diff --git a/drivers/gpu/drm/radeon/Kconfig b/drivers/gpu/drm/radeon/Kconfig index 52819e7f1fca1..97a277f9a25e3 100644 --- a/drivers/gpu/drm/radeon/Kconfig +++ b/drivers/gpu/drm/radeon/Kconfig @@ -1,4 +1,34 @@ # SPDX-License-Identifier: MIT + +config DRM_RADEON + tristate "ATI Radeon" + depends on DRM && PCI && MMU + depends on AGP || !AGP + select FW_LOADER + select DRM_DISPLAY_DP_HELPER + select DRM_DISPLAY_HELPER + select DRM_KMS_HELPER + select DRM_TTM + select DRM_TTM_HELPER + select SND_HDA_COMPONENT if SND_HDA_CORE + select POWER_SUPPLY + select HWMON + select BACKLIGHT_CLASS_DEVICE + select INTERVAL_TREE + # radeon depends on ACPI_VIDEO when ACPI is enabled, for select to work + # ACPI_VIDEO's dependencies must also be selected. + select INPUT if ACPI + select ACPI_VIDEO if ACPI + # On x86 ACPI_VIDEO also needs ACPI_WMI + select X86_PLATFORM_DEVICES if ACPI && X86 + select ACPI_WMI if ACPI && X86 + help + Choose this option if you have an ATI Radeon graphics card. There + are both PCI and AGP versions. You don't need to choose this to + run the Radeon in plain VGA mode. + + If M is selected, the module will be called radeon. + config DRM_RADEON_USERPTR bool "Always enable userptr support" depends on DRM_RADEON From 571f639bc41c2e0987fd84a9c196376d19f507f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 19 Dec 2022 09:36:27 +0100 Subject: [PATCH 117/227] drm: Only select I2C_ALGOBIT for drivers that actually need it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While working on a drm driver that doesn't need the i2c algobit stuff I noticed that DRM selects this code even though only 8 drivers actually use it. While also only some drivers use i2c, keep the select for I2C for the next cleanup patch. Still prepare this already by also selecting I2C for the individual drivers. Signed-off-by: Uwe Kleine-König Reviewed-by: Javier Martinez Canillas Signed-off-by: Javier Martinez Canillas Link: https://patchwork.freedesktop.org/patch/msgid/20221219083627.1401627-1-u.kleine-koenig@pengutronix.de Signed-off-by: chenyi --- drivers/gpu/drm/Kconfig | 1 - drivers/gpu/drm/amd/amdgpu/Kconfig | 2 ++ drivers/gpu/drm/ast/Kconfig | 2 ++ drivers/gpu/drm/gma500/Kconfig | 2 ++ drivers/gpu/drm/hisilicon/hibmc/Kconfig | 2 ++ drivers/gpu/drm/i915/Kconfig | 2 ++ drivers/gpu/drm/mgag200/Kconfig | 2 ++ drivers/gpu/drm/nouveau/Kconfig | 2 ++ drivers/gpu/drm/radeon/Kconfig | 2 ++ 9 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ac462525a9753..c9ab4682c5908 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -12,7 +12,6 @@ menuconfig DRM select HDMI select FB_CMDLINE select I2C - select I2C_ALGOBIT select DMA_SHARED_BUFFER select SYNC_FILE # gallium uses SYS_kcmp for os_same_file_description() to de-duplicate diff --git a/drivers/gpu/drm/amd/amdgpu/Kconfig b/drivers/gpu/drm/amd/amdgpu/Kconfig index cb7542d7273aa..77006ecc7f3a8 100644 --- a/drivers/gpu/drm/amd/amdgpu/Kconfig +++ b/drivers/gpu/drm/amd/amdgpu/Kconfig @@ -13,6 +13,8 @@ config DRM_AMDGPU select DRM_TTM_HELPER select POWER_SUPPLY select HWMON + select I2C + select I2C_ALGOBIT select BACKLIGHT_CLASS_DEVICE select INTERVAL_TREE select DRM_BUDDY diff --git a/drivers/gpu/drm/ast/Kconfig b/drivers/gpu/drm/ast/Kconfig index fbcf2f45cef51..b8b3dedea416e 100644 --- a/drivers/gpu/drm/ast/Kconfig +++ b/drivers/gpu/drm/ast/Kconfig @@ -3,6 +3,8 @@ config DRM_AST tristate "AST server chips" depends on DRM && PCI && MMU select DRM_KMS_HELPER + select I2C + select I2C_ALGOBIT select DRM_VRAM_HELPER select DRM_TTM select DRM_TTM_HELPER diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig index 0cff20265f979..e26c3a24955d1 100644 --- a/drivers/gpu/drm/gma500/Kconfig +++ b/drivers/gpu/drm/gma500/Kconfig @@ -3,6 +3,8 @@ config DRM_GMA500 tristate "Intel GMA500/600/3600/3650 KMS Framebuffer" depends on DRM && PCI && X86 && MMU select DRM_KMS_HELPER + select I2C + select I2C_ALGOBIT # GMA500 depends on ACPI_VIDEO when ACPI is enabled, just like i915 select ACPI_VIDEO if ACPI select BACKLIGHT_CLASS_DEVICE if ACPI diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig index 4e41c144a2902..126504318a4f9 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig +++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig @@ -7,6 +7,8 @@ config DRM_HISI_HIBMC select DRM_VRAM_HELPER select DRM_TTM select DRM_TTM_HELPER + select I2C + select I2C_ALGOBIT help Choose this option if you have a Hisilicon Hibmc soc chipset. If M is selected the module will be called hibmc-drm. diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index fe6b34774483f..4ea418f426fff 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -13,6 +13,8 @@ config DRM_I915 select DRM_PANEL select DRM_MIPI_DSI select RELAY + select I2C + select I2C_ALGOBIT select IRQ_WORK # i915 depends on ACPI_VIDEO when ACPI is enabled # but for select to work, need to select ACPI_VIDEO's dependencies, ick diff --git a/drivers/gpu/drm/mgag200/Kconfig b/drivers/gpu/drm/mgag200/Kconfig index eec59658a938b..b28c5e4828f47 100644 --- a/drivers/gpu/drm/mgag200/Kconfig +++ b/drivers/gpu/drm/mgag200/Kconfig @@ -4,6 +4,8 @@ config DRM_MGAG200 depends on DRM && PCI && MMU select DRM_GEM_SHMEM_HELPER select DRM_KMS_HELPER + select I2C + select I2C_ALGOBIT help This is a KMS driver for Matrox G200 chips. It supports the original MGA G200 desktop chips and the server variants. It requires 0.3.0 diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index 9436310d0854c..8823f0b24c73e 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -7,6 +7,8 @@ config DRM_NOUVEAU select DRM_KMS_HELPER select DRM_TTM select DRM_TTM_HELPER + select I2C + select I2C_ALGOBIT select BACKLIGHT_CLASS_DEVICE if DRM_NOUVEAU_BACKLIGHT select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && INPUT select X86_PLATFORM_DEVICES if ACPI && X86 diff --git a/drivers/gpu/drm/radeon/Kconfig b/drivers/gpu/drm/radeon/Kconfig index 97a277f9a25e3..62a596d3a8919 100644 --- a/drivers/gpu/drm/radeon/Kconfig +++ b/drivers/gpu/drm/radeon/Kconfig @@ -15,6 +15,8 @@ config DRM_RADEON select HWMON select BACKLIGHT_CLASS_DEVICE select INTERVAL_TREE + select I2C + select I2C_ALGOBIT # radeon depends on ACPI_VIDEO when ACPI is enabled, for select to work # ACPI_VIDEO's dependencies must also be selected. select INPUT if ACPI From 7abfc1202a8ef1b0a76fd974d938b9fbd37ad350 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 13 Jun 2023 16:51:33 +0200 Subject: [PATCH 118/227] drm: Remove struct drm_driver.gem_prime_mmap All drivers initialize this field with drm_gem_prime_mmap(). Call the function directly and remove the field. Simplifies the code and resolves a long-standing TODO item. Signed-off-by: Thomas Zimmermann Reviewed-by: Alex Deucher Link: https://patchwork.freedesktop.org/patch/msgid/20230613150441.17720-3-tzimmermann@suse.de Signed-off-by: chenyi --- Documentation/gpu/todo.rst | 9 --------- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 1 - drivers/gpu/drm/drm_fb_helper.c | 5 +---- drivers/gpu/drm/drm_prime.c | 14 ++------------ drivers/gpu/drm/etnaviv/etnaviv_drv.c | 1 - drivers/gpu/drm/exynos/exynos_drm_drv.c | 1 - drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 1 - drivers/gpu/drm/lima/lima_drv.c | 1 - drivers/gpu/drm/mediatek/mtk_drm_drv.c | 1 - drivers/gpu/drm/msm/msm_drv.c | 1 - drivers/gpu/drm/msm/msm_drv.h | 1 - drivers/gpu/drm/msm/msm_gem_prime.c | 15 --------------- drivers/gpu/drm/nouveau/nouveau_drm.c | 1 - drivers/gpu/drm/panfrost/panfrost_drv.c | 1 - drivers/gpu/drm/pl111/pl111_drv.c | 1 - drivers/gpu/drm/radeon/radeon_drv.c | 1 - drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 1 - drivers/gpu/drm/v3d/v3d_drv.c | 1 - drivers/gpu/drm/virtio/virtgpu_drv.c | 1 - drivers/gpu/drm/xen/xen_drm_front.c | 1 - include/drm/drm_drv.h | 11 ----------- include/drm/drm_gem_cma_helper.h | 6 ++---- include/drm/drm_gem_shmem_helper.h | 1 - include/drm/drm_gem_vram_helper.h | 1 - 24 files changed, 5 insertions(+), 73 deletions(-) diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst index 67de1e94fdf76..d7629961f3b53 100644 --- a/Documentation/gpu/todo.rst +++ b/Documentation/gpu/todo.rst @@ -323,15 +323,6 @@ Contact: Daniel Vetter Level: Starter -struct drm_gem_object_funcs ---------------------------- - -GEM objects can now have a function table instead of having the callbacks on the -DRM driver struct. This is now the preferred way. Callbacks in drivers have been -converted, except for struct drm_driver.gem_prime_mmap. - -Level: Intermediate - Use DRM_MODESET_LOCK_ALL_* helpers instead of boilerplate --------------------------------------------------------- diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 3ea7053372959..2dd6eae1dc189 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2603,7 +2603,6 @@ static const struct drm_driver amdgpu_kms_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = amdgpu_gem_prime_import, - .gem_prime_mmap = drm_gem_prime_mmap, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 82960d5d4e733..0747819a464bf 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -2088,10 +2088,7 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { struct drm_fb_helper *fb_helper = info->par; - if (fb_helper->dev->driver->gem_prime_mmap) - return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma); - else - return -ENODEV; + return drm_gem_prime_mmap(fb_helper->buffer->gem, vma); } static bool drm_fbdev_use_iomem(struct fb_info *info) diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 57f8570ca3ece..8dac80640f0fd 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -708,8 +708,6 @@ EXPORT_SYMBOL(drm_gem_dmabuf_vunmap); * the same codepath that is used for regular GEM buffer mapping on the DRM fd. * The fake GEM offset is added to vma->vm_pgoff and &drm_driver->fops->mmap is * called to set up the mapping. - * - * Drivers can use this as their &drm_driver.gem_prime_mmap callback. */ int drm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) { @@ -765,23 +763,15 @@ EXPORT_SYMBOL(drm_gem_prime_mmap); * @vma: virtual address range * * Provides memory mapping for the buffer. This can be used as the - * &dma_buf_ops.mmap callback. It just forwards to &drm_driver.gem_prime_mmap, - * which should be set to drm_gem_prime_mmap(). - * - * FIXME: There's really no point to this wrapper, drivers which need anything - * else but drm_gem_prime_mmap can roll their own &dma_buf_ops.mmap callback. + * &dma_buf_ops.mmap callback. It just forwards to drm_gem_prime_mmap(). * * Returns 0 on success or a negative error code on failure. */ int drm_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *vma) { struct drm_gem_object *obj = dma_buf->priv; - struct drm_device *dev = obj->dev; - - if (!dev->driver->gem_prime_mmap) - return -ENOSYS; - return dev->driver->gem_prime_mmap(obj, vma); + return drm_gem_prime_mmap(obj, vma); } EXPORT_SYMBOL(drm_gem_dmabuf_mmap); diff --git a/drivers/gpu/drm/etnaviv/etnaviv_drv.c b/drivers/gpu/drm/etnaviv/etnaviv_drv.c index 7dcc6392792df..a2c54d762157b 100644 --- a/drivers/gpu/drm/etnaviv/etnaviv_drv.c +++ b/drivers/gpu/drm/etnaviv/etnaviv_drv.c @@ -477,7 +477,6 @@ static const struct drm_driver etnaviv_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = etnaviv_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = etnaviv_debugfs_init, #endif diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c index 2a7f96237f95a..8b5068de88a52 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_drv.c +++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -124,7 +124,6 @@ static const struct drm_driver exynos_drm_driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = exynos_drm_gem_prime_import, .gem_prime_import_sg_table = exynos_drm_gem_prime_import_sg_table, - .gem_prime_mmap = exynos_drm_gem_prime_mmap, .ioctls = exynos_ioctls, .num_ioctls = ARRAY_SIZE(exynos_ioctls), .fops = &exynos_drm_driver_fops, diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index e2606f2880896..897dcb8d89cb4 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -66,7 +66,6 @@ static const struct drm_driver hibmc_driver = { .debugfs_init = drm_vram_mm_debugfs_init, .dumb_create = hibmc_dumb_create, .dumb_map_offset = drm_gem_ttm_dumb_map_offset, - .gem_prime_mmap = drm_gem_prime_mmap, }; static int __maybe_unused hibmc_pm_suspend(struct device *dev) diff --git a/drivers/gpu/drm/lima/lima_drv.c b/drivers/gpu/drm/lima/lima_drv.c index 39cab4a55f572..e40534f6c5583 100644 --- a/drivers/gpu/drm/lima/lima_drv.c +++ b/drivers/gpu/drm/lima/lima_drv.c @@ -279,7 +279,6 @@ static const struct drm_driver lima_drm_driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .gem_prime_mmap = drm_gem_prime_mmap, }; struct lima_block_reader { diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c index 8b3928c2c7d78..f8fed6aa0c05a 100644 --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c @@ -333,7 +333,6 @@ static const struct drm_driver mtk_drm_driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = mtk_drm_gem_prime_import, .gem_prime_import_sg_table = mtk_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .fops = &mtk_drm_fops, .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 108f567a430c2..f81ade3b672a6 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -1107,7 +1107,6 @@ static const struct drm_driver msm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = msm_gem_prime_import_sg_table, - .gem_prime_mmap = msm_gem_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = msm_debugfs_init, #endif diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h index d9c3d1ca80ff8..60187d47a7022 100644 --- a/drivers/gpu/drm/msm/msm_drv.h +++ b/drivers/gpu/drm/msm/msm_drv.h @@ -296,7 +296,6 @@ unsigned long msm_gem_shrinker_shrink(struct drm_device *dev, unsigned long nr_t int msm_gem_shrinker_init(struct drm_device *dev); void msm_gem_shrinker_cleanup(struct drm_device *dev); -int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj); int msm_gem_prime_vmap(struct drm_gem_object *obj, struct dma_buf_map *map); void msm_gem_prime_vunmap(struct drm_gem_object *obj, struct dma_buf_map *map); diff --git a/drivers/gpu/drm/msm/msm_gem_prime.c b/drivers/gpu/drm/msm/msm_gem_prime.c index 02c70a0b2a036..8a2d94bd5df28 100644 --- a/drivers/gpu/drm/msm/msm_gem_prime.c +++ b/drivers/gpu/drm/msm/msm_gem_prime.c @@ -11,21 +11,6 @@ #include "msm_drv.h" #include "msm_gem.h" -int msm_gem_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma) -{ - int ret; - - /* Ensure the mmap offset is initialized. We lazily initialize it, - * so if it has not been first mmap'd directly as a GEM object, the - * mmap offset will not be already initialized. - */ - ret = drm_gem_create_mmap_offset(obj); - if (ret) - return ret; - - return drm_gem_prime_mmap(obj, vma); -} - struct sg_table *msm_gem_prime_get_sg_table(struct drm_gem_object *obj) { struct msm_gem_object *msm_obj = to_msm_bo(obj); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 1e023596074ac..1674b606965da 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1250,7 +1250,6 @@ driver_stub = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = nouveau_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .dumb_create = nouveau_display_dumb_create, .dumb_map_offset = drm_gem_ttm_dumb_map_offset, diff --git a/drivers/gpu/drm/panfrost/panfrost_drv.c b/drivers/gpu/drm/panfrost/panfrost_drv.c index de243825cdbff..0c8fa81cfe6a2 100644 --- a/drivers/gpu/drm/panfrost/panfrost_drv.c +++ b/drivers/gpu/drm/panfrost/panfrost_drv.c @@ -538,7 +538,6 @@ static const struct drm_driver panfrost_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, }; static int panfrost_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index 520301b405f12..7c683794a9bba 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -227,7 +227,6 @@ static const struct drm_driver pl111_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = pl111_gem_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, #if defined(CONFIG_DEBUG_FS) .debugfs_init = pl111_debugfs_init, diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 9539cdbf58e31..ac21f36f73d9b 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -615,7 +615,6 @@ static const struct drm_driver kms_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = radeon_gem_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index bfba9793d238d..7c71d35faeede 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -220,7 +220,6 @@ static const struct drm_driver rockchip_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, - .gem_prime_mmap = rockchip_gem_mmap_buf, .fops = &rockchip_drm_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, diff --git a/drivers/gpu/drm/v3d/v3d_drv.c b/drivers/gpu/drm/v3d/v3d_drv.c index 6407a006d6ec4..321d300c7b2d5 100644 --- a/drivers/gpu/drm/v3d/v3d_drv.c +++ b/drivers/gpu/drm/v3d/v3d_drv.c @@ -182,7 +182,6 @@ static const struct drm_driver v3d_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = v3d_prime_import_sg_table, - .gem_prime_mmap = drm_gem_prime_mmap, .ioctls = v3d_drm_ioctls, .num_ioctls = ARRAY_SIZE(v3d_drm_ioctls), diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index e86248acc8bcc..e457c71c1a81a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -207,7 +207,6 @@ static const struct drm_driver driver = { #endif .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_mmap = drm_gem_prime_mmap, .gem_prime_import = virtgpu_gem_prime_import, .gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table, diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c index 9f14d99c763c2..eb34b38dcf9b0 100644 --- a/drivers/gpu/drm/xen/xen_drm_front.c +++ b/drivers/gpu/drm/xen/xen_drm_front.c @@ -489,7 +489,6 @@ static const struct drm_driver xen_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import_sg_table = xen_drm_front_gem_import_sg_table, - .gem_prime_mmap = xen_drm_front_gem_prime_mmap, .dumb_create = xen_drm_drv_dumb_create, .fops = &xen_drm_dev_fops, .name = "xendrm-du", diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index d344d1e8196d2..eec308da88f40 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -347,17 +347,6 @@ struct drm_driver { struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt); - /** - * @gem_prime_mmap: - * - * mmap hook for GEM drivers, used to implement dma-buf mmap in the - * PRIME helpers. - * - * FIXME: There's way too much duplication going on here, and also moved - * to &drm_gem_object_funcs. - */ - int (*gem_prime_mmap)(struct drm_gem_object *obj, - struct vm_area_struct *vma); /** * @dumb_create: diff --git a/include/drm/drm_gem_cma_helper.h b/include/drm/drm_gem_cma_helper.h index cd13508acbc1c..7568a367a3498 100644 --- a/include/drm/drm_gem_cma_helper.h +++ b/include/drm/drm_gem_cma_helper.h @@ -121,8 +121,7 @@ int drm_gem_cma_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); .dumb_create = (dumb_create_func), \ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ - .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, \ - .gem_prime_mmap = drm_gem_prime_mmap + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table /** * DRM_GEM_CMA_DRIVER_OPS - CMA GEM driver operations @@ -159,8 +158,7 @@ int drm_gem_cma_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma); .dumb_create = dumb_create_func, \ .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ - .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table_vmap, \ - .gem_prime_mmap = drm_gem_prime_mmap + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table_vmap /** * DRM_GEM_CMA_DRIVER_OPS_VMAP - CMA GEM driver operations ensuring a virtual diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h index 311d66c9cf4b1..2934cc0241a31 100644 --- a/include/drm/drm_gem_shmem_helper.h +++ b/include/drm/drm_gem_shmem_helper.h @@ -287,7 +287,6 @@ int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, \ .prime_fd_to_handle = drm_gem_prime_fd_to_handle, \ .gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, \ - .gem_prime_mmap = drm_gem_prime_mmap, \ .dumb_create = drm_gem_shmem_dumb_create #endif /* __DRM_GEM_SHMEM_HELPER_H__ */ diff --git a/include/drm/drm_gem_vram_helper.h b/include/drm/drm_gem_vram_helper.h index d3cf06c9af652..43616ba9f6fe4 100644 --- a/include/drm/drm_gem_vram_helper.h +++ b/include/drm/drm_gem_vram_helper.h @@ -159,7 +159,6 @@ void drm_gem_vram_simple_display_pipe_cleanup_fb( .debugfs_init = drm_vram_mm_debugfs_init, \ .dumb_create = drm_gem_vram_driver_dumb_create, \ .dumb_map_offset = drm_gem_ttm_dumb_map_offset, \ - .gem_prime_mmap = drm_gem_prime_mmap /* * VRAM memory manager From d49a3aeb22891f5392c93d458138811383e0377b Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 3 Jul 2025 21:08:43 +0800 Subject: [PATCH 119/227] cpu/SMT: Provide a default topology_is_primary_thread() driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBZQQR ---------------------------------------------------------------------- Currently if architectures want to support HOTPLUG_SMT they need to provide a topology_is_primary_thread() telling the framework which thread in the SMT cannot offline. However arm64 doesn't have a restriction on which thread in the SMT cannot offline, a simplest choice is that just make 1st thread as the "primary" thread. So just make this as the default implementation in the framework and let architectures like x86 that have special primary thread to override this function (which they've already done). There's no need to provide a stub function if !CONFIG_SMP or !CONFIG_HOTPLUG_SMT. In such case the testing CPU is already the 1st CPU in the SMT so it's always the primary thread. Reviewed-by: Jonathan Cameron Reviewed-by: Pierre Gondois Reviewed-by: Dietmar Eggemann Reviewed-by: Yicong Yang Signed-off-by: Yicong Yang Signed-off-by: Hongye Lin Signed-off-by: Qi Xi --- arch/x86/include/asm/topology.h | 1 - arch/x86/kernel/smpboot.c | 1 + include/linux/topology.h | 24 ++++++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index 5b4f87329031e..8bf8d5546e970 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -152,7 +152,6 @@ static inline int topology_phys_to_logical_die(unsigned int die, unsigned int cpu) { return 0; } static inline int topology_max_die_per_package(void) { return 1; } static inline int topology_max_smt_threads(void) { return 1; } -static inline bool topology_is_primary_thread(unsigned int cpu) { return true; } static inline bool topology_smt_supported(void) { return false; } #endif diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 5726a3c9623e3..2c897ad8b0149 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -284,6 +284,7 @@ bool topology_is_primary_thread(unsigned int cpu) { return apic_id_is_primary_thread(per_cpu(x86_cpu_to_apicid, cpu)); } +#define topology_is_primary_thread topology_is_primary_thread /** * topology_smt_supported - Check whether SMT is supported by the CPUs diff --git a/include/linux/topology.h b/include/linux/topology.h index 80d27d717631c..3aa7a3ef67717 100644 --- a/include/linux/topology.h +++ b/include/linux/topology.h @@ -212,6 +212,30 @@ static inline const struct cpumask *cpu_smt_mask(int cpu) } #endif +#ifndef topology_is_primary_thread + +#define topology_is_primary_thread topology_is_primary_thread + +static inline bool topology_is_primary_thread(unsigned int cpu) +{ + /* + * When disabling SMT the primary thread of the SMT will remain + * enabled/active. Architectures do have a special primary thread + * (e.g. x86) needs to override this function. Otherwise can make + * the first thread in the SMT as the primary thread. + * + * The sibling cpumask of an offline CPU contains always the CPU + * itself for architectures using the implementation of + * CONFIG_GENERIC_ARCH_TOPOLOGY for building their topology. + * Other architectures not using CONFIG_GENERIC_ARCH_TOPOLOGY for + * building their topology have to check whether to use this default + * implementation or to override it. + */ + return cpu == cpumask_first(topology_sibling_cpumask(cpu)); +} + +#endif + static inline const struct cpumask *cpu_cpu_mask(int cpu) { return cpumask_of_node(cpu_to_node(cpu)); From 40c299ab8f07e18cdaba0db100b1fb23ee2bf45a Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 3 Jul 2025 21:08:44 +0800 Subject: [PATCH 120/227] arch_topology: Don't set cluster identifier as physical package identifier Currently as we parse the CPU topology from /cpu-map node from the device tree, we assign generated cluster count as the physical package identifier for each CPU which is wrong. The device tree bindings for CPU topology supports sockets to infer the socket or physical package identifier for a given CPU. Since it is fairly new and not supported on most of the old and existing systems, we can assume all such systems have single socket/physical package. Fix the physical package identifier to 0 by removing the assignment of cluster identifier to the same. Link: https://lore.kernel.org/r/20220704101605.1318280-17-sudeep.holla@arm.com Tested-by: Ionela Voinescu Tested-by: Conor Dooley Reviewed-by: Ionela Voinescu Signed-off-by: Sudeep Holla Signed-off-by: Qi Xi --- drivers/base/arch_topology.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 48f36d8b91761..728c30ac343c9 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -473,7 +473,6 @@ static int __init parse_cluster(struct device_node *cluster, int depth) bool leaf = true; bool has_cores = false; struct device_node *c; - static int package_id __initdata; int core_id = 0; int i, ret; @@ -512,7 +511,7 @@ static int __init parse_cluster(struct device_node *cluster, int depth) } if (leaf) { - ret = parse_core(c, package_id, core_id++); + ret = parse_core(c, 0, core_id++); } else { pr_err("%pOF: Non-leaf cluster with core %s\n", cluster, name); @@ -529,9 +528,6 @@ static int __init parse_cluster(struct device_node *cluster, int depth) if (leaf && !has_cores) pr_warn("%pOF: empty cluster\n", cluster); - if (leaf) - package_id++; - return 0; } From f927598958246cd17dc0a34900e85df366039922 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Thu, 3 Jul 2025 21:08:45 +0800 Subject: [PATCH 121/227] arch_topology: Add support for parsing sockets in /cpu-map Finally let us add support for socket nodes in /cpu-map in the device tree. Since this may not be present in all the old platforms and even most of the existing platforms, we need to assume absence of the socket node indicates that it is a single socket system and handle appropriately. Also it is likely that most single socket systems skip to as the node since it is optional. Link: https://lore.kernel.org/r/20220704101605.1318280-20-sudeep.holla@arm.com Tested-by: Ionela Voinescu Tested-by: Conor Dooley Reviewed-by: Ionela Voinescu Signed-off-by: Sudeep Holla Signed-off-by: Qi Xi --- drivers/base/arch_topology.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 728c30ac343c9..1dddf518da0a2 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -467,7 +467,7 @@ static int __init parse_core(struct device_node *core, int package_id, return 0; } -static int __init parse_cluster(struct device_node *cluster, int depth) +static int __init parse_cluster(struct device_node *cluster, int package_id, int depth) { char name[20]; bool leaf = true; @@ -487,7 +487,7 @@ static int __init parse_cluster(struct device_node *cluster, int depth) c = of_get_child_by_name(cluster, name); if (c) { leaf = false; - ret = parse_cluster(c, depth + 1); + ret = parse_cluster(c, package_id, depth + 1); of_node_put(c); if (ret != 0) return ret; @@ -511,7 +511,7 @@ static int __init parse_cluster(struct device_node *cluster, int depth) } if (leaf) { - ret = parse_core(c, 0, core_id++); + ret = parse_core(c, package_id, core_id++); } else { pr_err("%pOF: Non-leaf cluster with core %s\n", cluster, name); @@ -531,6 +531,32 @@ static int __init parse_cluster(struct device_node *cluster, int depth) return 0; } +static int __init parse_socket(struct device_node *socket) +{ + char name[20]; + struct device_node *c; + bool has_socket = false; + int package_id = 0, ret; + + do { + snprintf(name, sizeof(name), "socket%d", package_id); + c = of_get_child_by_name(socket, name); + if (c) { + has_socket = true; + ret = parse_cluster(c, package_id, 0); + of_node_put(c); + if (ret != 0) + return ret; + } + package_id++; + } while (c); + + if (!has_socket) + ret = parse_cluster(socket, 0, 0); + + return ret; +} + static int __init parse_dt_topology(void) { struct device_node *cn, *map; @@ -551,7 +577,7 @@ static int __init parse_dt_topology(void) if (!map) goto out; - ret = parse_cluster(map, 0); + ret = parse_socket(map); if (ret != 0) goto out_map; From 0d4d72821f1cb54fcf1967185efa4ab9298ff297 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 3 Jul 2025 21:08:46 +0800 Subject: [PATCH 122/227] cpu/SMT: Move SMT prototypes into cpu_smt.h In order to export the cpuhp_smt_control enum as part of the interface between generic and architecture code, the architecture code needs to include asm/topology.h. But that leads to circular header dependencies. So split the enum and related declarations into a separate header. [ ldufour: Reworded the commit's description ] Signed-off-by: Michael Ellerman Signed-off-by: Laurent Dufour Signed-off-by: Thomas Gleixner Tested-by: Zhang Rui Link: https://lore.kernel.org/r/20230705145143.40545-3-ldufour@linux.ibm.com Signed-off-by: Qi Xi --- arch/x86/include/asm/topology.h | 2 ++ include/linux/cpu.h | 25 +------------------------ include/linux/cpu_smt.h | 29 +++++++++++++++++++++++++++++ kernel/cpu.c | 1 + 4 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 include/linux/cpu_smt.h diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index 8bf8d5546e970..75d7de7e0b80f 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -135,6 +135,8 @@ static inline int topology_max_smt_threads(void) return __max_smt_threads; } +#include + int topology_update_package_map(unsigned int apicid, unsigned int cpu); int topology_update_die_map(unsigned int dieid, unsigned int cpu); int topology_phys_to_logical_pkg(unsigned int pkg); diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 7a77bde22b619..c39f41017af5e 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -19,6 +19,7 @@ #include #include #include +#include struct device; struct device_node; @@ -186,30 +187,6 @@ void cpuhp_report_idle_dead(void); static inline void cpuhp_report_idle_dead(void) { } #endif /* #ifdef CONFIG_HOTPLUG_CPU */ -enum cpuhp_smt_control { - CPU_SMT_ENABLED, - CPU_SMT_DISABLED, - CPU_SMT_FORCE_DISABLED, - CPU_SMT_NOT_SUPPORTED, - CPU_SMT_NOT_IMPLEMENTED, -}; - -#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT) -extern enum cpuhp_smt_control cpu_smt_control; -extern void cpu_smt_disable(bool force); -extern void cpu_smt_check_topology(void); -extern bool cpu_smt_possible(void); -extern int cpuhp_smt_enable(void); -extern int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval); -#else -# define cpu_smt_control (CPU_SMT_NOT_IMPLEMENTED) -static inline void cpu_smt_disable(bool force) { } -static inline void cpu_smt_check_topology(void) { } -static inline bool cpu_smt_possible(void) { return false; } -static inline int cpuhp_smt_enable(void) { return 0; } -static inline int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) { return 0; } -#endif - extern bool cpu_mitigations_off(void); extern bool cpu_mitigations_auto_nosmt(void); diff --git a/include/linux/cpu_smt.h b/include/linux/cpu_smt.h new file mode 100644 index 0000000000000..722c2e306fef3 --- /dev/null +++ b/include/linux/cpu_smt.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_CPU_SMT_H_ +#define _LINUX_CPU_SMT_H_ + +enum cpuhp_smt_control { + CPU_SMT_ENABLED, + CPU_SMT_DISABLED, + CPU_SMT_FORCE_DISABLED, + CPU_SMT_NOT_SUPPORTED, + CPU_SMT_NOT_IMPLEMENTED, +}; + +#if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT) +extern enum cpuhp_smt_control cpu_smt_control; +extern void cpu_smt_disable(bool force); +extern void cpu_smt_check_topology(void); +extern bool cpu_smt_possible(void); +extern int cpuhp_smt_enable(void); +extern int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval); +#else +# define cpu_smt_control (CPU_SMT_NOT_IMPLEMENTED) +static inline void cpu_smt_disable(bool force) { } +static inline void cpu_smt_check_topology(void) { } +static inline bool cpu_smt_possible(void) { return false; } +static inline int cpuhp_smt_enable(void) { return 0; } +static inline int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) { return 0; } +#endif + +#endif /* _LINUX_CPU_SMT_H_ */ diff --git a/kernel/cpu.c b/kernel/cpu.c index 1c370f87d86f5..fcc43aa23ed6c 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -412,6 +412,7 @@ static void lockdep_release_cpus_lock(void) void __weak arch_smt_update(void) { } #ifdef CONFIG_HOTPLUG_SMT + enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED; void __init cpu_smt_disable(bool force) From 3d9acc15e4f8a865c3549c3a64b298a7ada27818 Mon Sep 17 00:00:00 2001 From: Michael Ellerman Date: Thu, 3 Jul 2025 21:08:47 +0800 Subject: [PATCH 123/227] cpu/SMT: Store the current/max number of threads Some architectures allow partial SMT states at boot time, ie. when not all SMT threads are brought online. To support that the SMT code needs to know the maximum number of SMT threads, and also the currently configured number. The architecture code knows the max number of threads, so have the architecture code pass that value to cpu_smt_set_num_threads(). Note that although topology_max_smt_threads() exists, it is not configured early enough to be used here. As architecture, like PowerPC, allows the threads number to be set through the kernel command line, also pass that value. [ ldufour: Slightly reword the commit message ] [ ldufour: Rename cpu_smt_check_topology and add a num_threads argument ] Signed-off-by: Michael Ellerman Signed-off-by: Laurent Dufour Signed-off-by: Thomas Gleixner Tested-by: Zhang Rui Link: https://lore.kernel.org/r/20230705145143.40545-5-ldufour@linux.ibm.com Signed-off-by: Qi Xi --- arch/x86/kernel/cpu/common.c | 2 +- include/linux/cpu_smt.h | 8 ++++++-- kernel/cpu.c | 21 ++++++++++++++++++++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c index 919f391543025..3bb626f1d99d8 100644 --- a/arch/x86/kernel/cpu/common.c +++ b/arch/x86/kernel/cpu/common.c @@ -2253,7 +2253,7 @@ void __init arch_cpu_finalize_init(void) * identify_boot_cpu() initialized SMT support information, let the * core code know. */ - cpu_smt_check_topology(); + cpu_smt_set_num_threads(smp_num_siblings, smp_num_siblings); if (!IS_ENABLED(CONFIG_SMP)) { pr_info("CPU: "); diff --git a/include/linux/cpu_smt.h b/include/linux/cpu_smt.h index 722c2e306fef3..0c1664294b579 100644 --- a/include/linux/cpu_smt.h +++ b/include/linux/cpu_smt.h @@ -12,15 +12,19 @@ enum cpuhp_smt_control { #if defined(CONFIG_SMP) && defined(CONFIG_HOTPLUG_SMT) extern enum cpuhp_smt_control cpu_smt_control; +extern unsigned int cpu_smt_num_threads; extern void cpu_smt_disable(bool force); -extern void cpu_smt_check_topology(void); +extern void cpu_smt_set_num_threads(unsigned int num_threads, + unsigned int max_threads); extern bool cpu_smt_possible(void); extern int cpuhp_smt_enable(void); extern int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval); #else # define cpu_smt_control (CPU_SMT_NOT_IMPLEMENTED) +# define cpu_smt_num_threads 1 static inline void cpu_smt_disable(bool force) { } -static inline void cpu_smt_check_topology(void) { } +static inline void cpu_smt_set_num_threads(unsigned int num_threads, + unsigned int max_threads) { } static inline bool cpu_smt_possible(void) { return false; } static inline int cpuhp_smt_enable(void) { return 0; } static inline int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval) { return 0; } diff --git a/kernel/cpu.c b/kernel/cpu.c index fcc43aa23ed6c..075250153f947 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -414,6 +414,8 @@ void __weak arch_smt_update(void) { } #ifdef CONFIG_HOTPLUG_SMT enum cpuhp_smt_control cpu_smt_control __read_mostly = CPU_SMT_ENABLED; +static unsigned int cpu_smt_max_threads __ro_after_init; +unsigned int cpu_smt_num_threads __read_mostly = UINT_MAX; void __init cpu_smt_disable(bool force) { @@ -427,16 +429,33 @@ void __init cpu_smt_disable(bool force) pr_info("SMT: disabled\n"); cpu_smt_control = CPU_SMT_DISABLED; } + cpu_smt_num_threads = 1; } /* * The decision whether SMT is supported can only be done after the full * CPU identification. Called from architecture code. */ -void __init cpu_smt_check_topology(void) +void __init cpu_smt_set_num_threads(unsigned int num_threads, + unsigned int max_threads) { + WARN_ON(!num_threads || (num_threads > max_threads)); + if (!topology_smt_supported()) cpu_smt_control = CPU_SMT_NOT_SUPPORTED; + + cpu_smt_max_threads = max_threads; + + /* + * If SMT has been disabled via the kernel command line or SMT is + * not supported, set cpu_smt_num_threads to 1 for consistency. + * If enabled, take the architecture requested number of threads + * to bring up into account. + */ + if (cpu_smt_control != CPU_SMT_ENABLED) + cpu_smt_num_threads = 1; + else if (num_threads < cpu_smt_num_threads) + cpu_smt_num_threads = num_threads; } static int __init smt_cmdline_disable(char *str) From f35ea84066e457050957f83446f88caf12b7f795 Mon Sep 17 00:00:00 2001 From: Laurent Dufour Date: Thu, 3 Jul 2025 21:08:48 +0800 Subject: [PATCH 124/227] cpu/SMT: Remove topology_smt_supported() Since the maximum number of threads is now passed to cpu_smt_set_num_threads(), checking that value is enough to know whether SMT is supported. Suggested-by: Thomas Gleixner Signed-off-by: Laurent Dufour Signed-off-by: Thomas Gleixner Tested-by: Zhang Rui Link: https://lore.kernel.org/r/20230705145143.40545-6-ldufour@linux.ibm.com Signed-off-by: Qi Xi --- arch/x86/include/asm/topology.h | 2 -- arch/x86/kernel/smpboot.c | 8 -------- kernel/cpu.c | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) diff --git a/arch/x86/include/asm/topology.h b/arch/x86/include/asm/topology.h index 75d7de7e0b80f..39b3b4a12773e 100644 --- a/arch/x86/include/asm/topology.h +++ b/arch/x86/include/asm/topology.h @@ -142,7 +142,6 @@ int topology_update_die_map(unsigned int dieid, unsigned int cpu); int topology_phys_to_logical_pkg(unsigned int pkg); int topology_phys_to_logical_die(unsigned int die, unsigned int cpu); bool topology_is_primary_thread(unsigned int cpu); -bool topology_smt_supported(void); #else #define topology_max_packages() (1) static inline int @@ -154,7 +153,6 @@ static inline int topology_phys_to_logical_die(unsigned int die, unsigned int cpu) { return 0; } static inline int topology_max_die_per_package(void) { return 1; } static inline int topology_max_smt_threads(void) { return 1; } -static inline bool topology_smt_supported(void) { return false; } #endif static inline void arch_fix_phys_package_id(int num, u32 slot) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 2c897ad8b0149..58f683388fc59 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -286,14 +286,6 @@ bool topology_is_primary_thread(unsigned int cpu) } #define topology_is_primary_thread topology_is_primary_thread -/** - * topology_smt_supported - Check whether SMT is supported by the CPUs - */ -bool topology_smt_supported(void) -{ - return smp_num_siblings > 1; -} - /** * topology_phys_to_logical_pkg - Map a physical package id to a logical * diff --git a/kernel/cpu.c b/kernel/cpu.c index 075250153f947..3db5760b8d29f 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -441,7 +441,7 @@ void __init cpu_smt_set_num_threads(unsigned int num_threads, { WARN_ON(!num_threads || (num_threads > max_threads)); - if (!topology_smt_supported()) + if (max_threads == 1) cpu_smt_control = CPU_SMT_NOT_SUPPORTED; cpu_smt_max_threads = max_threads; From ee272ec200f97d3514af53eca046a383d4481b9d Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 3 Jul 2025 21:08:49 +0800 Subject: [PATCH 125/227] arch_topology: Support SMT control for OF based system driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBZQQR ---------------------------------------------------------------------- On building the topology from the devicetree, we've already gotten the SMT thread number of each core. Update the largest SMT thread number and enable the SMT control by the end of topology parsing. The framework's SMT control provides two interface to the users [1] through /sys/devices/system/cpu/smt/control: 1) enable SMT by writing "on" and disable by "off" 2) enable SMT by writing max_thread_number or disable by writing 1 Both method support to completely disable/enable the SMT cores so both work correctly for symmetric SMT platform and asymmetric platform with non-SMT and one type SMT cores like: core A: 1 thread core B: X (X!=1) threads Note that for a theoretically possible multiple SMT-X (X>1) core platform the SMT control is also supported as expected but only by writing the "on/off" method. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-devices-system-cpu#n542 Reviewed-by: Pierre Gondois Reviewed-by: Dietmar Eggemann Reviewed-by: Yicong Yang Signed-off-by: Yicong Yang Signed-off-by: Hongye Lin Signed-off-by: Qi Xi --- drivers/base/arch_topology.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 1dddf518da0a2..76244508ccc8d 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -390,6 +391,10 @@ core_initcall(free_raw_capacity); #endif #if defined(CONFIG_ARM64) || defined(CONFIG_RISCV) + +/* Used to enable the SMT control */ +static unsigned int max_smt_thread_num = 1; + /* * This function returns the logic cpu number of the node. * There are basically three kinds of return values: @@ -449,6 +454,8 @@ static int __init parse_core(struct device_node *core, int package_id, i++; } while (t); + max_smt_thread_num = max_t(unsigned int, max_smt_thread_num, i); + cpu = get_cpu_for_node(core); if (cpu >= 0) { if (!leaf) { @@ -554,6 +561,17 @@ static int __init parse_socket(struct device_node *socket) if (!has_socket) ret = parse_cluster(socket, 0, 0); + /* + * Reset the max_smt_thread_num to 1 on failure. Since on failure + * we need to notify the framework the SMT is not supported, but + * max_smt_thread_num can be initialized to the SMT thread number + * of the cores which are successfully parsed. + */ + if (ret) + max_smt_thread_num = 1; + + cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num); + return ret; } From 32cfbe86f16b6e87d5cf5e8c71fed35ea840ab40 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 3 Jul 2025 21:08:50 +0800 Subject: [PATCH 126/227] arm64: topology: Support SMT control on ACPI based system driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBZQQR ---------------------------------------------------------------------- For ACPI we'll build the topology from PPTT and we cannot directly get the SMT number of each core. Instead using a temporary xarray to record the heterogeneous information (from ACPI_PPTT_ACPI_IDENTICAL) and SMT information of the first core in its heterogeneous CPU cluster when building the topology. Then we can know the largest SMT number in the system. If a homogeneous system's using ACPI 6.2 or later, all the CPUs should be under the root node of PPTT. There'll be only one entry in the xarray and all the CPUs in the system will be assumed identical. The framework's SMT control provides two interface to the users [1] through /sys/devices/system/cpu/smt/control: 1) enable SMT by writing "on" and disable by "off" 2) enable SMT by writing max_thread_number or disable by writing 1 Both method support to completely disable/enable the SMT cores so both work correctly for symmetric SMT platform and asymmetric platform with non-SMT and one type SMT cores like: core A: 1 thread core B: X (X!=1) threads Note that for a theoretically possible multiple SMT-X (X>1) core platform the SMT control is also supported as expected but only by writing the "on/off" method. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/ABI/testing/sysfs-devices-system-cpu#n542 Reviewed-by: Jonathan Cameron Reviewed-by: Hanjun Guo Reviewed-by: Pierre Gondois Reviewed-by: Dietmar Eggemann Reviewed-by: Yicong Yang Signed-off-by: Yicong Yang Signed-off-by: Hongye Lin Signed-off-by: Qi Xi --- arch/arm64/kernel/topology.c | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index ea650f2b0e2e0..dcc7a666ecfee 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -15,8 +15,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -37,17 +39,28 @@ static bool __init acpi_cpu_is_threaded(int cpu) return !!is_threaded; } +struct cpu_smt_info { + unsigned int thread_num; + int core_id; +}; + /* * Propagate the topology information of the processor_topology_node tree to the * cpu_topology array. */ int __init parse_acpi_topology(void) { + unsigned int max_smt_thread_num = 1; + struct cpu_smt_info *entry; + struct xarray hetero_cpu; + unsigned long hetero_id; int cpu, topology_id; if (acpi_disabled) return 0; + xa_init(&hetero_cpu); + for_each_possible_cpu(cpu) { int i, cache_id; @@ -59,6 +72,34 @@ int __init parse_acpi_topology(void) cpu_topology[cpu].thread_id = topology_id; topology_id = find_acpi_cpu_topology(cpu, 1); cpu_topology[cpu].core_id = topology_id; + + /* + * In the PPTT, CPUs below a node with the 'identical + * implementation' flag have the same number of threads. + * Count the number of threads for only one CPU (i.e. + * one core_id) among those with the same hetero_id. + * See the comment of find_acpi_cpu_topology_hetero_id() + * for more details. + * + * One entry is created for each node having: + * - the 'identical implementation' flag + * - its parent not having the flag + */ + hetero_id = find_acpi_cpu_topology_hetero_id(cpu); + entry = xa_load(&hetero_cpu, hetero_id); + if (!entry) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + WARN_ON_ONCE(!entry); + + if (entry) { + entry->core_id = topology_id; + entry->thread_num = 1; + xa_store(&hetero_cpu, hetero_id, + entry, GFP_KERNEL); + } + } else if (entry->core_id == topology_id) { + entry->thread_num++; + } } else { cpu_topology[cpu].thread_id = -1; cpu_topology[cpu].core_id = topology_id; @@ -81,6 +122,20 @@ int __init parse_acpi_topology(void) } } + /* + * This is a short loop since the number of XArray elements is the + * number of heterogeneous CPU clusters. On a homogeneous system + * there's only one entry in the XArray. + */ + xa_for_each(&hetero_cpu, hetero_id, entry) { + max_smt_thread_num = max(max_smt_thread_num, entry->thread_num); + xa_erase(&hetero_cpu, hetero_id); + kfree(entry); + } + + cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num); + xa_destroy(&hetero_cpu); + return 0; } #endif From 75df7f0a5dbd1d05dafb9e926a6ab1b9ee8ca2bb Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 3 Jul 2025 21:08:51 +0800 Subject: [PATCH 127/227] arm64: Kconfig: Enable HOTPLUG_SMT driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBZQQR ---------------------------------------------------------------------- Enable HOTPLUG_SMT for SMT control. Reviewed-by: Jonathan Cameron Reviewed-by: Pierre Gondois Reviewed-by: Dietmar Eggemann Reviewed-by: Yicong Yang Signed-off-by: Yicong Yang Signed-off-by: Hongye Lin Signed-off-by: Qi Xi --- arch/arm64/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 01281aad440bf..6890847c1a134 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -203,6 +203,7 @@ config ARM64 select HAVE_KPROBES select HAVE_KRETPROBES select HAVE_GENERIC_VDSO + select HOTPLUG_SMT if HOTPLUG_CPU select IOMMU_DMA if IOMMU_SUPPORT select IRQ_DOMAIN select IRQ_FORCED_THREADING From cda58d9aed16d0aec1cda7fe6e113faf538ea4d3 Mon Sep 17 00:00:00 2001 From: Joey Gouly Date: Thu, 3 Jul 2025 21:08:52 +0800 Subject: [PATCH 128/227] arm64: add HWCAP for FEAT_HBC (hinted conditional branches) Add a HWCAP for FEAT_HBC, so that userspace can make a decision on using this feature. Signed-off-by: Joey Gouly Cc: Catalin Marinas Cc: Will Deacon Link: https://lore.kernel.org/r/20230804143746.3900803-2-joey.gouly@arm.com Signed-off-by: Will Deacon Signed-off-by: Qi Xi --- arch/arm64/include/asm/hwcap.h | 1 + arch/arm64/include/uapi/asm/hwcap.h | 1 + arch/arm64/kernel/cpufeature.c | 3 ++- arch/arm64/kernel/cpuinfo.c | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index f68fbb2074730..2155094ef9f73 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -108,6 +108,7 @@ #define KERNEL_HWCAP_ECV __khwcap2_feature(ECV) #define KERNEL_HWCAP_AFP __khwcap2_feature(AFP) #define KERNEL_HWCAP_RPRES __khwcap2_feature(RPRES) +#define KERNEL_HWCAP_HBC __khwcap2_feature(HBC) /* * This yields a mask that user programs can use to figure out what diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h index f03731847d9df..0a3df77b8b649 100644 --- a/arch/arm64/include/uapi/asm/hwcap.h +++ b/arch/arm64/include/uapi/asm/hwcap.h @@ -78,5 +78,6 @@ #define HWCAP2_ECV (1 << 19) #define HWCAP2_AFP (1 << 20) #define HWCAP2_RPRES (1 << 21) +#define HWCAP2_HBC (1UL << 22) #endif /* _UAPI__ASM_HWCAP_H */ diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 752e1001a5c7e..433f95c7bb949 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -232,7 +232,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar1[] = { }; static const struct arm64_ftr_bits ftr_id_aa64isar2[] = { - ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_HIGHER_SAFE, ID_AA64ISAR2_CLEARBHB_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_SAFE, ID_AA64ISAR2_CLEARBHB_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_RPRES_SHIFT, 4, 0), ARM64_FTR_END, }; @@ -2507,6 +2507,7 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { HWCAP_CAP(SYS_ID_AA64MMFR0_EL1, ID_AA64MMFR0_ECV_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_ECV), HWCAP_CAP(SYS_ID_AA64MMFR1_EL1, ID_AA64MMFR1_AFP_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_AFP), HWCAP_CAP(SYS_ID_AA64ISAR2_EL1, ID_AA64ISAR2_RPRES_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_RPRES), + HWCAP_CAP(ID_AA64ISAR2_EL1, BC, IMP, CAP_HWCAP, KERNEL_HWCAP_HBC), {}, }; diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index d72898af05b4c..04a7d2a974ce7 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -97,6 +97,7 @@ static const char *const hwcap_str[] = { [KERNEL_HWCAP_ECV] = "ecv", [KERNEL_HWCAP_AFP] = "afp", [KERNEL_HWCAP_RPRES] = "rpres", + [KERNEL_HWCAP_HBC] = "hbc", }; #ifdef CONFIG_COMPAT From 91f78fb7fd0e531cb60e7c7eb0b07938df7b337b Mon Sep 17 00:00:00 2001 From: Kristina Martsenko Date: Thu, 3 Jul 2025 21:08:53 +0800 Subject: [PATCH 129/227] arm64: trap implementation defined functionality in userspace The Arm v8.8 extension adds a new control FEAT_TIDCP1 that allows the kernel to disable all implementation-defined system registers and instructions in userspace. This can improve robustness against covert channels between processes, for example in cases where the firmware or hardware didn't disable that functionality by default. The kernel does not currently support any implementation-defined features, as there are no hwcaps for any such features, so disable all imp-def features unconditionally. Any use of imp-def instructions will result in a SIGILL being delivered to the process (same as for undefined instructions). Signed-off-by: Kristina Martsenko Link: https://lore.kernel.org/r/20220622115424.683520-1-kristina.martsenko@arm.com Signed-off-by: Will Deacon Signed-off-by: Qi Xi --- arch/arm64/include/asm/sysreg.h | 4 ++++ arch/arm64/kernel/cpufeature.c | 18 ++++++++++++++++++ arch/arm64/tools/cpucaps | 1 + 3 files changed, 23 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 3cafd4ef0c74a..b8d00be796cf0 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -907,6 +907,7 @@ /* id_aa64mmfr1 */ #define ID_AA64MMFR1_ECBHB_SHIFT 60 +#define ID_AA64MMFR1_TIDCP1_SHIFT 52 #define ID_AA64MMFR1_AFP_SHIFT 44 #define ID_AA64MMFR1_ETS_SHIFT 36 #define ID_AA64MMFR1_TWED_SHIFT 32 @@ -922,6 +923,9 @@ #define ID_AA64MMFR1_VMIDBITS_8 0 #define ID_AA64MMFR1_VMIDBITS_16 2 +#define ID_AA64MMFR1_TIDCP1_NI 0 +#define ID_AA64MMFR1_TIDCP1_IMP 1 + /* id_aa64mmfr2 */ #define ID_AA64MMFR2_E0PD_SHIFT 60 #define ID_AA64MMFR2_EVT_SHIFT 56 diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 433f95c7bb949..e0b79f932a5f3 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -337,6 +337,7 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = { }; static const struct arm64_ftr_bits ftr_id_aa64mmfr1[] = { + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_TIDCP1_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_AFP_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_ETS_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_TWED_SHIFT, 4, 0), @@ -1943,6 +1944,11 @@ static bool is_kvm_protected_mode(const struct arm64_cpu_capabilities *entry, in } #endif /* CONFIG_KVM */ +static void cpu_trap_el0_impdef(const struct arm64_cpu_capabilities *__unused) +{ + sysreg_clear_set(sctlr_el1, 0, SCTLR_EL1_TIDCP); +} + /* Internal helper functions to match cpu capability type */ static bool cpucap_late_cpu_optional(const struct arm64_cpu_capabilities *cap) @@ -2385,6 +2391,18 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, .min_field_value = 1, }, + { + .desc = "Trap EL0 IMPLEMENTATION DEFINED functionality", + .capability = ARM64_HAS_TIDCP1, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .sys_reg = SYS_ID_AA64MMFR1_EL1, + .sign = FTR_UNSIGNED, + .field_pos = ID_AA64MMFR1_TIDCP1_SHIFT, + .field_width = 4, + .min_field_value = ID_AA64MMFR1_TIDCP1_IMP, + .matches = has_cpuid_feature, + .cpu_enable = cpu_trap_el0_impdef, + }, {}, }; diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index a590cabe890c5..c44cd3b076e9a 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -34,6 +34,7 @@ HAS_RNG HAS_SB HAS_STAGE2_FWB HAS_SYSREG_GIC_CPUIF +HAS_TIDCP1 HAS_TLB_RANGE HAS_VIRT_HOST_EXTN HW_DBM From 9680226ddc22f5c1c44785e78973674b53c31bdb Mon Sep 17 00:00:00 2001 From: Yu Liao Date: Thu, 3 Jul 2025 21:08:54 +0800 Subject: [PATCH 130/227] tick/broadcast: Make takeover of broadcast hrtimer reliable stable inclusion from stable-v6.6.44 commit 3a58c590f6bd1d20eb1e76c5cea31c36cc032339 bugzilla: https://gitee.com/openeuler/kernel/issues/IAHMJO Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=3a58c590f6bd1d20eb1e76c5cea31c36cc032339 -------------------------------- commit f7d43dd206e7e18c182f200e67a8db8c209907fa upstream. Running the LTP hotplug stress test on a aarch64 machine results in rcu_sched stall warnings when the broadcast hrtimer was owned by the un-plugged CPU. The issue is the following: CPU1 (owns the broadcast hrtimer) CPU2 tick_broadcast_enter() // shutdown local timer device broadcast_shutdown_local() ... tick_broadcast_exit() clockevents_switch_state(dev, CLOCK_EVT_STATE_ONESHOT) // timer device is not programmed cpumask_set_cpu(cpu, tick_broadcast_force_mask) initiates offlining of CPU1 take_cpu_down() /* * CPU1 shuts down and does not * send broadcast IPI anymore */ takedown_cpu() hotplug_cpu__broadcast_tick_pull() // move broadcast hrtimer to this CPU clockevents_program_event() bc_set_next() hrtimer_start() /* * timer device is not programmed * because only the first expiring * timer will trigger clockevent * device reprogramming */ What happens is that CPU2 exits broadcast mode with force bit set, then the local timer device is not reprogrammed and CPU2 expects to receive the expired event by the broadcast IPI. But this does not happen because CPU1 is offlined by CPU2. CPU switches the clockevent device to ONESHOT state, but does not reprogram the device. The subsequent reprogramming of the hrtimer broadcast device does not program the clockevent device of CPU2 either because the pending expiry time is already in the past and the CPU expects the event to be delivered. As a consequence all CPUs which wait for a broadcast event to be delivered are stuck forever. Fix this issue by reprogramming the local timer device if the broadcast force bit of the CPU is set so that the broadcast hrtimer is delivered. [ tglx: Massage comment and change log. Add Fixes tag ] Fixes: 989dcb645ca7 ("tick: Handle broadcast wakeup of multiple cpus") Signed-off-by: Yu Liao Signed-off-by: Thomas Gleixner Cc: stable@vger.kernel.org Link: https://lore.kernel.org/r/20240711124843.64167-1-liaoyu15@huawei.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: ZhangPeng Signed-off-by: Qi Xi --- kernel/time/tick-broadcast.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 0916cc9adb828..ba551ec546f52 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -1137,6 +1137,7 @@ void tick_broadcast_switch_to_oneshot(void) #ifdef CONFIG_HOTPLUG_CPU void hotplug_cpu__broadcast_tick_pull(int deadcpu) { + struct tick_device *td = this_cpu_ptr(&tick_cpu_device); struct clock_event_device *bc; unsigned long flags; @@ -1144,6 +1145,28 @@ void hotplug_cpu__broadcast_tick_pull(int deadcpu) bc = tick_broadcast_device.evtdev; if (bc && broadcast_needs_cpu(bc, deadcpu)) { + /* + * If the broadcast force bit of the current CPU is set, + * then the current CPU has not yet reprogrammed the local + * timer device to avoid a ping-pong race. See + * ___tick_broadcast_oneshot_control(). + * + * If the broadcast device is hrtimer based then + * programming the broadcast event below does not have any + * effect because the local clockevent device is not + * running and not programmed because the broadcast event + * is not earlier than the pending event of the local clock + * event device. As a consequence all CPUs waiting for a + * broadcast event are stuck forever. + * + * Detect this condition and reprogram the cpu local timer + * device to avoid the starvation. + */ + if (tick_check_broadcast_expired()) { + cpumask_clear_cpu(smp_processor_id(), tick_broadcast_force_mask); + tick_program_event(td->evtdev->next_event, 1); + } + /* This moves the broadcast assignment to this CPU: */ clockevents_program_event(bc, bc->next_event, 1); } From 46aa8c6b5d2746ff0f981da7dae28f4622081c07 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 Jul 2025 21:08:55 +0800 Subject: [PATCH 131/227] kselftest/arm64: Add a test program to exercise the syscall ABI Currently we don't have any coverage of the syscall ABI so let's add a very dumb test program which sets up register patterns, does a sysscall and then checks that the register state after the syscall matches what we expect. The program is written in an extremely simplistic fashion with the goal of making it easy to verify that it's doing what it thinks it's doing, it is not a model of how one should write actual code. Currently we validate the general purpose, FPSIMD and SVE registers. There are other thing things that could be covered like FPCR and flags registers, these can be covered incrementally - my main focus at the minute is covering the ABI for the SVE registers. The program repeats the tests for all possible SVE vector lengths in case some vector length specific optimisation causes issues, as well as testing FPSIMD only. It tries two syscalls, getpid() and sched_yield(), in an effort to cover both immediate return to userspace and scheduling another task though there are no guarantees which cases will be hit. A new test directory "abi" is added to hold the test, it doesn't seem to fit well into any of the existing directories. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20211210184133.320748-7-broonie@kernel.org Signed-off-by: Catalin Marinas Signed-off-by: Qi Xi --- tools/testing/selftests/arm64/Makefile | 2 +- tools/testing/selftests/arm64/abi/.gitignore | 1 + tools/testing/selftests/arm64/abi/Makefile | 8 + .../selftests/arm64/abi/syscall-abi-asm.S | 240 +++++++++++++ .../testing/selftests/arm64/abi/syscall-abi.c | 318 ++++++++++++++++++ 5 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/abi/.gitignore create mode 100644 tools/testing/selftests/arm64/abi/Makefile create mode 100644 tools/testing/selftests/arm64/abi/syscall-abi-asm.S create mode 100644 tools/testing/selftests/arm64/abi/syscall-abi.c diff --git a/tools/testing/selftests/arm64/Makefile b/tools/testing/selftests/arm64/Makefile index ced910fb40198..1e8d9a8f59df8 100644 --- a/tools/testing/selftests/arm64/Makefile +++ b/tools/testing/selftests/arm64/Makefile @@ -4,7 +4,7 @@ ARCH ?= $(shell uname -m 2>/dev/null || echo not) ifneq (,$(filter $(ARCH),aarch64 arm64)) -ARM64_SUBTARGETS ?= tags signal pauth fp mte bti +ARM64_SUBTARGETS ?= tags signal pauth fp mte bti abi else ARM64_SUBTARGETS := endif diff --git a/tools/testing/selftests/arm64/abi/.gitignore b/tools/testing/selftests/arm64/abi/.gitignore new file mode 100644 index 0000000000000..b79cf5814c230 --- /dev/null +++ b/tools/testing/selftests/arm64/abi/.gitignore @@ -0,0 +1 @@ +syscall-abi diff --git a/tools/testing/selftests/arm64/abi/Makefile b/tools/testing/selftests/arm64/abi/Makefile new file mode 100644 index 0000000000000..96eba974ac8d5 --- /dev/null +++ b/tools/testing/selftests/arm64/abi/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2021 ARM Limited + +TEST_GEN_PROGS := syscall-abi + +include ../../lib.mk + +$(OUTPUT)/syscall-abi: syscall-abi.c syscall-abi-asm.S diff --git a/tools/testing/selftests/arm64/abi/syscall-abi-asm.S b/tools/testing/selftests/arm64/abi/syscall-abi-asm.S new file mode 100644 index 0000000000000..983467cfcee0e --- /dev/null +++ b/tools/testing/selftests/arm64/abi/syscall-abi-asm.S @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2021 ARM Limited. +// +// Assembly portion of the syscall ABI test + +// +// Load values from memory into registers, invoke a syscall and save the +// register values back to memory for later checking. The syscall to be +// invoked is configured in x8 of the input GPR data. +// +// x0: SVE VL, 0 for FP only +// +// GPRs: gpr_in, gpr_out +// FPRs: fpr_in, fpr_out +// Zn: z_in, z_out +// Pn: p_in, p_out +// FFR: ffr_in, ffr_out + +.arch_extension sve + +.globl do_syscall +do_syscall: + // Store callee saved registers x19-x29 (80 bytes) plus x0 and x1 + stp x29, x30, [sp, #-112]! + mov x29, sp + stp x0, x1, [sp, #16] + stp x19, x20, [sp, #32] + stp x21, x22, [sp, #48] + stp x23, x24, [sp, #64] + stp x25, x26, [sp, #80] + stp x27, x28, [sp, #96] + + // Load GPRs x8-x28, and save our SP/FP for later comparison + ldr x2, =gpr_in + add x2, x2, #64 + ldp x8, x9, [x2], #16 + ldp x10, x11, [x2], #16 + ldp x12, x13, [x2], #16 + ldp x14, x15, [x2], #16 + ldp x16, x17, [x2], #16 + ldp x18, x19, [x2], #16 + ldp x20, x21, [x2], #16 + ldp x22, x23, [x2], #16 + ldp x24, x25, [x2], #16 + ldp x26, x27, [x2], #16 + ldr x28, [x2], #8 + str x29, [x2], #8 // FP + str x30, [x2], #8 // LR + + // Load FPRs if we're not doing SVE + cbnz x0, 1f + ldr x2, =fpr_in + ldp q0, q1, [x2] + ldp q2, q3, [x2, #16 * 2] + ldp q4, q5, [x2, #16 * 4] + ldp q6, q7, [x2, #16 * 6] + ldp q8, q9, [x2, #16 * 8] + ldp q10, q11, [x2, #16 * 10] + ldp q12, q13, [x2, #16 * 12] + ldp q14, q15, [x2, #16 * 14] + ldp q16, q17, [x2, #16 * 16] + ldp q18, q19, [x2, #16 * 18] + ldp q20, q21, [x2, #16 * 20] + ldp q22, q23, [x2, #16 * 22] + ldp q24, q25, [x2, #16 * 24] + ldp q26, q27, [x2, #16 * 26] + ldp q28, q29, [x2, #16 * 28] + ldp q30, q31, [x2, #16 * 30] +1: + + // Load the SVE registers if we're doing SVE + cbz x0, 1f + + ldr x2, =z_in + ldr z0, [x2, #0, MUL VL] + ldr z1, [x2, #1, MUL VL] + ldr z2, [x2, #2, MUL VL] + ldr z3, [x2, #3, MUL VL] + ldr z4, [x2, #4, MUL VL] + ldr z5, [x2, #5, MUL VL] + ldr z6, [x2, #6, MUL VL] + ldr z7, [x2, #7, MUL VL] + ldr z8, [x2, #8, MUL VL] + ldr z9, [x2, #9, MUL VL] + ldr z10, [x2, #10, MUL VL] + ldr z11, [x2, #11, MUL VL] + ldr z12, [x2, #12, MUL VL] + ldr z13, [x2, #13, MUL VL] + ldr z14, [x2, #14, MUL VL] + ldr z15, [x2, #15, MUL VL] + ldr z16, [x2, #16, MUL VL] + ldr z17, [x2, #17, MUL VL] + ldr z18, [x2, #18, MUL VL] + ldr z19, [x2, #19, MUL VL] + ldr z20, [x2, #20, MUL VL] + ldr z21, [x2, #21, MUL VL] + ldr z22, [x2, #22, MUL VL] + ldr z23, [x2, #23, MUL VL] + ldr z24, [x2, #24, MUL VL] + ldr z25, [x2, #25, MUL VL] + ldr z26, [x2, #26, MUL VL] + ldr z27, [x2, #27, MUL VL] + ldr z28, [x2, #28, MUL VL] + ldr z29, [x2, #29, MUL VL] + ldr z30, [x2, #30, MUL VL] + ldr z31, [x2, #31, MUL VL] + + ldr x2, =ffr_in + ldr p0, [x2, #0] + wrffr p0.b + + ldr x2, =p_in + ldr p0, [x2, #0, MUL VL] + ldr p1, [x2, #1, MUL VL] + ldr p2, [x2, #2, MUL VL] + ldr p3, [x2, #3, MUL VL] + ldr p4, [x2, #4, MUL VL] + ldr p5, [x2, #5, MUL VL] + ldr p6, [x2, #6, MUL VL] + ldr p7, [x2, #7, MUL VL] + ldr p8, [x2, #8, MUL VL] + ldr p9, [x2, #9, MUL VL] + ldr p10, [x2, #10, MUL VL] + ldr p11, [x2, #11, MUL VL] + ldr p12, [x2, #12, MUL VL] + ldr p13, [x2, #13, MUL VL] + ldr p14, [x2, #14, MUL VL] + ldr p15, [x2, #15, MUL VL] +1: + + // Do the syscall + svc #0 + + // Save GPRs x8-x30 + ldr x2, =gpr_out + add x2, x2, #64 + stp x8, x9, [x2], #16 + stp x10, x11, [x2], #16 + stp x12, x13, [x2], #16 + stp x14, x15, [x2], #16 + stp x16, x17, [x2], #16 + stp x18, x19, [x2], #16 + stp x20, x21, [x2], #16 + stp x22, x23, [x2], #16 + stp x24, x25, [x2], #16 + stp x26, x27, [x2], #16 + stp x28, x29, [x2], #16 + str x30, [x2] + + // Restore x0 and x1 for feature checks + ldp x0, x1, [sp, #16] + + // Save FPSIMD state + ldr x2, =fpr_out + stp q0, q1, [x2] + stp q2, q3, [x2, #16 * 2] + stp q4, q5, [x2, #16 * 4] + stp q6, q7, [x2, #16 * 6] + stp q8, q9, [x2, #16 * 8] + stp q10, q11, [x2, #16 * 10] + stp q12, q13, [x2, #16 * 12] + stp q14, q15, [x2, #16 * 14] + stp q16, q17, [x2, #16 * 16] + stp q18, q19, [x2, #16 * 18] + stp q20, q21, [x2, #16 * 20] + stp q22, q23, [x2, #16 * 22] + stp q24, q25, [x2, #16 * 24] + stp q26, q27, [x2, #16 * 26] + stp q28, q29, [x2, #16 * 28] + stp q30, q31, [x2, #16 * 30] + + // Save the SVE state if we have some + cbz x0, 1f + + ldr x2, =z_out + str z0, [x2, #0, MUL VL] + str z1, [x2, #1, MUL VL] + str z2, [x2, #2, MUL VL] + str z3, [x2, #3, MUL VL] + str z4, [x2, #4, MUL VL] + str z5, [x2, #5, MUL VL] + str z6, [x2, #6, MUL VL] + str z7, [x2, #7, MUL VL] + str z8, [x2, #8, MUL VL] + str z9, [x2, #9, MUL VL] + str z10, [x2, #10, MUL VL] + str z11, [x2, #11, MUL VL] + str z12, [x2, #12, MUL VL] + str z13, [x2, #13, MUL VL] + str z14, [x2, #14, MUL VL] + str z15, [x2, #15, MUL VL] + str z16, [x2, #16, MUL VL] + str z17, [x2, #17, MUL VL] + str z18, [x2, #18, MUL VL] + str z19, [x2, #19, MUL VL] + str z20, [x2, #20, MUL VL] + str z21, [x2, #21, MUL VL] + str z22, [x2, #22, MUL VL] + str z23, [x2, #23, MUL VL] + str z24, [x2, #24, MUL VL] + str z25, [x2, #25, MUL VL] + str z26, [x2, #26, MUL VL] + str z27, [x2, #27, MUL VL] + str z28, [x2, #28, MUL VL] + str z29, [x2, #29, MUL VL] + str z30, [x2, #30, MUL VL] + str z31, [x2, #31, MUL VL] + + ldr x2, =p_out + str p0, [x2, #0, MUL VL] + str p1, [x2, #1, MUL VL] + str p2, [x2, #2, MUL VL] + str p3, [x2, #3, MUL VL] + str p4, [x2, #4, MUL VL] + str p5, [x2, #5, MUL VL] + str p6, [x2, #6, MUL VL] + str p7, [x2, #7, MUL VL] + str p8, [x2, #8, MUL VL] + str p9, [x2, #9, MUL VL] + str p10, [x2, #10, MUL VL] + str p11, [x2, #11, MUL VL] + str p12, [x2, #12, MUL VL] + str p13, [x2, #13, MUL VL] + str p14, [x2, #14, MUL VL] + str p15, [x2, #15, MUL VL] + + ldr x2, =ffr_out + rdffr p0.b + str p0, [x2, #0] +1: + + // Restore callee saved registers x19-x30 + ldp x19, x20, [sp, #32] + ldp x21, x22, [sp, #48] + ldp x23, x24, [sp, #64] + ldp x25, x26, [sp, #80] + ldp x27, x28, [sp, #96] + ldp x29, x30, [sp], #112 + + ret diff --git a/tools/testing/selftests/arm64/abi/syscall-abi.c b/tools/testing/selftests/arm64/abi/syscall-abi.c new file mode 100644 index 0000000000000..d8eeeafb50dcb --- /dev/null +++ b/tools/testing/selftests/arm64/abi/syscall-abi.c @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2021 ARM Limited. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../kselftest.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) +#define NUM_VL ((SVE_VQ_MAX - SVE_VQ_MIN) + 1) + +extern void do_syscall(int sve_vl); + +static void fill_random(void *buf, size_t size) +{ + int i; + uint32_t *lbuf = buf; + + /* random() returns a 32 bit number regardless of the size of long */ + for (i = 0; i < size / sizeof(uint32_t); i++) + lbuf[i] = random(); +} + +/* + * We also repeat the test for several syscalls to try to expose different + * behaviour. + */ +static struct syscall_cfg { + int syscall_nr; + const char *name; +} syscalls[] = { + { __NR_getpid, "getpid()" }, + { __NR_sched_yield, "sched_yield()" }, +}; + +#define NUM_GPR 31 +uint64_t gpr_in[NUM_GPR]; +uint64_t gpr_out[NUM_GPR]; + +static void setup_gpr(struct syscall_cfg *cfg, int sve_vl) +{ + fill_random(gpr_in, sizeof(gpr_in)); + gpr_in[8] = cfg->syscall_nr; + memset(gpr_out, 0, sizeof(gpr_out)); +} + +static int check_gpr(struct syscall_cfg *cfg, int sve_vl) +{ + int errors = 0; + int i; + + /* + * GPR x0-x7 may be clobbered, and all others should be preserved. + */ + for (i = 9; i < ARRAY_SIZE(gpr_in); i++) { + if (gpr_in[i] != gpr_out[i]) { + ksft_print_msg("%s SVE VL %d mismatch in GPR %d: %llx != %llx\n", + cfg->name, sve_vl, i, + gpr_in[i], gpr_out[i]); + errors++; + } + } + + return errors; +} + +#define NUM_FPR 32 +uint64_t fpr_in[NUM_FPR * 2]; +uint64_t fpr_out[NUM_FPR * 2]; + +static void setup_fpr(struct syscall_cfg *cfg, int sve_vl) +{ + fill_random(fpr_in, sizeof(fpr_in)); + memset(fpr_out, 0, sizeof(fpr_out)); +} + +static int check_fpr(struct syscall_cfg *cfg, int sve_vl) +{ + int errors = 0; + int i; + + if (!sve_vl) { + for (i = 0; i < ARRAY_SIZE(fpr_in); i++) { + if (fpr_in[i] != fpr_out[i]) { + ksft_print_msg("%s Q%d/%d mismatch %llx != %llx\n", + cfg->name, + i / 2, i % 2, + fpr_in[i], fpr_out[i]); + errors++; + } + } + } + + return errors; +} + +static uint8_t z_zero[__SVE_ZREG_SIZE(SVE_VQ_MAX)]; +uint8_t z_in[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)]; +uint8_t z_out[SVE_NUM_PREGS * __SVE_ZREG_SIZE(SVE_VQ_MAX)]; + +static void setup_z(struct syscall_cfg *cfg, int sve_vl) +{ + fill_random(z_in, sizeof(z_in)); + fill_random(z_out, sizeof(z_out)); +} + +static int check_z(struct syscall_cfg *cfg, int sve_vl) +{ + size_t reg_size = sve_vl; + int errors = 0; + int i; + + if (!sve_vl) + return 0; + + /* + * After a syscall the low 128 bits of the Z registers should + * be preserved and the rest be zeroed or preserved. + */ + for (i = 0; i < SVE_NUM_ZREGS; i++) { + void *in = &z_in[reg_size * i]; + void *out = &z_out[reg_size * i]; + + if (memcmp(in, out, SVE_VQ_BYTES) != 0) { + ksft_print_msg("%s SVE VL %d Z%d low 128 bits changed\n", + cfg->name, sve_vl, i); + errors++; + } + } + + return errors; +} + +uint8_t p_in[SVE_NUM_PREGS * __SVE_PREG_SIZE(SVE_VQ_MAX)]; +uint8_t p_out[SVE_NUM_PREGS * __SVE_PREG_SIZE(SVE_VQ_MAX)]; + +static void setup_p(struct syscall_cfg *cfg, int sve_vl) +{ + fill_random(p_in, sizeof(p_in)); + fill_random(p_out, sizeof(p_out)); +} + +static int check_p(struct syscall_cfg *cfg, int sve_vl) +{ + size_t reg_size = sve_vq_from_vl(sve_vl) * 2; /* 1 bit per VL byte */ + + int errors = 0; + int i; + + if (!sve_vl) + return 0; + + /* After a syscall the P registers should be preserved or zeroed */ + for (i = 0; i < SVE_NUM_PREGS * reg_size; i++) + if (p_out[i] && (p_in[i] != p_out[i])) + errors++; + if (errors) + ksft_print_msg("%s SVE VL %d predicate registers non-zero\n", + cfg->name, sve_vl); + + return errors; +} + +uint8_t ffr_in[__SVE_PREG_SIZE(SVE_VQ_MAX)]; +uint8_t ffr_out[__SVE_PREG_SIZE(SVE_VQ_MAX)]; + +static void setup_ffr(struct syscall_cfg *cfg, int sve_vl) +{ + /* + * It is only valid to set a contiguous set of bits starting + * at 0. For now since we're expecting this to be cleared by + * a syscall just set all bits. + */ + memset(ffr_in, 0xff, sizeof(ffr_in)); + fill_random(ffr_out, sizeof(ffr_out)); +} + +static int check_ffr(struct syscall_cfg *cfg, int sve_vl) +{ + size_t reg_size = sve_vq_from_vl(sve_vl) * 2; /* 1 bit per VL byte */ + int errors = 0; + int i; + + if (!sve_vl) + return 0; + + /* After a syscall the P registers should be preserved or zeroed */ + for (i = 0; i < reg_size; i++) + if (ffr_out[i] && (ffr_in[i] != ffr_out[i])) + errors++; + if (errors) + ksft_print_msg("%s SVE VL %d FFR non-zero\n", + cfg->name, sve_vl); + + return errors; +} + +typedef void (*setup_fn)(struct syscall_cfg *cfg, int sve_vl); +typedef int (*check_fn)(struct syscall_cfg *cfg, int sve_vl); + +/* + * Each set of registers has a setup function which is called before + * the syscall to fill values in a global variable for loading by the + * test code and a check function which validates that the results are + * as expected. Vector lengths are passed everywhere, a vector length + * of 0 should be treated as do not test. + */ +static struct { + setup_fn setup; + check_fn check; +} regset[] = { + { setup_gpr, check_gpr }, + { setup_fpr, check_fpr }, + { setup_z, check_z }, + { setup_p, check_p }, + { setup_ffr, check_ffr }, +}; + +static bool do_test(struct syscall_cfg *cfg, int sve_vl) +{ + int errors = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(regset); i++) + regset[i].setup(cfg, sve_vl); + + do_syscall(sve_vl); + + for (i = 0; i < ARRAY_SIZE(regset); i++) + errors += regset[i].check(cfg, sve_vl); + + return errors == 0; +} + +static void test_one_syscall(struct syscall_cfg *cfg) +{ + int sve_vq, sve_vl; + + /* FPSIMD only case */ + ksft_test_result(do_test(cfg, 0), + "%s FPSIMD\n", cfg->name); + + if (!(getauxval(AT_HWCAP) & HWCAP_SVE)) + return; + + for (sve_vq = SVE_VQ_MAX; sve_vq > 0; --sve_vq) { + sve_vl = prctl(PR_SVE_SET_VL, sve_vq * 16); + if (sve_vl == -1) + ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n", + strerror(errno), errno); + + sve_vl &= PR_SVE_VL_LEN_MASK; + + if (sve_vq != sve_vq_from_vl(sve_vl)) + sve_vq = sve_vq_from_vl(sve_vl); + + ksft_test_result(do_test(cfg, sve_vl), + "%s SVE VL %d\n", cfg->name, sve_vl); + } +} + +int sve_count_vls(void) +{ + unsigned int vq; + int vl_count = 0; + int vl; + + if (!(getauxval(AT_HWCAP) & HWCAP_SVE)) + return 0; + + /* + * Enumerate up to SVE_VQ_MAX vector lengths + */ + for (vq = SVE_VQ_MAX; vq > 0; --vq) { + vl = prctl(PR_SVE_SET_VL, vq * 16); + if (vl == -1) + ksft_exit_fail_msg("PR_SVE_SET_VL failed: %s (%d)\n", + strerror(errno), errno); + + vl &= PR_SVE_VL_LEN_MASK; + + if (vq != sve_vq_from_vl(vl)) + vq = sve_vq_from_vl(vl); + + vl_count++; + } + + return vl_count; +} + +int main(void) +{ + int i; + + srandom(getpid()); + + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(syscalls) * (sve_count_vls() + 1)); + + for (i = 0; i < ARRAY_SIZE(syscalls); i++) + test_one_syscall(&syscalls[i]); + + ksft_print_cnts(); + + return 0; +} From c268cf08c8012a266ee93260727de57d8c1bc535 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 Jul 2025 21:08:56 +0800 Subject: [PATCH 132/227] kselftest/arm64: Add simple hwcap validation Add some trivial hwcap validation which checks that /proc/cpuinfo and AT_HWCAP agree with each other and can verify that for extensions that can generate a SIGILL due to adding new instructions one appears or doesn't appear as expected. I've added SVE and SME, other capabilities can be added later if this gets merged. This isn't super exciting but on the other hand took very little time to write and should be handy when verifying that you wired up AT_HWCAP properly. Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220829154602.827275-1-broonie@kernel.org Signed-off-by: Catalin Marinas Signed-off-by: Qi Xi --- tools/testing/selftests/arm64/abi/Makefile | 2 +- tools/testing/selftests/arm64/abi/hwcap.c | 188 +++++++++++++++++++++ 2 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/arm64/abi/hwcap.c diff --git a/tools/testing/selftests/arm64/abi/Makefile b/tools/testing/selftests/arm64/abi/Makefile index 96eba974ac8d5..e84ccbed660b9 100644 --- a/tools/testing/selftests/arm64/abi/Makefile +++ b/tools/testing/selftests/arm64/abi/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright (C) 2021 ARM Limited -TEST_GEN_PROGS := syscall-abi +TEST_GEN_PROGS := hwcap syscall-abi include ../../lib.mk diff --git a/tools/testing/selftests/arm64/abi/hwcap.c b/tools/testing/selftests/arm64/abi/hwcap.c new file mode 100644 index 0000000000000..8b5c646cea634 --- /dev/null +++ b/tools/testing/selftests/arm64/abi/hwcap.c @@ -0,0 +1,188 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 ARM Limited. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../kselftest.h" + +#define TESTS_PER_HWCAP 2 + +/* + * Function expected to generate SIGILL when the feature is not + * supported and return when it is supported. If SIGILL is generated + * then the handler must be able to skip over the instruction safely. + * + * Note that it is expected that for many architecture extensions + * there are no specific traps due to no architecture state being + * added so we may not fault if running on a kernel which doesn't know + * to add the hwcap. + */ +typedef void (*sigill_fn)(void); + +static void sme_sigill(void) +{ + /* RDSVL x0, #0 */ + asm volatile(".inst 0x04bf5800" : : : "x0"); +} + +static void sve_sigill(void) +{ + /* RDVL x0, #0 */ + asm volatile(".inst 0x04bf5000" : : : "x0"); +} + +static const struct hwcap_data { + const char *name; + unsigned long at_hwcap; + unsigned long hwcap_bit; + const char *cpuinfo; + sigill_fn sigill_fn; + bool sigill_reliable; +} hwcaps[] = { + { + .name = "SME", + .at_hwcap = AT_HWCAP2, + .hwcap_bit = HWCAP2_SME, + .cpuinfo = "sme", + .sigill_fn = sme_sigill, + .sigill_reliable = true, + }, + { + .name = "SVE", + .at_hwcap = AT_HWCAP, + .hwcap_bit = HWCAP_SVE, + .cpuinfo = "sve", + .sigill_fn = sve_sigill, + .sigill_reliable = true, + }, +}; + +static bool seen_sigill; + +static void handle_sigill(int sig, siginfo_t *info, void *context) +{ + ucontext_t *uc = context; + + seen_sigill = true; + + /* Skip over the offending instruction */ + uc->uc_mcontext.pc += 4; +} + +bool cpuinfo_present(const char *name) +{ + FILE *f; + char buf[2048], name_space[30], name_newline[30]; + char *s; + + /* + * The feature should appear with a leading space and either a + * trailing space or a newline. + */ + snprintf(name_space, sizeof(name_space), " %s ", name); + snprintf(name_newline, sizeof(name_newline), " %s\n", name); + + f = fopen("/proc/cpuinfo", "r"); + if (!f) { + ksft_print_msg("Failed to open /proc/cpuinfo\n"); + return false; + } + + while (fgets(buf, sizeof(buf), f)) { + /* Features: line? */ + if (strncmp(buf, "Features\t:", strlen("Features\t:")) != 0) + continue; + + /* All CPUs should be symmetric, don't read any more */ + fclose(f); + + s = strstr(buf, name_space); + if (s) + return true; + s = strstr(buf, name_newline); + if (s) + return true; + + return false; + } + + ksft_print_msg("Failed to find Features in /proc/cpuinfo\n"); + fclose(f); + return false; +} + +int main(void) +{ + const struct hwcap_data *hwcap; + int i, ret; + bool have_cpuinfo, have_hwcap; + struct sigaction sa; + + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(hwcaps) * TESTS_PER_HWCAP); + + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = handle_sigill; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + sigemptyset(&sa.sa_mask); + ret = sigaction(SIGILL, &sa, NULL); + if (ret < 0) + ksft_exit_fail_msg("Failed to install SIGILL handler: %s (%d)\n", + strerror(errno), errno); + + for (i = 0; i < ARRAY_SIZE(hwcaps); i++) { + hwcap = &hwcaps[i]; + + have_hwcap = getauxval(hwcaps->at_hwcap) & hwcap->hwcap_bit; + have_cpuinfo = cpuinfo_present(hwcap->cpuinfo); + + if (have_hwcap) + ksft_print_msg("%s present", hwcap->name); + + ksft_test_result(have_hwcap == have_cpuinfo, + "cpuinfo_match_%s\n", hwcap->name); + + if (hwcap->sigill_fn) { + seen_sigill = false; + hwcap->sigill_fn(); + + if (have_hwcap) { + /* Should be able to use the extension */ + ksft_test_result(!seen_sigill, "sigill_%s\n", + hwcap->name); + } else if (hwcap->sigill_reliable) { + /* Guaranteed a SIGILL */ + ksft_test_result(seen_sigill, "sigill_%s\n", + hwcap->name); + } else { + /* Missing SIGILL might be fine */ + ksft_print_msg("SIGILL %sreported for %s\n", + seen_sigill ? "" : "not ", + hwcap->name); + ksft_test_result_skip("sigill_%s\n", + hwcap->name); + } + } else { + ksft_test_result_skip("sigill_%s\n", + hwcap->name); + } + } + + ksft_print_cnts(); + + return 0; +} From bee2f898e8f3edddfcd6f2ccfac0770bb1d0c5c8 Mon Sep 17 00:00:00 2001 From: Joey Gouly Date: Thu, 3 Jul 2025 21:08:57 +0800 Subject: [PATCH 133/227] selftests/arm64: add HWCAP2_HBC test Add a test for the newly added HWCAP2_HBC. Signed-off-by: Joey Gouly Cc: Catalin Marinas Cc: Will Deacon Link: https://lore.kernel.org/r/20230804143746.3900803-3-joey.gouly@arm.com Signed-off-by: Will Deacon Signed-off-by: Qi Xi --- tools/testing/selftests/arm64/abi/hwcap.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/testing/selftests/arm64/abi/hwcap.c b/tools/testing/selftests/arm64/abi/hwcap.c index 8b5c646cea634..65a7b01d7cdcf 100644 --- a/tools/testing/selftests/arm64/abi/hwcap.c +++ b/tools/testing/selftests/arm64/abi/hwcap.c @@ -45,6 +45,13 @@ static void sve_sigill(void) asm volatile(".inst 0x04bf5000" : : : "x0"); } +static void hbc_sigill(void) +{ + /* BC.EQ +4 */ + asm volatile("cmp xzr, xzr\n" + ".inst 0x54000030" : : : "cc"); +} + static const struct hwcap_data { const char *name; unsigned long at_hwcap; @@ -69,6 +76,14 @@ static const struct hwcap_data { .sigill_fn = sve_sigill, .sigill_reliable = true, }, + { + .name = "HBC", + .at_hwcap = AT_HWCAP2, + .hwcap_bit = HWCAP2_HBC, + .cpuinfo = "hbc", + .sigill_fn = hbc_sigill, + .sigill_reliable = true, + }, }; static bool seen_sigill; From b0bec81aa459179a2ba8bead97631501ced7ec47 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Oct 2021 16:47:54 +0300 Subject: [PATCH 134/227] driver core: Provide device_match_acpi_handle() helper We have a couple of users of this helper, make it available for them. The prototype for the helper is specifically crafted in order to be easily used with bus_find_device() call. That's why its location is in the driver core rather than ACPI. Reviewed-by: Rafael J. Wysocki Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211014134756.39092-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 ++++++ include/linux/device/bus.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index c46a6947bb29e..4dc1457a15969 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4842,6 +4842,12 @@ int device_match_acpi_dev(struct device *dev, const void *adev) } EXPORT_SYMBOL(device_match_acpi_dev); +int device_match_acpi_handle(struct device *dev, const void *handle) +{ + return ACPI_HANDLE(dev) == handle; +} +EXPORT_SYMBOL(device_match_acpi_handle); + int device_match_any(struct device *dev, const void *unused) { return 1; diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h index 6a4b257c270ef..a247fb545a2f4 100644 --- a/include/linux/device/bus.h +++ b/include/linux/device/bus.h @@ -143,6 +143,7 @@ int device_match_of_node(struct device *dev, const void *np); int device_match_fwnode(struct device *dev, const void *fwnode); int device_match_devt(struct device *dev, const void *pdevt); int device_match_acpi_dev(struct device *dev, const void *adev); +int device_match_acpi_handle(struct device *dev, const void *handle); int device_match_any(struct device *dev, const void *unused); /* iterator helpers for buses */ From 755a4a7fbf6fdb9811ae8ed63eb81a5467929d59 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Mon, 13 May 2024 15:59:01 +0800 Subject: [PATCH 135/227] gpiolib: acpi: Fix failed in acpi_gpiochip_find() by adding parent node match mainline inclusion from mainline-v6.11-rc1 commit adbc49a5a8c6fcf7be154c2e30213bbf472940da category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAQ1MS CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git/commit/drivers/gpio/gpiolib-acpi.c?h=spi-v6.11&id=adbc49a5a8c6fcf7be154c2e30213bbf472940da -------------------------------- Previous patch modified the standard used by acpi_gpiochip_find() to match device nodes. Using the device node set in gc->gpiodev->d- ev instead of gc->parent. However, there is a situation in gpio-dwapb where the GPIO device driver will set gc->fwnode for each port corresponding to a child node under a GPIO device, so gc->gpiodev->dev will be assigned the value of each child node in gpiochip_add_data(). gpio-dwapb.c: 128,31 static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, struct dwapb_port_property *pp, unsigned int offs); port->gc.fwnode = pp->fwnode; 693,39 static int dwapb_gpio_probe; err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i); When other drivers request GPIO pin resources through the GPIO device node provided by ACPI (corresponding to the parent node), the change of the matching object to gc->gpiodev->dev in acpi_gpiochip_find() only allows finding the value of each port (child node), resulting in a failed request. Reapply the condition of using gc->parent for match in acpi_gpio- chip_find() in the code can compatible with the problem of gpio-dwapb, and will not affect the two cases mentioned in the patch: 1. There is no setting for gc->fwnode. 2. The case that depends on using gc->fwnode for match. Fixes: 5062e4c14b75 ("gpiolib: acpi: use the fwnode in acpi_gpiochip_find()") Fixes: 067dbc1ea5ce ("gpiolib: acpi: Don't use GPIO chip fwnode in acpi_gpiochip_find()") Signed-off-by:lujunhua Signed-off-by: Devyn Liu Reviewed-by: Mika Westerberg Tested-by: Benjamin Tissoires Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 27e3fb9938049..c477684ed54ab 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -95,10 +95,23 @@ static bool acpi_gpio_deferred_req_irqs_done; static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) { - if (!gc->parent) - return false; + /* First check the actual GPIO device */ + if (device_match_acpi_handle(&gc->gpiodev->dev, data)) + return true; - return ACPI_HANDLE(gc->parent) == data; + /* + * When the ACPI device is artificially split to the banks of GPIOs, + * where each of them is represented by a separate GPIO device, + * the firmware node of the physical device may not be shared among + * the banks as they may require different values for the same property, + * e.g., number of GPIOs in a certain bank. In such case the ACPI handle + * of a GPIO device is NULL and can not be used. Hence we have to check + * the parent device to be sure that there is no match before bailing + * out. + */ + if (gc->parent) + return device_match_acpi_handle(gc->parent, data); + return false; } /** From d138e6cb5bfc778d3b5b204ef78f590049e1eba7 Mon Sep 17 00:00:00 2001 From: hbmm Date: Thu, 3 Jul 2025 17:04:30 +0800 Subject: [PATCH 136/227] Revert "ACPI: thermal: Move to dedicated function sysfs extra attr creation" This reverts commit c7ffeb5fa1e015ead36cda8c1e998b9be4df3fa1. --- drivers/acpi/thermal.c | 45 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 7e24554fe00f3..809e12b941235 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -783,32 +783,6 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = { .critical = acpi_thermal_zone_device_critical, }; -static int acpi_thermal_zone_sysfs_add(struct acpi_thermal *tz) -{ - struct device *tzdev = thermal_zone_device(tz->thermal_zone); - int ret; - - ret = sysfs_create_link(&tz->device->dev.kobj, - &tzdev->kobj, "thermal_zone"); - if (ret) - return ret; - - ret = sysfs_create_link(&tzdev->kobj, - &tz->device->dev.kobj, "device"); - if (ret) - sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); - - return ret; -} - -static void acpi_thermal_zone_sysfs_remove(struct acpi_thermal *tz) -{ - struct device *tzdev = thermal_zone_device(tz->thermal_zone); - - sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); - sysfs_remove_link(&tzdev->kobj, "device"); -} - static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) { int trips = 0; @@ -842,15 +816,21 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (IS_ERR(tz->thermal_zone)) return -ENODEV; - result = acpi_thermal_zone_sysfs_add(tz); + result = sysfs_create_link(&tz->device->dev.kobj, + &tz->thermal_zone->device.kobj, "thermal_zone"); if (result) goto unregister_tzd; + result = sysfs_create_link(&tz->thermal_zone->device.kobj, + &tz->device->dev.kobj, "device"); + if (result) + goto remove_tz_link; + status = acpi_bus_attach_private_data(tz->device->handle, tz->thermal_zone); if (ACPI_FAILURE(status)) { result = -ENODEV; - goto remove_links; + goto remove_dev_link; } result = thermal_zone_device_enable(tz->thermal_zone); @@ -864,15 +844,10 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) acpi_bus_detach: acpi_bus_detach_private_data(tz->device->handle); -<<<<<<< HEAD remove_dev_link: sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); remove_tz_link: sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); -======= -remove_links: - acpi_thermal_zone_sysfs_remove(tz); ->>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) unregister_tzd: thermal_zone_device_unregister(tz->thermal_zone); @@ -881,12 +856,8 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) { -<<<<<<< HEAD sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); -======= - acpi_thermal_zone_sysfs_remove(tz); ->>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) thermal_zone_device_unregister(tz->thermal_zone); tz->thermal_zone = NULL; acpi_bus_detach_private_data(tz->device->handle); From ba5f9554bb1df73efd485115b8f84c425c377ad4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Dec 2021 17:37:10 +0100 Subject: [PATCH 137/227] ACPI: Use acpi_fetch_acpi_dev() instead of acpi_bus_get_device() Modify the ACPI code to use acpi_fetch_acpi_dev() instead of acpi_bus_get_device() where applicable. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede --- drivers/acpi/acpi_video.c | 5 ++--- drivers/acpi/device_pm.c | 31 +++++++++++++------------------ drivers/acpi/dock.c | 5 ++--- drivers/acpi/pci_link.c | 12 ++++-------- drivers/acpi/pci_root.c | 10 ++++------ drivers/acpi/power.c | 7 +++---- drivers/acpi/processor_driver.c | 10 +++++++--- drivers/acpi/processor_idle.c | 2 +- drivers/acpi/property.c | 11 +++++------ drivers/acpi/resource.c | 4 ++-- drivers/acpi/thermal.c | 9 ++++----- drivers/acpi/video_detect.c | 6 ++---- drivers/acpi/x86/s2idle.c | 4 ++-- include/acpi/acpi_bus.h | 1 + 14 files changed, 52 insertions(+), 65 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index c8eb69d3e1d61..e87f4bf82d8b0 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1765,13 +1765,12 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context, { struct acpi_device *device = context; struct acpi_device *sibling; - int result; if (handle == device->handle) return AE_CTRL_TERMINATE; - result = acpi_bus_get_device(handle, &sibling); - if (result) + sibling = acpi_fetch_acpi_dev(handle); + if (!sibling) return AE_OK; if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 0028b6b51c877..60a8d7a5ff03f 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -285,14 +285,12 @@ EXPORT_SYMBOL(acpi_device_set_power); int acpi_bus_set_power(acpi_handle handle, int state) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - if (result) - return result; + if (device) + return acpi_device_set_power(device, state); - return acpi_device_set_power(device, state); + return -ENODEV; } EXPORT_SYMBOL(acpi_bus_set_power); @@ -410,21 +408,20 @@ EXPORT_SYMBOL_GPL(acpi_device_update_power); int acpi_bus_update_power(acpi_handle handle, int *state_p) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? result : acpi_device_update_power(device, state_p); + if (device) + return acpi_device_update_power(device, state_p); + + return -ENODEV; } EXPORT_SYMBOL_GPL(acpi_bus_update_power); bool acpi_bus_power_manageable(acpi_handle handle) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? false : device->flags.power_manageable; + return device && device->flags.power_manageable; } EXPORT_SYMBOL(acpi_bus_power_manageable); @@ -543,11 +540,9 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) bool acpi_bus_can_wakeup(acpi_handle handle) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? false : device->wakeup.flags.valid; + return device && device->wakeup.flags.valid; } EXPORT_SYMBOL(acpi_bus_can_wakeup); diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 7cf92158008fa..ece9109db0586 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -489,10 +489,9 @@ static ssize_t docked_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dock_station *dock_station = dev->platform_data; - struct acpi_device *adev = NULL; + struct acpi_device *adev = acpi_fetch_acpi_dev(dock_station->handle); - acpi_bus_get_device(dock_station->handle, &adev); - return snprintf(buf, PAGE_SIZE, "%u\n", acpi_device_enumerated(adev)); + return sysfs_emit(buf, "%u\n", acpi_device_enumerated(adev)); } static DEVICE_ATTR_RO(docked); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index cb7b900d9466c..d54fb8e54671d 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -606,12 +606,10 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link) int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name) { - int result; - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_link *link; - result = acpi_bus_get_device(handle, &device); - if (result) { + if (!device) { acpi_handle_err(handle, "Invalid link device\n"); return -1; } @@ -658,12 +656,10 @@ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, */ int acpi_pci_link_free_irq(acpi_handle handle) { - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_link *link; - acpi_status result; - result = acpi_bus_get_device(handle, &device); - if (result) { + if (!device) { acpi_handle_err(handle, "Invalid link device\n"); return -1; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 223aa010dd8da..d85b619108dae 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -65,11 +65,10 @@ static struct acpi_scan_handler pci_root_handler = { */ int acpi_is_root_bridge(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); int ret; - struct acpi_device *device; - ret = acpi_bus_get_device(handle, &device); - if (ret) + if (!device) return 0; ret = acpi_match_device_ids(device, root_device_ids); @@ -226,11 +225,10 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags) struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_root *root; - struct acpi_device *device; - if (acpi_bus_get_device(handle, &device) || - acpi_match_device_ids(device, root_device_ids)) + if (!device || acpi_match_device_ids(device, root_device_ids)) return NULL; root = acpi_driver_data(device); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index c95eedd58f5bf..fe22366b4b385 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -81,9 +81,9 @@ struct acpi_power_resource *to_power_resource(struct acpi_device *device) static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - if (acpi_bus_get_device(handle, &device)) + if (!device) return NULL; return to_power_resource(device); @@ -914,14 +914,13 @@ static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource struct acpi_device *acpi_add_power_resource(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_power_resource *resource; - struct acpi_device *device = NULL; union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; acpi_status status; int result; - acpi_bus_get_device(handle, &device); if (device) return device; diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 77541f939be3e..368a9edefd0cb 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -98,8 +98,13 @@ static int acpi_soft_cpu_online(unsigned int cpu) struct acpi_processor *pr = per_cpu(processors, cpu); struct acpi_device *device; - if (!pr || acpi_bus_get_device(pr->handle, &device)) + if (!pr) + return 0; + + device = acpi_fetch_acpi_dev(pr->handle); + if (!device) return 0; + /* * CPU got physically hotplugged and onlined for the first time: * Initialize missing things. @@ -125,9 +130,8 @@ static int acpi_soft_cpu_online(unsigned int cpu) static int acpi_soft_cpu_dead(unsigned int cpu) { struct acpi_processor *pr = per_cpu(processors, cpu); - struct acpi_device *device; - if (!pr || acpi_bus_get_device(pr->handle, &device)) + if (!pr || !acpi_fetch_acpi_dev(pr->handle)) return 0; acpi_processor_reevaluate_tstate(pr, true); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 5fbb435b58122..756ffc9d10b7c 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1132,7 +1132,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) status = acpi_get_parent(handle, &pr_ahandle); while (ACPI_SUCCESS(status)) { - acpi_bus_get_device(pr_ahandle, &d); + d = acpi_fetch_acpi_dev(pr_ahandle); handle = pr_ahandle; if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 9ab7f7184343a..617e52dc415d7 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -694,9 +694,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (index) return -ENOENT; - ret = acpi_bus_get_device(obj->reference.handle, &device); - if (ret) - return ret == -ENODEV ? -EINVAL : ret; + device = acpi_fetch_acpi_dev(obj->reference.handle); + if (!device) + return -EINVAL; if (!args) return 0; @@ -729,9 +729,8 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (element->type == ACPI_TYPE_LOCAL_REFERENCE) { struct fwnode_handle *ref_fwnode; - ret = acpi_bus_get_device(element->reference.handle, - &device); - if (ret) + device = acpi_fetch_acpi_dev(element->reference.handle); + if (!device) return -EINVAL; nargs = 0; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 57f34aa4f4295..27ea1317e33a6 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -982,9 +982,9 @@ static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth, { struct resource *res = context; struct acpi_device **consumer = (struct acpi_device **) ret; - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); - if (acpi_bus_get_device(handle, &adev)) + if (!adev) return AE_OK; if (acpi_dev_consumes_res(adev, res)) { diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 809e12b941235..5482e28c5bc68 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -693,7 +693,6 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; struct acpi_device *dev; - acpi_status status; acpi_handle handle; int i; int j; @@ -711,8 +710,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, for (i = 0; i < tz->trips.passive.devices.count; i++) { handle = tz->trips.passive.devices.handles[i]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_FAILURE(status) || dev != device) + dev = acpi_fetch_acpi_dev(handle); + if (dev != device) continue; if (bind) result = @@ -737,8 +736,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, j < tz->trips.active[i].devices.count; j++) { handle = tz->trips.active[i].devices.handles[j]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_FAILURE(status) || dev != device) + dev = acpi_fetch_acpi_dev(handle); + if (dev != device) continue; if (bind) result = thermal_zone_bind_cooling_device diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index a5cb9e1d48bcc..d55c143a49d7c 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -59,18 +59,16 @@ static void acpi_video_parse_cmdline(void) static acpi_status find_video(acpi_handle handle, u32 lvl, void *context, void **rv) { + struct acpi_device *acpi_dev = acpi_fetch_acpi_dev(handle); long *cap = context; struct pci_dev *dev; - struct acpi_device *acpi_dev; static const struct acpi_device_id video_ids[] = { {ACPI_VIDEO_HID, 0}, {"", 0}, }; - if (acpi_bus_get_device(handle, &acpi_dev)) - return AE_OK; - if (!acpi_match_device_ids(acpi_dev, video_ids)) { + if (acpi_dev && !acpi_match_device_ids(acpi_dev, video_ids)) { dev = acpi_get_pci_dev(handle); if (!dev) return AE_OK; diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index ada989670d9b8..ee3deea964fd4 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -294,9 +294,9 @@ static void lpi_check_constraints(void) for (i = 0; i < lpi_constraints_table_size; ++i) { acpi_handle handle = lpi_constraints_table[i].handle; - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); - if (!handle || acpi_bus_get_device(handle, &adev)) + if (!adev) continue; acpi_handle_debug(handle, diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a87a6d557e467..8580e33382427 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -727,6 +727,7 @@ static inline void acpi_dev_put(struct acpi_device *adev) } struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle); +struct acpi_device *acpi_fetch_acpi_dev(acpi_handle handle); static inline void acpi_bus_put_acpi_device(struct acpi_device *adev) { From bb19af8c335dd39e12a192512e422030460afed9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 4 Oct 2022 18:31:11 +0200 Subject: [PATCH 138/227] ACPI: thermal: Use white space more consistently The usage of white space in the ACPI thermal driver is not very consistent, so improve that a bit. While at it, add missing braces to if()/else in a few places. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano --- drivers/acpi/thermal.c | 190 ++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 98 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5482e28c5bc68..852a413d230d5 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -154,7 +154,7 @@ struct acpi_thermal_flags { }; struct acpi_thermal { - struct acpi_device * device; + struct acpi_device *device; acpi_bus_id name; unsigned long temperature; unsigned long last_temperature; @@ -266,8 +266,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* Critical Shutdown */ if (flag & ACPI_TRIPS_CRITICAL) { - status = acpi_evaluate_integer(tz->device->handle, - "_CRT", NULL, &tmp); + status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, &tmp); tz->trips.critical.temperature = tmp; /* * Treat freezing temperatures as invalid as well; some @@ -280,8 +279,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) acpi_handle_debug(tz->device->handle, "No critical threshold\n"); } else if (tmp <= 2732) { - pr_info(FW_BUG "Invalid critical threshold (%llu)\n", - tmp); + pr_info(FW_BUG "Invalid critical threshold (%llu)\n", tmp); tz->trips.critical.flags.valid = 0; } else { tz->trips.critical.flags.valid = 1; @@ -308,8 +306,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* Critical Sleep (optional) */ if (flag & ACPI_TRIPS_HOT) { - status = acpi_evaluate_integer(tz->device->handle, - "_HOT", NULL, &tmp); + status = acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, &tmp); if (ACPI_FAILURE(status)) { tz->trips.hot.flags.valid = 0; acpi_handle_debug(tz->device->handle, @@ -325,7 +322,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* Passive (optional) */ if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) || - (flag == ACPI_TRIPS_INIT)) { + (flag == ACPI_TRIPS_INIT)) { valid = tz->trips.passive.flags.valid; if (psv == -1) { status = AE_SUPPORT; @@ -334,32 +331,31 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) status = AE_OK; } else { status = acpi_evaluate_integer(tz->device->handle, - "_PSV", NULL, &tmp); + "_PSV", NULL, &tmp); } - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { tz->trips.passive.flags.valid = 0; - else { + } else { tz->trips.passive.temperature = tmp; tz->trips.passive.flags.valid = 1; if (flag == ACPI_TRIPS_INIT) { - status = acpi_evaluate_integer( - tz->device->handle, "_TC1", - NULL, &tmp); + status = acpi_evaluate_integer(tz->device->handle, + "_TC1", NULL, &tmp); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; else tz->trips.passive.tc1 = tmp; - status = acpi_evaluate_integer( - tz->device->handle, "_TC2", - NULL, &tmp); + + status = acpi_evaluate_integer(tz->device->handle, + "_TC2", NULL, &tmp); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; else tz->trips.passive.tc2 = tmp; - status = acpi_evaluate_integer( - tz->device->handle, "_TSP", - NULL, &tmp); + + status = acpi_evaluate_integer(tz->device->handle, + "_TSP", NULL, &tmp); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; else @@ -370,25 +366,25 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) { memset(&devices, 0, sizeof(struct acpi_handle_list)); status = acpi_evaluate_reference(tz->device->handle, "_PSL", - NULL, &devices); + NULL, &devices); if (ACPI_FAILURE(status)) { acpi_handle_info(tz->device->handle, "Invalid passive threshold\n"); tz->trips.passive.flags.valid = 0; - } - else + } else { tz->trips.passive.flags.valid = 1; + } if (memcmp(&tz->trips.passive.devices, &devices, - sizeof(struct acpi_handle_list))) { + sizeof(struct acpi_handle_list))) { memcpy(&tz->trips.passive.devices, &devices, - sizeof(struct acpi_handle_list)); + sizeof(struct acpi_handle_list)); ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device"); } } if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) { if (valid != tz->trips.passive.flags.valid) - ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "state"); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "state"); } /* Active (optional) */ @@ -400,28 +396,30 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) break; /* disable all active trip points */ if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) && - tz->trips.active[i].flags.valid)) { + tz->trips.active[i].flags.valid)) { status = acpi_evaluate_integer(tz->device->handle, - name, NULL, &tmp); + name, NULL, &tmp); if (ACPI_FAILURE(status)) { tz->trips.active[i].flags.valid = 0; if (i == 0) break; + if (act <= 0) break; + if (i == 1) - tz->trips.active[0].temperature = - celsius_to_deci_kelvin(act); + tz->trips.active[0].temperature = celsius_to_deci_kelvin(act); else /* * Don't allow override higher than * the next higher trip point */ - tz->trips.active[i - 1].temperature = - (tz->trips.active[i - 2].temperature < + tz->trips.active[i-1].temperature = + (tz->trips.active[i-2].temperature < celsius_to_deci_kelvin(act) ? - tz->trips.active[i - 2].temperature : + tz->trips.active[i-2].temperature : celsius_to_deci_kelvin(act)); + break; } else { tz->trips.active[i].temperature = tmp; @@ -430,22 +428,22 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) } name[2] = 'L'; - if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) { + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid) { memset(&devices, 0, sizeof(struct acpi_handle_list)); status = acpi_evaluate_reference(tz->device->handle, - name, NULL, &devices); + name, NULL, &devices); if (ACPI_FAILURE(status)) { acpi_handle_info(tz->device->handle, "Invalid active%d threshold\n", i); tz->trips.active[i].flags.valid = 0; - } - else + } else { tz->trips.active[i].flags.valid = 1; + } if (memcmp(&tz->trips.active[i].devices, &devices, - sizeof(struct acpi_handle_list))) { + sizeof(struct acpi_handle_list))) { memcpy(&tz->trips.active[i].devices, &devices, - sizeof(struct acpi_handle_list)); + sizeof(struct acpi_handle_list)); ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device"); } } @@ -460,9 +458,9 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) if (flag & ACPI_TRIPS_DEVICES) { memset(&devices, 0, sizeof(devices)); status = acpi_evaluate_reference(tz->device->handle, "_TZD", - NULL, &devices); - if (ACPI_SUCCESS(status) - && memcmp(&tz->devices, &devices, sizeof(devices))) { + NULL, &devices); + if (ACPI_SUCCESS(status) && + memcmp(&tz->devices, &devices, sizeof(devices))) { tz->devices = devices; ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device"); } @@ -544,8 +542,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal, trip--; } - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && - tz->trips.active[i].flags.valid; i++) { + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid; i++) { if (!trip) { *type = THERMAL_TRIP_ACTIVE; return 0; @@ -568,8 +565,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.critical.flags.valid) { if (!trip) { *temp = deci_kelvin_to_millicelsius_with_offset( - tz->trips.critical.temperature, - tz->kelvin_offset); + tz->trips.critical.temperature, + tz->kelvin_offset); return 0; } trip--; @@ -578,8 +575,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.hot.flags.valid) { if (!trip) { *temp = deci_kelvin_to_millicelsius_with_offset( - tz->trips.hot.temperature, - tz->kelvin_offset); + tz->trips.hot.temperature, + tz->kelvin_offset); return 0; } trip--; @@ -588,8 +585,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.passive.flags.valid) { if (!trip) { *temp = deci_kelvin_to_millicelsius_with_offset( - tz->trips.passive.temperature, - tz->kelvin_offset); + tz->trips.passive.temperature, + tz->kelvin_offset); return 0; } trip--; @@ -599,8 +596,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, tz->trips.active[i].flags.valid; i++) { if (!trip) { *temp = deci_kelvin_to_millicelsius_with_offset( - tz->trips.active[i].temperature, - tz->kelvin_offset); + tz->trips.active[i].temperature, + tz->kelvin_offset); return 0; } trip--; @@ -616,15 +613,15 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, if (tz->trips.critical.flags.valid) { *temperature = deci_kelvin_to_millicelsius_with_offset( - tz->trips.critical.temperature, - tz->kelvin_offset); + tz->trips.critical.temperature, + tz->kelvin_offset); return 0; } else return -EINVAL; } static int thermal_get_trend(struct thermal_zone_device *thermal, - int trip, enum thermal_trend *trend) + int trip, enum thermal_trend *trend) { struct acpi_thermal *tz = thermal->devdata; enum thermal_trip_type type; @@ -653,9 +650,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, * tz->temperature has already been updated by generic thermal layer, * before this callback being invoked */ - i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) - + (tz->trips.passive.tc2 - * (tz->temperature - tz->trips.passive.temperature)); + i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) + + (tz->trips.passive.tc2 * (tz->temperature - tz->trips.passive.temperature)); if (i > 0) *trend = THERMAL_TREND_RAISING; @@ -663,6 +659,7 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, *trend = THERMAL_TREND_DROPPING; else *trend = THERMAL_TREND_STABLE; + return 0; } @@ -687,8 +684,8 @@ static void acpi_thermal_zone_device_critical(struct thermal_zone_device *therma } static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev, - bool bind) + struct thermal_cooling_device *cdev, + bool bind) { struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; @@ -707,22 +704,23 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, if (tz->trips.passive.flags.valid) { trip++; - for (i = 0; i < tz->trips.passive.devices.count; - i++) { + for (i = 0; i < tz->trips.passive.devices.count; i++) { handle = tz->trips.passive.devices.handles[i]; dev = acpi_fetch_acpi_dev(handle); if (dev != device) continue; + if (bind) - result = - thermal_zone_bind_cooling_device - (thermal, trip, cdev, - THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); + result = thermal_zone_bind_cooling_device( + thermal, trip, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT, + THERMAL_WEIGHT_DEFAULT); else result = - thermal_zone_unbind_cooling_device - (thermal, trip, cdev); + thermal_zone_unbind_cooling_device( + thermal, trip, cdev); + if (result) goto failed; } @@ -731,22 +729,24 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { if (!tz->trips.active[i].flags.valid) break; + trip++; - for (j = 0; - j < tz->trips.active[i].devices.count; - j++) { + for (j = 0; j < tz->trips.active[i].devices.count; j++) { handle = tz->trips.active[i].devices.handles[j]; dev = acpi_fetch_acpi_dev(handle); if (dev != device) continue; + if (bind) - result = thermal_zone_bind_cooling_device - (thermal, trip, cdev, - THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); + result = thermal_zone_bind_cooling_device( + thermal, trip, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT, + THERMAL_WEIGHT_DEFAULT); else - result = thermal_zone_unbind_cooling_device - (thermal, trip, cdev); + result = thermal_zone_unbind_cooling_device( + thermal, trip, cdev); + if (result) goto failed; } @@ -758,14 +758,14 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, static int acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) + struct thermal_cooling_device *cdev) { return acpi_thermal_cooling_device_cb(thermal, cdev, true); } static int acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) + struct thermal_cooling_device *cdev) { return acpi_thermal_cooling_device_cb(thermal, cdev, false); } @@ -798,20 +798,20 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (tz->trips.passive.flags.valid) trips++; - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && - tz->trips.active[i].flags.valid; i++, trips++); + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid; + i++, trips++); if (tz->trips.passive.flags.valid) - tz->thermal_zone = - thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, NULL, - tz->trips.passive.tsp*100, - tz->polling_frequency*100); + tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, + &acpi_thermal_zone_ops, NULL, + tz->trips.passive.tsp * 100, + tz->polling_frequency * 100); else tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, NULL, - 0, tz->polling_frequency*100); + &acpi_thermal_zone_ops, NULL, + 0, tz->polling_frequency * 100); + if (IS_ERR(tz->thermal_zone)) return -ENODEV; @@ -877,7 +877,6 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event) { struct acpi_thermal *tz = acpi_driver_data(device); - if (!tz) return; @@ -889,13 +888,13 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event) acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS); acpi_queue_thermal_check(tz); acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, 0); + dev_name(&device->dev), event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); acpi_queue_thermal_check(tz); acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, 0); + dev_name(&device->dev), event, 0); break; default: acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n", @@ -940,7 +939,6 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz) { int result = 0; - if (!tz) return -EINVAL; @@ -1019,7 +1017,6 @@ static int acpi_thermal_add(struct acpi_device *device) int result = 0; struct acpi_thermal *tz = NULL; - if (!device) return -EINVAL; @@ -1095,6 +1092,7 @@ static int acpi_thermal_resume(struct device *dev) for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { if (!tz->trips.active[i].flags.valid) break; + tz->trips.active[i].flags.enabled = 1; for (j = 0; j < tz->trips.active[i].devices.count; j++) { result = acpi_bus_update_power( @@ -1115,7 +1113,6 @@ static int acpi_thermal_resume(struct device *dev) #endif static int thermal_act(const struct dmi_system_id *d) { - if (act == 0) { pr_notice("%s detected: disabling all active thermal trip points\n", d->ident); @@ -1124,14 +1121,12 @@ static int thermal_act(const struct dmi_system_id *d) { return 0; } static int thermal_nocrt(const struct dmi_system_id *d) { - pr_notice("%s detected: disabling all critical thermal trip point actions.\n", d->ident); crt = -1; return 0; } static int thermal_tzp(const struct dmi_system_id *d) { - if (tzp == 0) { pr_notice("%s detected: enabling thermal zone polling\n", d->ident); @@ -1140,7 +1135,6 @@ static int thermal_tzp(const struct dmi_system_id *d) { return 0; } static int thermal_psv(const struct dmi_system_id *d) { - if (psv == 0) { pr_notice("%s detected: disabling all passive thermal trip points\n", d->ident); From 8ead7d8e78bbacd47380fba7e5e6c028ced81118 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 4 Oct 2022 18:32:08 +0200 Subject: [PATCH 139/227] ACPI: thermal: Drop redundant parens from expressions Some expressions in the ACPI thermal driver contain redundant parentheses. Drop them. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano --- drivers/acpi/thermal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 852a413d230d5..5d084360180f0 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -322,7 +322,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* Passive (optional) */ if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) || - (flag == ACPI_TRIPS_INIT)) { + flag == ACPI_TRIPS_INIT) { valid = tz->trips.passive.flags.valid; if (psv == -1) { status = AE_SUPPORT; @@ -395,7 +395,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) if (act == -1) break; /* disable all active trip points */ - if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) && + if (flag == ACPI_TRIPS_INIT || ((flag & ACPI_TRIPS_ACTIVE) && tz->trips.active[i].flags.valid)) { status = acpi_evaluate_integer(tz->device->handle, name, NULL, &tmp); @@ -650,8 +650,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, * tz->temperature has already been updated by generic thermal layer, * before this callback being invoked */ - i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) + - (tz->trips.passive.tc2 * (tz->temperature - tz->trips.passive.temperature)); + i = tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature) + + tz->trips.passive.tc2 * (tz->temperature - tz->trips.passive.temperature); if (i > 0) *trend = THERMAL_TREND_RAISING; From c08bc006976cc17ca4871b3d6f0c67e57e1cf2f1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 4 Oct 2022 18:32:52 +0200 Subject: [PATCH 140/227] ACPI: thermal: Drop some redundant code Drop some redundant initialization of local variables, a redundant return statement and a redundant "else" from the ACPI thermal driver. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano --- drivers/acpi/thermal.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5d084360180f0..40ecb55b52f8c 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -258,7 +258,7 @@ do { \ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) { - acpi_status status = AE_OK; + acpi_status status; unsigned long long tmp; struct acpi_handle_list devices; int valid = 0; @@ -616,8 +616,9 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, tz->trips.critical.temperature, tz->kelvin_offset); return 0; - } else - return -EINVAL; + } + + return -EINVAL; } static int thermal_get_trend(struct thermal_zone_device *thermal, @@ -937,7 +938,7 @@ static void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz) static int acpi_thermal_get_info(struct acpi_thermal *tz) { - int result = 0; + int result; if (!tz) return -EINVAL; @@ -1014,8 +1015,8 @@ static void acpi_thermal_check_fn(struct work_struct *work) static int acpi_thermal_add(struct acpi_device *device) { - int result = 0; - struct acpi_thermal *tz = NULL; + struct acpi_thermal *tz; + int result; if (!device) return -EINVAL; @@ -1056,7 +1057,7 @@ static int acpi_thermal_add(struct acpi_device *device) static int acpi_thermal_remove(struct acpi_device *device) { - struct acpi_thermal *tz = NULL; + struct acpi_thermal *tz; if (!device || !acpi_driver_data(device)) return -EINVAL; @@ -1185,7 +1186,7 @@ static const struct dmi_system_id thermal_dmi_table[] __initconst = { static int __init acpi_thermal_init(void) { - int result = 0; + int result; dmi_check_system(thermal_dmi_table); @@ -1212,8 +1213,6 @@ static void __exit acpi_thermal_exit(void) { acpi_bus_unregister_driver(&acpi_thermal_driver); destroy_workqueue(acpi_thermal_pm_queue); - - return; } module_init(acpi_thermal_init); From d3fe1e1a8c62f633c7252837695ad1785df1e0f4 Mon Sep 17 00:00:00 2001 From: Dawei Li Date: Mon, 14 Nov 2022 00:26:09 +0800 Subject: [PATCH 141/227] ACPI: make remove callback of ACPI driver void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For bus-based driver, device removal is implemented as: 1 device_remove()-> 2 bus->remove()-> 3 driver->remove() Driver core needs no inform from callee(bus driver) about the result of remove callback. In that case, commit fc7a6209d571 ("bus: Make remove callback return void") forces bus_type::remove be void-returned. Now we have the situation that both 1 & 2 of calling chain are void-returned, so it does not make much sense for 3(driver->remove) to return non-void to its caller. So the basic idea behind this change is making remove() callback of any bus-based driver to be void-returned. This change, for itself, is for device drivers based on acpi-bus. Acked-by: Uwe Kleine-König Acked-by: Lee Jones Acked-by: Dmitry Torokhov Reviewed-by: Hans de Goede Signed-off-by: Dawei Li Reviewed-by: Maximilian Luz # for drivers/platform/surface/* Signed-off-by: Rafael J. Wysocki --- arch/ia64/hp/common/aml_nfw.c | 4 +- arch/x86/platform/olpc/olpc-xo15-sci.c | 3 +- drivers/acpi/ac.c | 8 +- drivers/acpi/acpi_pad.c | 3 +- drivers/acpi/acpi_video.c | 8 +- drivers/acpi/battery.c | 5 +- drivers/acpi/button.c | 5 +- drivers/acpi/ec.c | 5 +- drivers/acpi/hed.c | 3 +- drivers/acpi/nfit/core.c | 3 +- drivers/acpi/sbs.c | 9 +- drivers/acpi/sbshc.c | 7 +- drivers/acpi/thermal.c | 7 +- drivers/acpi/tiny-power-button.c | 10 +- drivers/char/sonypi.c | 3 +- drivers/char/tpm/tpm_crb.c | 4 +- drivers/hv/vmbus_drv.c | 4 +- drivers/hwmon/acpi_power_meter.c | 5 +- drivers/hwmon/asus_atk0110.c | 6 +- drivers/input/misc/atlas_btns.c | 4 +- drivers/net/fjes/fjes_main.c | 4 +- .../platform/chrome/chromeos_privacy_screen.c | 152 ++++++++++++++++++ drivers/platform/chrome/wilco_ec/event.c | 4 +- drivers/platform/surface/surfacepro3_button.c | 3 +- drivers/platform/x86/asus-laptop.c | 3 +- drivers/platform/x86/asus-wireless.c | 3 +- drivers/platform/x86/classmate-laptop.c | 20 ++- drivers/platform/x86/dell/dell-rbtn.c | 6 +- drivers/platform/x86/eeepc-laptop.c | 3 +- drivers/platform/x86/fujitsu-laptop.c | 4 +- drivers/platform/x86/fujitsu-tablet.c | 3 +- drivers/platform/x86/intel/rst.c | 4 +- drivers/platform/x86/lg-laptop.c | 4 +- drivers/platform/x86/panasonic-laptop.c | 8 +- drivers/platform/x86/sony-laptop.c | 9 +- drivers/platform/x86/system76_acpi.c | 4 +- drivers/platform/x86/topstar-laptop.c | 3 +- drivers/platform/x86/toshiba_acpi.c | 4 +- drivers/platform/x86/toshiba_bluetooth.c | 6 +- drivers/platform/x86/toshiba_haps.c | 4 +- drivers/platform/x86/wireless-hotkey.c | 3 +- drivers/platform/x86/xo15-ebook.c | 3 +- drivers/ptp/ptp_vmw.c | 3 +- drivers/thermal/intel/intel_menlow.c | 8 +- drivers/video/backlight/apple_bl.c | 3 +- drivers/watchdog/ni903x_wdt.c | 4 +- drivers/xen/xen-acpi-pad.c | 3 +- include/acpi/acpi_bus.h | 2 +- 48 files changed, 239 insertions(+), 147 deletions(-) create mode 100644 drivers/platform/chrome/chromeos_privacy_screen.c diff --git a/arch/ia64/hp/common/aml_nfw.c b/arch/ia64/hp/common/aml_nfw.c index 684667ade5259..901df49461a05 100644 --- a/arch/ia64/hp/common/aml_nfw.c +++ b/arch/ia64/hp/common/aml_nfw.c @@ -187,9 +187,9 @@ static int aml_nfw_add(struct acpi_device *device) return aml_nfw_add_global_handler(); } -static int aml_nfw_remove(struct acpi_device *device) +static void aml_nfw_remove(struct acpi_device *device) { - return aml_nfw_remove_global_handler(); + aml_nfw_remove_global_handler(); } static const struct acpi_device_id aml_nfw_ids[] = { diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 994a229cb79fe..68244a3422d1d 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -183,13 +183,12 @@ static int xo15_sci_add(struct acpi_device *device) return r; } -static int xo15_sci_remove(struct acpi_device *device) +static void xo15_sci_remove(struct acpi_device *device) { acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); cancel_work_sync(&sci_work); sysfs_remove_file(&device->dev.kobj, &lid_wake_on_close_attr.attr); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 81aff651a0d49..2deb903917467 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -34,7 +34,7 @@ MODULE_LICENSE("GPL"); static int acpi_ac_add(struct acpi_device *device); -static int acpi_ac_remove(struct acpi_device *device); +static void acpi_ac_remove(struct acpi_device *device); static void acpi_ac_notify(struct acpi_device *device, u32 event); struct acpi_ac_bl { @@ -321,13 +321,13 @@ static int acpi_ac_resume(struct device *dev) #define acpi_ac_resume NULL #endif -static int acpi_ac_remove(struct acpi_device *device) +static void acpi_ac_remove(struct acpi_device *device) { struct acpi_ac *ac = NULL; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; ac = acpi_driver_data(device); @@ -335,8 +335,6 @@ static int acpi_ac_remove(struct acpi_device *device) unregister_acpi_notifier(&ac->battery_nb); kfree(ac); - - return 0; } static int __init acpi_ac_init(void) diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index ec0e22a1e25d6..edbb28faee2a6 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -449,7 +449,7 @@ static int acpi_pad_add(struct acpi_device *device) return 0; } -static int acpi_pad_remove(struct acpi_device *device) +static void acpi_pad_remove(struct acpi_device *device) { mutex_lock(&isolated_cpus_lock); acpi_pad_idle_cpus(0); @@ -458,7 +458,6 @@ static int acpi_pad_remove(struct acpi_device *device) acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_pad_notify); acpi_pad_remove_sysfs(device); - return 0; } static const struct acpi_device_id pad_device_ids[] = { diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index e87f4bf82d8b0..ba938b9a43921 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -79,7 +79,7 @@ static DEFINE_MUTEX(register_count_mutex); static DEFINE_MUTEX(video_list_lock); static LIST_HEAD(video_bus_head); static int acpi_video_bus_add(struct acpi_device *device); -static int acpi_video_bus_remove(struct acpi_device *device); +static void acpi_video_bus_remove(struct acpi_device *device); static void acpi_video_bus_notify(struct acpi_device *device, u32 event); void acpi_video_detect_exit(void); @@ -2136,13 +2136,13 @@ static int acpi_video_bus_add(struct acpi_device *device) return error; } -static int acpi_video_bus_remove(struct acpi_device *device) +static void acpi_video_bus_remove(struct acpi_device *device) { struct acpi_video_bus *video = NULL; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; video = acpi_driver_data(device); @@ -2156,8 +2156,6 @@ static int acpi_video_bus_remove(struct acpi_device *device) kfree(video->attached_array); kfree(video); - - return 0; } static int __init is_i740(struct pci_dev *dev) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index c7569151fd02a..dae63a9f80985 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1259,12 +1259,12 @@ static int acpi_battery_add(struct acpi_device *device) return result; } -static int acpi_battery_remove(struct acpi_device *device) +static void acpi_battery_remove(struct acpi_device *device) { struct acpi_battery *battery = NULL; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; device_init_wakeup(&device->dev, 0); battery = acpi_driver_data(device); unregister_pm_notifier(&battery->pm_nb); @@ -1272,7 +1272,6 @@ static int acpi_battery_remove(struct acpi_device *device) mutex_destroy(&battery->lock); mutex_destroy(&battery->sysfs_lock); kfree(battery); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index d8b1481141385..ef77c14c72a92 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -134,7 +134,7 @@ static const struct dmi_system_id dmi_lid_quirks[] = { }; static int acpi_button_add(struct acpi_device *device); -static int acpi_button_remove(struct acpi_device *device); +static void acpi_button_remove(struct acpi_device *device); static void acpi_button_notify(struct acpi_device *device, u32 event); #ifdef CONFIG_PM_SLEEP @@ -589,14 +589,13 @@ static int acpi_button_add(struct acpi_device *device) return error; } -static int acpi_button_remove(struct acpi_device *device) +static void acpi_button_remove(struct acpi_device *device) { struct acpi_button *button = acpi_driver_data(device); acpi_button_remove_fs(device); input_unregister_device(button->input); kfree(button); - return 0; } static int param_set_lid_init_state(const char *val, diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 472418a0e0cab..93cbb10b42629 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1650,12 +1650,12 @@ static int acpi_ec_add(struct acpi_device *device) return ret; } -static int acpi_ec_remove(struct acpi_device *device) +static void acpi_ec_remove(struct acpi_device *device) { struct acpi_ec *ec; if (!device) - return -EINVAL; + return; ec = acpi_driver_data(device); release_region(ec->data_addr, 1); @@ -1665,7 +1665,6 @@ static int acpi_ec_remove(struct acpi_device *device) ec_remove_handlers(ec); acpi_ec_free(ec); } - return 0; } static acpi_status diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c index 60a2939cde6c5..78d44e3fe1295 100644 --- a/drivers/acpi/hed.c +++ b/drivers/acpi/hed.c @@ -56,10 +56,9 @@ static int acpi_hed_add(struct acpi_device *device) return 0; } -static int acpi_hed_remove(struct acpi_device *device) +static void acpi_hed_remove(struct acpi_device *device) { hed_handle = NULL; - return 0; } static struct acpi_driver acpi_hed_driver = { diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 9cc83df22de49..88b8656c3956d 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3755,10 +3755,9 @@ static int acpi_nfit_add(struct acpi_device *adev) return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc); } -static int acpi_nfit_remove(struct acpi_device *adev) +static void acpi_nfit_remove(struct acpi_device *adev) { /* see acpi_nfit_unregister */ - return 0; } static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle) diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 4938010fcac78..811f9eb7292ac 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -96,7 +96,7 @@ struct acpi_sbs { #define to_acpi_sbs(x) power_supply_get_drvdata(x) -static int acpi_sbs_remove(struct acpi_device *device); +static void acpi_sbs_remove(struct acpi_device *device); static int acpi_battery_get_state(struct acpi_battery *battery); static inline int battery_scale(int log) @@ -664,16 +664,16 @@ static int acpi_sbs_add(struct acpi_device *device) return result; } -static int acpi_sbs_remove(struct acpi_device *device) +static void acpi_sbs_remove(struct acpi_device *device) { struct acpi_sbs *sbs; int id; if (!device) - return -EINVAL; + return; sbs = acpi_driver_data(device); if (!sbs) - return -EINVAL; + return; mutex_lock(&sbs->lock); acpi_smbus_unregister_callback(sbs->hc); for (id = 0; id < MAX_SBS_BAT; ++id) @@ -682,7 +682,6 @@ static int acpi_sbs_remove(struct acpi_device *device) mutex_unlock(&sbs->lock); mutex_destroy(&sbs->lock); kfree(sbs); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index 7c62e149a7a16..3fd1cc0dadb87 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -30,7 +30,7 @@ struct acpi_smb_hc { }; static int acpi_smbus_hc_add(struct acpi_device *device); -static int acpi_smbus_hc_remove(struct acpi_device *device); +static void acpi_smbus_hc_remove(struct acpi_device *device); static const struct acpi_device_id sbs_device_ids[] = { {"ACPI0001", 0}, @@ -280,19 +280,18 @@ static int acpi_smbus_hc_add(struct acpi_device *device) extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); -static int acpi_smbus_hc_remove(struct acpi_device *device) +static void acpi_smbus_hc_remove(struct acpi_device *device) { struct acpi_smb_hc *hc; if (!device) - return -EINVAL; + return; hc = acpi_driver_data(device); acpi_ec_remove_query_handler(hc->ec, hc->query_bit); acpi_os_wait_events_complete(); kfree(hc); device->driver_data = NULL; - return 0; } module_acpi_driver(acpi_smb_hc_driver); diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 40ecb55b52f8c..642d498d448bb 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -70,7 +70,7 @@ MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); static struct workqueue_struct *acpi_thermal_pm_queue; static int acpi_thermal_add(struct acpi_device *device); -static int acpi_thermal_remove(struct acpi_device *device); +static void acpi_thermal_remove(struct acpi_device *device); static void acpi_thermal_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id thermal_device_ids[] = { @@ -1055,19 +1055,18 @@ static int acpi_thermal_add(struct acpi_device *device) return result; } -static int acpi_thermal_remove(struct acpi_device *device) +static void acpi_thermal_remove(struct acpi_device *device) { struct acpi_thermal *tz; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; flush_workqueue(acpi_thermal_pm_queue); tz = acpi_driver_data(device); acpi_thermal_unregister_thermal_zone(tz); kfree(tz); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/acpi/tiny-power-button.c b/drivers/acpi/tiny-power-button.c index a19f0e4e69f76..598f548b21f36 100644 --- a/drivers/acpi/tiny-power-button.c +++ b/drivers/acpi/tiny-power-button.c @@ -19,11 +19,15 @@ static const struct acpi_device_id tiny_power_button_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, tiny_power_button_device_ids); -static int acpi_noop_add_remove(struct acpi_device *device) +static int acpi_noop_add(struct acpi_device *device) { return 0; } +static void acpi_noop_remove(struct acpi_device *device) +{ +} + static void acpi_tiny_power_button_notify(struct acpi_device *device, u32 event) { kill_cad_pid(power_signal, 1); @@ -34,8 +38,8 @@ static struct acpi_driver acpi_tiny_power_button_driver = { .class = "tiny-power-button", .ids = tiny_power_button_device_ids, .ops = { - .add = acpi_noop_add_remove, - .remove = acpi_noop_add_remove, + .add = acpi_noop_add, + .remove = acpi_noop_remove, .notify = acpi_tiny_power_button_notify, }, }; diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 27e301a6bb7a3..9211531689b28 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1123,10 +1123,9 @@ static int sonypi_acpi_add(struct acpi_device *device) return 0; } -static int sonypi_acpi_remove(struct acpi_device *device) +static void sonypi_acpi_remove(struct acpi_device *device) { sonypi_acpi_device = NULL; - return 0; } static const struct acpi_device_id sonypi_device_ids[] = { diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 16fc481d60950..7e9da671a0e84 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -724,14 +724,12 @@ static int crb_acpi_add(struct acpi_device *device) return rc; } -static int crb_acpi_remove(struct acpi_device *device) +static void crb_acpi_remove(struct acpi_device *device) { struct device *dev = &device->dev; struct tpm_chip *chip = dev_get_drvdata(dev); tpm_chip_unregister(chip); - - return 0; } static const struct dev_pm_ops crb_pm = { diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 115835bd562c7..c0cafd728af7b 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2266,7 +2266,7 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) return AE_OK; } -static int vmbus_acpi_remove(struct acpi_device *device) +static void vmbus_acpi_remove(struct acpi_device *device) { struct resource *cur_res; struct resource *next_res; @@ -2283,8 +2283,6 @@ static int vmbus_acpi_remove(struct acpi_device *device) kfree(cur_res); } } - - return 0; } static void vmbus_reserve_fb(void) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 11f6e074c984e..27c278431ba9e 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -948,12 +948,12 @@ static int acpi_power_meter_add(struct acpi_device *device) return res; } -static int acpi_power_meter_remove(struct acpi_device *device) +static void acpi_power_meter_remove(struct acpi_device *device) { struct acpi_power_meter_resource *resource; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; resource = acpi_driver_data(device); hwmon_device_unregister(resource->hwmon_dev); @@ -962,7 +962,6 @@ static int acpi_power_meter_remove(struct acpi_device *device) free_capabilities(resource); kfree(resource); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index ff64a39d56def..d778a2aaefec1 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -187,7 +187,7 @@ struct atk_acpi_input_buf { }; static int atk_add(struct acpi_device *device); -static int atk_remove(struct acpi_device *device); +static void atk_remove(struct acpi_device *device); static void atk_print_sensor(struct atk_data *data, union acpi_object *obj); static int atk_read_value(struct atk_sensor_data *sensor, u64 *value); @@ -1344,7 +1344,7 @@ static int atk_add(struct acpi_device *device) return err; } -static int atk_remove(struct acpi_device *device) +static void atk_remove(struct acpi_device *device) { struct atk_data *data = device->driver_data; dev_dbg(&device->dev, "removing...\n"); @@ -1359,8 +1359,6 @@ static int atk_remove(struct acpi_device *device) if (atk_ec_ctl(data, 0)) dev_err(&device->dev, "Failed to disable EC\n"); } - - return 0; } static int __init atk0110_init(void) diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 0e77c40e1966b..3c9bbd04e1434 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -106,7 +106,7 @@ static int atlas_acpi_button_add(struct acpi_device *device) return err; } -static int atlas_acpi_button_remove(struct acpi_device *device) +static void atlas_acpi_button_remove(struct acpi_device *device) { acpi_status status; @@ -116,8 +116,6 @@ static int atlas_acpi_button_remove(struct acpi_device *device) pr_err("error removing addr spc handler\n"); input_unregister_device(input_dev); - - return 0; } static const struct acpi_device_id atlas_device_ids[] = { diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index 1d1808afd5295..6cacc8c3b6ccd 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -166,14 +166,12 @@ static int fjes_acpi_add(struct acpi_device *device) return 0; } -static int fjes_acpi_remove(struct acpi_device *device) +static void fjes_acpi_remove(struct acpi_device *device) { struct platform_device *plat_dev; plat_dev = (struct platform_device *)acpi_driver_data(device); platform_device_unregister(plat_dev); - - return 0; } static acpi_status diff --git a/drivers/platform/chrome/chromeos_privacy_screen.c b/drivers/platform/chrome/chromeos_privacy_screen.c new file mode 100644 index 0000000000000..bb74ddf9af4ac --- /dev/null +++ b/drivers/platform/chrome/chromeos_privacy_screen.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * ChromeOS Privacy Screen support + * + * Copyright (C) 2022 Google LLC + * + * This is the Chromeos privacy screen provider, present on certain chromebooks, + * represented by a GOOG0010 device in the ACPI. This ACPI device, if present, + * will cause the i915 drm driver to probe defer until this driver registers + * the privacy-screen. + */ + +#include +#include + +/* + * The DSM (Device Specific Method) constants below are the agreed API with + * the firmware team, on how to control privacy screen using ACPI methods. + */ +#define PRIV_SCRN_DSM_REVID 1 /* DSM version */ +#define PRIV_SCRN_DSM_FN_GET_STATUS 1 /* Get privacy screen status */ +#define PRIV_SCRN_DSM_FN_ENABLE 2 /* Enable privacy screen */ +#define PRIV_SCRN_DSM_FN_DISABLE 3 /* Disable privacy screen */ + +static const guid_t chromeos_privacy_screen_dsm_guid = + GUID_INIT(0xc7033113, 0x8720, 0x4ceb, + 0x90, 0x90, 0x9d, 0x52, 0xb3, 0xe5, 0x2d, 0x73); + +static void +chromeos_privacy_screen_get_hw_state(struct drm_privacy_screen + *drm_privacy_screen) +{ + union acpi_object *obj; + acpi_handle handle; + struct device *privacy_screen = + drm_privacy_screen_get_drvdata(drm_privacy_screen); + + handle = acpi_device_handle(to_acpi_device(privacy_screen)); + obj = acpi_evaluate_dsm(handle, &chromeos_privacy_screen_dsm_guid, + PRIV_SCRN_DSM_REVID, + PRIV_SCRN_DSM_FN_GET_STATUS, NULL); + if (!obj) { + dev_err(privacy_screen, + "_DSM failed to get privacy-screen state\n"); + return; + } + + if (obj->type != ACPI_TYPE_INTEGER) + dev_err(privacy_screen, + "Bad _DSM to get privacy-screen state\n"); + else if (obj->integer.value == 1) + drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = + PRIVACY_SCREEN_ENABLED; + else + drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = + PRIVACY_SCREEN_DISABLED; + + ACPI_FREE(obj); +} + +static int +chromeos_privacy_screen_set_sw_state(struct drm_privacy_screen + *drm_privacy_screen, + enum drm_privacy_screen_status state) +{ + union acpi_object *obj = NULL; + acpi_handle handle; + struct device *privacy_screen = + drm_privacy_screen_get_drvdata(drm_privacy_screen); + + handle = acpi_device_handle(to_acpi_device(privacy_screen)); + + if (state == PRIVACY_SCREEN_DISABLED) { + obj = acpi_evaluate_dsm(handle, + &chromeos_privacy_screen_dsm_guid, + PRIV_SCRN_DSM_REVID, + PRIV_SCRN_DSM_FN_DISABLE, NULL); + } else if (state == PRIVACY_SCREEN_ENABLED) { + obj = acpi_evaluate_dsm(handle, + &chromeos_privacy_screen_dsm_guid, + PRIV_SCRN_DSM_REVID, + PRIV_SCRN_DSM_FN_ENABLE, NULL); + } else { + dev_err(privacy_screen, + "Bad attempt to set privacy-screen status to %u\n", + state); + return -EINVAL; + } + + if (!obj) { + dev_err(privacy_screen, + "_DSM failed to set privacy-screen state\n"); + return -EIO; + } + + drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = state; + ACPI_FREE(obj); + return 0; +} + +static const struct drm_privacy_screen_ops chromeos_privacy_screen_ops = { + .get_hw_state = chromeos_privacy_screen_get_hw_state, + .set_sw_state = chromeos_privacy_screen_set_sw_state, +}; + +static int chromeos_privacy_screen_add(struct acpi_device *adev) +{ + struct drm_privacy_screen *drm_privacy_screen = + drm_privacy_screen_register(&adev->dev, + &chromeos_privacy_screen_ops, + &adev->dev); + + if (IS_ERR(drm_privacy_screen)) { + dev_err(&adev->dev, "Error registering privacy-screen\n"); + return PTR_ERR(drm_privacy_screen); + } + + adev->driver_data = drm_privacy_screen; + dev_info(&adev->dev, "registered privacy-screen '%s'\n", + dev_name(&drm_privacy_screen->dev)); + + return 0; +} + +static void chromeos_privacy_screen_remove(struct acpi_device *adev) +{ + struct drm_privacy_screen *drm_privacy_screen = acpi_driver_data(adev); + + drm_privacy_screen_unregister(drm_privacy_screen); +} + +static const struct acpi_device_id chromeos_privacy_screen_device_ids[] = { + {"GOOG0010", 0}, /* Google's electronic privacy screen for eDP-1 */ + {} +}; +MODULE_DEVICE_TABLE(acpi, chromeos_privacy_screen_device_ids); + +static struct acpi_driver chromeos_privacy_screen_driver = { + .name = "chromeos_privacy_screen_driver", + .class = "ChromeOS", + .ids = chromeos_privacy_screen_device_ids, + .ops = { + .add = chromeos_privacy_screen_add, + .remove = chromeos_privacy_screen_remove, + }, +}; + +module_acpi_driver(chromeos_privacy_screen_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS ACPI Privacy Screen driver"); +MODULE_AUTHOR("Rajat Jain "); diff --git a/drivers/platform/chrome/wilco_ec/event.c b/drivers/platform/chrome/wilco_ec/event.c index 814518509739d..a873066b03825 100644 --- a/drivers/platform/chrome/wilco_ec/event.c +++ b/drivers/platform/chrome/wilco_ec/event.c @@ -500,15 +500,13 @@ static int event_device_add(struct acpi_device *adev) return error; } -static int event_device_remove(struct acpi_device *adev) +static void event_device_remove(struct acpi_device *adev) { struct event_device_data *dev_data = adev->driver_data; cdev_device_del(&dev_data->cdev, &dev_data->dev); ida_simple_remove(&event_ida, MINOR(dev_data->dev.devt)); hangup_device(dev_data); - - return 0; } static const struct acpi_device_id event_acpi_ids[] = { diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index 242fb690dcaf7..2755601f979cd 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -239,13 +239,12 @@ static int surface_button_add(struct acpi_device *device) return error; } -static int surface_button_remove(struct acpi_device *device) +static void surface_button_remove(struct acpi_device *device) { struct surface_button *button = acpi_driver_data(device); input_unregister_device(button->input); kfree(button); - return 0; } static SIMPLE_DEV_PM_OPS(surface_button_pm, diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 4d2d32bfbe2a6..a6dbe05af271b 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1901,7 +1901,7 @@ static int asus_acpi_add(struct acpi_device *device) return result; } -static int asus_acpi_remove(struct acpi_device *device) +static void asus_acpi_remove(struct acpi_device *device) { struct asus_laptop *asus = acpi_driver_data(device); @@ -1914,7 +1914,6 @@ static int asus_acpi_remove(struct acpi_device *device) kfree(asus->name); kfree(asus); - return 0; } static const struct acpi_device_id asus_device_ids[] = { diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index d3e7171928e5d..abf01e00b799f 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -175,7 +175,7 @@ static int asus_wireless_add(struct acpi_device *adev) return err; } -static int asus_wireless_remove(struct acpi_device *adev) +static void asus_wireless_remove(struct acpi_device *adev) { struct asus_wireless_data *data = acpi_driver_data(adev); @@ -183,7 +183,6 @@ static int asus_wireless_remove(struct acpi_device *adev) devm_led_classdev_unregister(&adev->dev, &data->led); destroy_workqueue(data->wq); } - return 0; } static struct acpi_driver asus_wireless_driver = { diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 9309ab5792cbc..8b6a146118599 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -418,11 +418,11 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) return error; } -static int cmpc_accel_remove_v4(struct acpi_device *acpi) +static void cmpc_accel_remove_v4(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); - return cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(acpi); } static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, @@ -648,10 +648,10 @@ static int cmpc_accel_add(struct acpi_device *acpi) return error; } -static int cmpc_accel_remove(struct acpi_device *acpi) +static void cmpc_accel_remove(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); - return cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(acpi); } static const struct acpi_device_id cmpc_accel_device_ids[] = { @@ -727,9 +727,9 @@ static int cmpc_tablet_add(struct acpi_device *acpi) cmpc_tablet_idev_init); } -static int cmpc_tablet_remove(struct acpi_device *acpi) +static void cmpc_tablet_remove(struct acpi_device *acpi) { - return cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(acpi); } #ifdef CONFIG_PM_SLEEP @@ -974,7 +974,7 @@ static int cmpc_ipml_add(struct acpi_device *acpi) return retval; } -static int cmpc_ipml_remove(struct acpi_device *acpi) +static void cmpc_ipml_remove(struct acpi_device *acpi) { struct ipml200_dev *ipml; @@ -988,8 +988,6 @@ static int cmpc_ipml_remove(struct acpi_device *acpi) } kfree(ipml); - - return 0; } static const struct acpi_device_id cmpc_ipml_device_ids[] = { @@ -1055,9 +1053,9 @@ static int cmpc_keys_add(struct acpi_device *acpi) cmpc_keys_idev_init); } -static int cmpc_keys_remove(struct acpi_device *acpi) +static void cmpc_keys_remove(struct acpi_device *acpi) { - return cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(acpi); } static const struct acpi_device_id cmpc_keys_device_ids[] = { diff --git a/drivers/platform/x86/dell/dell-rbtn.c b/drivers/platform/x86/dell/dell-rbtn.c index a89fad47ff139..aa0e6c9074942 100644 --- a/drivers/platform/x86/dell/dell-rbtn.c +++ b/drivers/platform/x86/dell/dell-rbtn.c @@ -206,7 +206,7 @@ static void rbtn_input_event(struct rbtn_data *rbtn_data) */ static int rbtn_add(struct acpi_device *device); -static int rbtn_remove(struct acpi_device *device); +static void rbtn_remove(struct acpi_device *device); static void rbtn_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id rbtn_ids[] = { @@ -426,7 +426,7 @@ static int rbtn_add(struct acpi_device *device) } -static int rbtn_remove(struct acpi_device *device) +static void rbtn_remove(struct acpi_device *device) { struct rbtn_data *rbtn_data = device->driver_data; @@ -443,8 +443,6 @@ static int rbtn_remove(struct acpi_device *device) rbtn_acquire(device, false); device->driver_data = NULL; - - return 0; } static void rbtn_notify(struct acpi_device *device, u32 event) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ba08c9235f769..c8b3fdabbc5e3 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1440,7 +1440,7 @@ static int eeepc_acpi_add(struct acpi_device *device) return result; } -static int eeepc_acpi_remove(struct acpi_device *device) +static void eeepc_acpi_remove(struct acpi_device *device) { struct eeepc_laptop *eeepc = acpi_driver_data(device); @@ -1451,7 +1451,6 @@ static int eeepc_acpi_remove(struct acpi_device *device) eeepc_platform_exit(eeepc); kfree(eeepc); - return 0; } diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 80929380ec7e3..5c30794c0c344 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -847,15 +847,13 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) return ret; } -static int acpi_fujitsu_laptop_remove(struct acpi_device *device) +static void acpi_fujitsu_laptop_remove(struct acpi_device *device) { struct fujitsu_laptop *priv = acpi_driver_data(device); fujitsu_laptop_platform_remove(device); kfifo_free(&priv->fifo); - - return 0; } static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 7fb7fe5eb55a1..17f08ce7552d7 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -484,12 +484,11 @@ static int acpi_fujitsu_add(struct acpi_device *adev) return 0; } -static int acpi_fujitsu_remove(struct acpi_device *adev) +static void acpi_fujitsu_remove(struct acpi_device *adev) { free_irq(fujitsu.irq, fujitsu_interrupt); release_region(fujitsu.io_base, fujitsu.io_length); input_fujitsu_remove(); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/platform/x86/intel/rst.c b/drivers/platform/x86/intel/rst.c index 3b81cb896fedf..35814a7707af7 100644 --- a/drivers/platform/x86/intel/rst.c +++ b/drivers/platform/x86/intel/rst.c @@ -113,12 +113,10 @@ static int irst_add(struct acpi_device *acpi) return error; } -static int irst_remove(struct acpi_device *acpi) +static void irst_remove(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &irst_wakeup_attr); device_remove_file(&acpi->dev, &irst_timeout_attr); - - return 0; } static const struct acpi_device_id irst_ids[] = { diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 88b551caeaaf4..f406efac58300 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -710,7 +710,7 @@ static int acpi_add(struct acpi_device *device) return ret; } -static int acpi_remove(struct acpi_device *device) +static void acpi_remove(struct acpi_device *device) { sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); @@ -721,8 +721,6 @@ static int acpi_remove(struct acpi_device *device) platform_device_unregister(pf_device); pf_device = NULL; platform_driver_unregister(&pf_driver); - - return 0; } static const struct acpi_device_id device_ids[] = { diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index b06382dcecf78..3b1d1b51dc570 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -183,7 +183,7 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0, /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */ static int acpi_pcc_hotkey_add(struct acpi_device *device); -static int acpi_pcc_hotkey_remove(struct acpi_device *device); +static void acpi_pcc_hotkey_remove(struct acpi_device *device); static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id pcc_device_ids[] = { @@ -1090,12 +1090,12 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) return result; } -static int acpi_pcc_hotkey_remove(struct acpi_device *device) +static void acpi_pcc_hotkey_remove(struct acpi_device *device) { struct pcc_acpi *pcc = acpi_driver_data(device); if (!device || !pcc) - return -EINVAL; + return; i8042_remove_filter(panasonic_i8042_filter); @@ -1113,8 +1113,6 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device) kfree(pcc->sinf); kfree(pcc); - - return 0; } module_acpi_driver(acpi_pcc_driver); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 336dee9485d4b..07d2e1901f010 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -3280,7 +3280,7 @@ static int sony_nc_add(struct acpi_device *device) return result; } -static int sony_nc_remove(struct acpi_device *device) +static void sony_nc_remove(struct acpi_device *device) { struct sony_nc_value *item; @@ -3297,8 +3297,6 @@ static int sony_nc_remove(struct acpi_device *device) sony_pf_remove(); sony_laptop_remove_input(); dprintk(SONY_NC_DRIVER_NAME " removed.\n"); - - return 0; } static const struct acpi_device_id sony_device_ids[] = { @@ -4652,14 +4650,14 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) * ACPI driver * *****************/ -static int sony_pic_remove(struct acpi_device *device) +static void sony_pic_remove(struct acpi_device *device) { struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; if (sony_pic_disable(device)) { pr_err("Couldn't disable device\n"); - return -ENXIO; + return; } free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); @@ -4689,7 +4687,6 @@ static int sony_pic_remove(struct acpi_device *device) spic_dev.cur_irq = NULL; dprintk(SONY_PIC_DRIVER_NAME " removed.\n"); - return 0; } static int sony_pic_add(struct acpi_device *device) diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index c14fd22ba1968..85ba3c6acde9e 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -350,7 +350,7 @@ static int system76_add(struct acpi_device *acpi_dev) } // Remove a System76 ACPI device -static int system76_remove(struct acpi_device *acpi_dev) +static void system76_remove(struct acpi_device *acpi_dev) { struct system76_data *data; @@ -363,8 +363,6 @@ static int system76_remove(struct acpi_device *acpi_dev) devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led); system76_get(data, "FINI"); - - return 0; } static struct acpi_driver system76_driver = { diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index f7761d98c0fd0..2e4913b993332 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -332,7 +332,7 @@ static int topstar_acpi_add(struct acpi_device *device) return err; } -static int topstar_acpi_remove(struct acpi_device *device) +static void topstar_acpi_remove(struct acpi_device *device) { struct topstar_laptop *topstar = acpi_driver_data(device); @@ -344,7 +344,6 @@ static int topstar_acpi_remove(struct acpi_device *device) topstar_acpi_exit(topstar); kfree(topstar); - return 0; } static const struct acpi_device_id topstar_device_ids[] = { diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 352508d304675..b50333c952b3f 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -2960,7 +2960,7 @@ static void print_supported_features(struct toshiba_acpi_dev *dev) pr_cont("\n"); } -static int toshiba_acpi_remove(struct acpi_device *acpi_dev) +static void toshiba_acpi_remove(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); @@ -3000,8 +3000,6 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) toshiba_acpi = NULL; kfree(dev); - - return 0; } static const char *find_hci_method(acpi_handle handle) diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 57a5dc60c58a6..d8f81962a2402 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -36,7 +36,7 @@ struct toshiba_bluetooth_dev { }; static int toshiba_bt_rfkill_add(struct acpi_device *device); -static int toshiba_bt_rfkill_remove(struct acpi_device *device); +static void toshiba_bt_rfkill_remove(struct acpi_device *device); static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id bt_device_ids[] = { @@ -279,7 +279,7 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) return result; } -static int toshiba_bt_rfkill_remove(struct acpi_device *device) +static void toshiba_bt_rfkill_remove(struct acpi_device *device) { struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device); @@ -291,7 +291,7 @@ static int toshiba_bt_rfkill_remove(struct acpi_device *device) kfree(bt_dev); - return toshiba_bluetooth_disable(device->handle); + toshiba_bluetooth_disable(device->handle); } module_acpi_driver(toshiba_bt_rfkill_driver); diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index 49e84095bb010..8c9f76286b080 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -138,14 +138,12 @@ static void toshiba_haps_notify(struct acpi_device *device, u32 event) event, 0); } -static int toshiba_haps_remove(struct acpi_device *device) +static void toshiba_haps_remove(struct acpi_device *device) { sysfs_remove_group(&device->dev.kobj, &haps_attr_group); if (toshiba_haps) toshiba_haps = NULL; - - return 0; } /* Helper function */ diff --git a/drivers/platform/x86/wireless-hotkey.c b/drivers/platform/x86/wireless-hotkey.c index 11c60a2734468..c64d9f0844049 100644 --- a/drivers/platform/x86/wireless-hotkey.c +++ b/drivers/platform/x86/wireless-hotkey.c @@ -83,10 +83,9 @@ static int wl_add(struct acpi_device *device) return err; } -static int wl_remove(struct acpi_device *device) +static void wl_remove(struct acpi_device *device) { wireless_input_destroy(); - return 0; } static struct acpi_driver wl_driver = { diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 97440462aa258..391f7ea4431e0 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -143,13 +143,12 @@ static int ebook_switch_add(struct acpi_device *device) return error; } -static int ebook_switch_remove(struct acpi_device *device) +static void ebook_switch_remove(struct acpi_device *device) { struct ebook_switch *button = acpi_driver_data(device); input_unregister_device(button->input); kfree(button); - return 0; } static struct acpi_driver xo15_ebook_driver = { diff --git a/drivers/ptp/ptp_vmw.c b/drivers/ptp/ptp_vmw.c index 5dca26e14bdc8..0dcbabd1533db 100644 --- a/drivers/ptp/ptp_vmw.c +++ b/drivers/ptp/ptp_vmw.c @@ -101,10 +101,9 @@ static int ptp_vmw_acpi_add(struct acpi_device *device) return 0; } -static int ptp_vmw_acpi_remove(struct acpi_device *device) +static void ptp_vmw_acpi_remove(struct acpi_device *device) { ptp_clock_unregister(ptp_vmw_clock); - return 0; } static const struct acpi_device_id ptp_vmw_acpi_device_ids[] = { diff --git a/drivers/thermal/intel/intel_menlow.c b/drivers/thermal/intel/intel_menlow.c index 101d7e791a137..3f885b08a4909 100644 --- a/drivers/thermal/intel/intel_menlow.c +++ b/drivers/thermal/intel/intel_menlow.c @@ -179,22 +179,20 @@ static int intel_menlow_memory_add(struct acpi_device *device) } -static int intel_menlow_memory_remove(struct acpi_device *device) +static void intel_menlow_memory_remove(struct acpi_device *device) { struct thermal_cooling_device *cdev; if (!device) - return -EINVAL; + return; cdev = acpi_driver_data(device); if (!cdev) - return -EINVAL; + return; sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&cdev->device.kobj, "device"); thermal_cooling_device_unregister(cdev); - - return 0; } static const struct acpi_device_id intel_menlow_memory_ids[] = { diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c index c0d9339cff873..e9e7acb577bfa 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -193,13 +193,12 @@ static int apple_bl_add(struct acpi_device *dev) return 0; } -static int apple_bl_remove(struct acpi_device *dev) +static void apple_bl_remove(struct acpi_device *dev) { backlight_device_unregister(apple_backlight_device); release_region(hw_data->iostart, hw_data->iolen); hw_data = NULL; - return 0; } static const struct acpi_device_id apple_bl_ids[] = { diff --git a/drivers/watchdog/ni903x_wdt.c b/drivers/watchdog/ni903x_wdt.c index 4cebad324b202..045bb72d9a438 100644 --- a/drivers/watchdog/ni903x_wdt.c +++ b/drivers/watchdog/ni903x_wdt.c @@ -224,14 +224,12 @@ static int ni903x_acpi_add(struct acpi_device *device) return 0; } -static int ni903x_acpi_remove(struct acpi_device *device) +static void ni903x_acpi_remove(struct acpi_device *device) { struct ni903x_wdt *wdt = acpi_driver_data(device); ni903x_wdd_stop(&wdt->wdd); watchdog_unregister_device(&wdt->wdd); - - return 0; } static const struct acpi_device_id ni903x_device_ids[] = { diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c index ccd8012020f15..ede69a5278d3f 100644 --- a/drivers/xen/xen-acpi-pad.c +++ b/drivers/xen/xen-acpi-pad.c @@ -122,7 +122,7 @@ static int acpi_pad_add(struct acpi_device *device) return 0; } -static int acpi_pad_remove(struct acpi_device *device) +static void acpi_pad_remove(struct acpi_device *device) { mutex_lock(&xen_cpu_lock); xen_acpi_pad_idle_cpus(0); @@ -130,7 +130,6 @@ static int acpi_pad_remove(struct acpi_device *device) acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_pad_notify); - return 0; } static const struct acpi_device_id pad_device_ids[] = { diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 8580e33382427..8035f70a6e5ff 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -150,7 +150,7 @@ struct acpi_hotplug_context { */ typedef int (*acpi_op_add) (struct acpi_device * device); -typedef int (*acpi_op_remove) (struct acpi_device * device); +typedef void (*acpi_op_remove) (struct acpi_device *device); typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); struct acpi_device_ops { From 48ec39d52c85fc3f9fe6113e05589941df517bba Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 5 Dec 2022 19:56:14 +0100 Subject: [PATCH 142/227] ACPI: thermal: Adjust critical.flags.valid check It is not necessary to compare critical.flags.valid to 1 in acpi_thermal_trips_update() and doing so is also inconsistent with other similar checks in that code, so simply check if the flag is not 0 instead. No expected functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 642d498d448bb..496c226613ae1 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -287,7 +287,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) "Found critical threshold [%lu]\n", tz->trips.critical.temperature); } - if (tz->trips.critical.flags.valid == 1) { + if (tz->trips.critical.flags.valid) { if (crt == -1) { tz->trips.critical.flags.valid = 0; } else if (crt > 0) { From 72d3b9034e8c24b49f098d2c9efdafe44303cc0b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 14 Oct 2021 16:47:54 +0300 Subject: [PATCH 143/227] driver core: Provide device_match_acpi_handle() helper We have a couple of users of this helper, make it available for them. The prototype for the helper is specifically crafted in order to be easily used with bus_find_device() call. That's why its location is in the driver core rather than ACPI. Reviewed-by: Rafael J. Wysocki Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211014134756.39092-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 6 ++++++ include/linux/device/bus.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index c46a6947bb29e..4dc1457a15969 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4842,6 +4842,12 @@ int device_match_acpi_dev(struct device *dev, const void *adev) } EXPORT_SYMBOL(device_match_acpi_dev); +int device_match_acpi_handle(struct device *dev, const void *handle) +{ + return ACPI_HANDLE(dev) == handle; +} +EXPORT_SYMBOL(device_match_acpi_handle); + int device_match_any(struct device *dev, const void *unused) { return 1; diff --git a/include/linux/device/bus.h b/include/linux/device/bus.h index 6a4b257c270ef..a247fb545a2f4 100644 --- a/include/linux/device/bus.h +++ b/include/linux/device/bus.h @@ -143,6 +143,7 @@ int device_match_of_node(struct device *dev, const void *np); int device_match_fwnode(struct device *dev, const void *fwnode); int device_match_devt(struct device *dev, const void *pdevt); int device_match_acpi_dev(struct device *dev, const void *adev); +int device_match_acpi_handle(struct device *dev, const void *handle); int device_match_any(struct device *dev, const void *unused); /* iterator helpers for buses */ From 4ee34072d579dbfc14fc5fffa792c7db486679c5 Mon Sep 17 00:00:00 2001 From: Devyn Liu Date: Mon, 13 May 2024 15:59:01 +0800 Subject: [PATCH 144/227] gpiolib: acpi: Fix failed in acpi_gpiochip_find() by adding parent node match mainline inclusion from mainline-v6.11-rc1 commit adbc49a5a8c6fcf7be154c2e30213bbf472940da category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAQ1MS CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git/commit/drivers/gpio/gpiolib-acpi.c?h=spi-v6.11&id=adbc49a5a8c6fcf7be154c2e30213bbf472940da -------------------------------- Previous patch modified the standard used by acpi_gpiochip_find() to match device nodes. Using the device node set in gc->gpiodev->d- ev instead of gc->parent. However, there is a situation in gpio-dwapb where the GPIO device driver will set gc->fwnode for each port corresponding to a child node under a GPIO device, so gc->gpiodev->dev will be assigned the value of each child node in gpiochip_add_data(). gpio-dwapb.c: 128,31 static int dwapb_gpio_add_port(struct dwapb_gpio *gpio, struct dwapb_port_property *pp, unsigned int offs); port->gc.fwnode = pp->fwnode; 693,39 static int dwapb_gpio_probe; err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i); When other drivers request GPIO pin resources through the GPIO device node provided by ACPI (corresponding to the parent node), the change of the matching object to gc->gpiodev->dev in acpi_gpiochip_find() only allows finding the value of each port (child node), resulting in a failed request. Reapply the condition of using gc->parent for match in acpi_gpio- chip_find() in the code can compatible with the problem of gpio-dwapb, and will not affect the two cases mentioned in the patch: 1. There is no setting for gc->fwnode. 2. The case that depends on using gc->fwnode for match. Fixes: 5062e4c14b75 ("gpiolib: acpi: use the fwnode in acpi_gpiochip_find()") Fixes: 067dbc1ea5ce ("gpiolib: acpi: Don't use GPIO chip fwnode in acpi_gpiochip_find()") Signed-off-by:lujunhua Signed-off-by: Devyn Liu Reviewed-by: Mika Westerberg Tested-by: Benjamin Tissoires Signed-off-by: Andy Shevchenko --- drivers/gpio/gpiolib-acpi.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 27e3fb9938049..c477684ed54ab 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -95,10 +95,23 @@ static bool acpi_gpio_deferred_req_irqs_done; static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) { - if (!gc->parent) - return false; + /* First check the actual GPIO device */ + if (device_match_acpi_handle(&gc->gpiodev->dev, data)) + return true; - return ACPI_HANDLE(gc->parent) == data; + /* + * When the ACPI device is artificially split to the banks of GPIOs, + * where each of them is represented by a separate GPIO device, + * the firmware node of the physical device may not be shared among + * the banks as they may require different values for the same property, + * e.g., number of GPIOs in a certain bank. In such case the ACPI handle + * of a GPIO device is NULL and can not be used. Hence we have to check + * the parent device to be sure that there is no match before bailing + * out. + */ + if (gc->parent) + return device_match_acpi_handle(gc->parent, data); + return false; } /** From 31f7f90094ef129fcc8dbbab14893117445a953f Mon Sep 17 00:00:00 2001 From: hbmm Date: Thu, 3 Jul 2025 17:04:30 +0800 Subject: [PATCH 145/227] Revert "ACPI: thermal: Move to dedicated function sysfs extra attr creation" This reverts commit c7ffeb5fa1e015ead36cda8c1e998b9be4df3fa1. --- drivers/acpi/thermal.c | 45 ++++++++---------------------------------- 1 file changed, 8 insertions(+), 37 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 7e24554fe00f3..809e12b941235 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -783,32 +783,6 @@ static struct thermal_zone_device_ops acpi_thermal_zone_ops = { .critical = acpi_thermal_zone_device_critical, }; -static int acpi_thermal_zone_sysfs_add(struct acpi_thermal *tz) -{ - struct device *tzdev = thermal_zone_device(tz->thermal_zone); - int ret; - - ret = sysfs_create_link(&tz->device->dev.kobj, - &tzdev->kobj, "thermal_zone"); - if (ret) - return ret; - - ret = sysfs_create_link(&tzdev->kobj, - &tz->device->dev.kobj, "device"); - if (ret) - sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); - - return ret; -} - -static void acpi_thermal_zone_sysfs_remove(struct acpi_thermal *tz) -{ - struct device *tzdev = thermal_zone_device(tz->thermal_zone); - - sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); - sysfs_remove_link(&tzdev->kobj, "device"); -} - static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) { int trips = 0; @@ -842,15 +816,21 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (IS_ERR(tz->thermal_zone)) return -ENODEV; - result = acpi_thermal_zone_sysfs_add(tz); + result = sysfs_create_link(&tz->device->dev.kobj, + &tz->thermal_zone->device.kobj, "thermal_zone"); if (result) goto unregister_tzd; + result = sysfs_create_link(&tz->thermal_zone->device.kobj, + &tz->device->dev.kobj, "device"); + if (result) + goto remove_tz_link; + status = acpi_bus_attach_private_data(tz->device->handle, tz->thermal_zone); if (ACPI_FAILURE(status)) { result = -ENODEV; - goto remove_links; + goto remove_dev_link; } result = thermal_zone_device_enable(tz->thermal_zone); @@ -864,15 +844,10 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) acpi_bus_detach: acpi_bus_detach_private_data(tz->device->handle); -<<<<<<< HEAD remove_dev_link: sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); remove_tz_link: sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); -======= -remove_links: - acpi_thermal_zone_sysfs_remove(tz); ->>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) unregister_tzd: thermal_zone_device_unregister(tz->thermal_zone); @@ -881,12 +856,8 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) { -<<<<<<< HEAD sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); -======= - acpi_thermal_zone_sysfs_remove(tz); ->>>>>>> a4b81715a58e (ACPI: thermal: Move to dedicated function sysfs extra attr creation) thermal_zone_device_unregister(tz->thermal_zone); tz->thermal_zone = NULL; acpi_bus_detach_private_data(tz->device->handle); From bdaae6dd5ce85c0add1b071a4e6dc2b252494c86 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 3 Dec 2021 17:37:10 +0100 Subject: [PATCH 146/227] ACPI: Use acpi_fetch_acpi_dev() instead of acpi_bus_get_device() Modify the ACPI code to use acpi_fetch_acpi_dev() instead of acpi_bus_get_device() where applicable. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mika Westerberg Reviewed-by: Hans de Goede --- drivers/acpi/acpi_video.c | 5 ++--- drivers/acpi/device_pm.c | 31 +++++++++++++------------------ drivers/acpi/dock.c | 5 ++--- drivers/acpi/pci_link.c | 12 ++++-------- drivers/acpi/pci_root.c | 10 ++++------ drivers/acpi/power.c | 7 +++---- drivers/acpi/processor_driver.c | 10 +++++++--- drivers/acpi/processor_idle.c | 2 +- drivers/acpi/property.c | 11 +++++------ drivers/acpi/resource.c | 4 ++-- drivers/acpi/thermal.c | 9 ++++----- drivers/acpi/video_detect.c | 6 ++---- drivers/acpi/x86/s2idle.c | 4 ++-- include/acpi/acpi_bus.h | 1 + 14 files changed, 52 insertions(+), 65 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index c8eb69d3e1d61..e87f4bf82d8b0 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1765,13 +1765,12 @@ acpi_video_bus_match(acpi_handle handle, u32 level, void *context, { struct acpi_device *device = context; struct acpi_device *sibling; - int result; if (handle == device->handle) return AE_CTRL_TERMINATE; - result = acpi_bus_get_device(handle, &sibling); - if (result) + sibling = acpi_fetch_acpi_dev(handle); + if (!sibling) return AE_OK; if (!strcmp(acpi_device_name(sibling), ACPI_VIDEO_BUS_NAME)) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 0028b6b51c877..60a8d7a5ff03f 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -285,14 +285,12 @@ EXPORT_SYMBOL(acpi_device_set_power); int acpi_bus_set_power(acpi_handle handle, int state) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - if (result) - return result; + if (device) + return acpi_device_set_power(device, state); - return acpi_device_set_power(device, state); + return -ENODEV; } EXPORT_SYMBOL(acpi_bus_set_power); @@ -410,21 +408,20 @@ EXPORT_SYMBOL_GPL(acpi_device_update_power); int acpi_bus_update_power(acpi_handle handle, int *state_p) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? result : acpi_device_update_power(device, state_p); + if (device) + return acpi_device_update_power(device, state_p); + + return -ENODEV; } EXPORT_SYMBOL_GPL(acpi_bus_update_power); bool acpi_bus_power_manageable(acpi_handle handle) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? false : device->flags.power_manageable; + return device && device->flags.power_manageable; } EXPORT_SYMBOL(acpi_bus_power_manageable); @@ -543,11 +540,9 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev) bool acpi_bus_can_wakeup(acpi_handle handle) { - struct acpi_device *device; - int result; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - result = acpi_bus_get_device(handle, &device); - return result ? false : device->wakeup.flags.valid; + return device && device->wakeup.flags.valid; } EXPORT_SYMBOL(acpi_bus_can_wakeup); diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 7cf92158008fa..ece9109db0586 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -489,10 +489,9 @@ static ssize_t docked_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dock_station *dock_station = dev->platform_data; - struct acpi_device *adev = NULL; + struct acpi_device *adev = acpi_fetch_acpi_dev(dock_station->handle); - acpi_bus_get_device(dock_station->handle, &adev); - return snprintf(buf, PAGE_SIZE, "%u\n", acpi_device_enumerated(adev)); + return sysfs_emit(buf, "%u\n", acpi_device_enumerated(adev)); } static DEVICE_ATTR_RO(docked); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index cb7b900d9466c..d54fb8e54671d 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -606,12 +606,10 @@ static int acpi_pci_link_allocate(struct acpi_pci_link *link) int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name) { - int result; - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_link *link; - result = acpi_bus_get_device(handle, &device); - if (result) { + if (!device) { acpi_handle_err(handle, "Invalid link device\n"); return -1; } @@ -658,12 +656,10 @@ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, */ int acpi_pci_link_free_irq(acpi_handle handle) { - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_link *link; - acpi_status result; - result = acpi_bus_get_device(handle, &device); - if (result) { + if (!device) { acpi_handle_err(handle, "Invalid link device\n"); return -1; } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 223aa010dd8da..d85b619108dae 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -65,11 +65,10 @@ static struct acpi_scan_handler pci_root_handler = { */ int acpi_is_root_bridge(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); int ret; - struct acpi_device *device; - ret = acpi_bus_get_device(handle, &device); - if (ret) + if (!device) return 0; ret = acpi_match_device_ids(device, root_device_ids); @@ -226,11 +225,10 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags) struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_pci_root *root; - struct acpi_device *device; - if (acpi_bus_get_device(handle, &device) || - acpi_match_device_ids(device, root_device_ids)) + if (!device || acpi_match_device_ids(device, root_device_ids)) return NULL; root = acpi_driver_data(device); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index c95eedd58f5bf..fe22366b4b385 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -81,9 +81,9 @@ struct acpi_power_resource *to_power_resource(struct acpi_device *device) static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) { - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - if (acpi_bus_get_device(handle, &device)) + if (!device) return NULL; return to_power_resource(device); @@ -914,14 +914,13 @@ static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource struct acpi_device *acpi_add_power_resource(acpi_handle handle) { + struct acpi_device *device = acpi_fetch_acpi_dev(handle); struct acpi_power_resource *resource; - struct acpi_device *device = NULL; union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; acpi_status status; int result; - acpi_bus_get_device(handle, &device); if (device) return device; diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c index 77541f939be3e..368a9edefd0cb 100644 --- a/drivers/acpi/processor_driver.c +++ b/drivers/acpi/processor_driver.c @@ -98,8 +98,13 @@ static int acpi_soft_cpu_online(unsigned int cpu) struct acpi_processor *pr = per_cpu(processors, cpu); struct acpi_device *device; - if (!pr || acpi_bus_get_device(pr->handle, &device)) + if (!pr) + return 0; + + device = acpi_fetch_acpi_dev(pr->handle); + if (!device) return 0; + /* * CPU got physically hotplugged and onlined for the first time: * Initialize missing things. @@ -125,9 +130,8 @@ static int acpi_soft_cpu_online(unsigned int cpu) static int acpi_soft_cpu_dead(unsigned int cpu) { struct acpi_processor *pr = per_cpu(processors, cpu); - struct acpi_device *device; - if (!pr || acpi_bus_get_device(pr->handle, &device)) + if (!pr || !acpi_fetch_acpi_dev(pr->handle)) return 0; acpi_processor_reevaluate_tstate(pr, true); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 5fbb435b58122..756ffc9d10b7c 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -1132,7 +1132,7 @@ static int acpi_processor_get_lpi_info(struct acpi_processor *pr) status = acpi_get_parent(handle, &pr_ahandle); while (ACPI_SUCCESS(status)) { - acpi_bus_get_device(pr_ahandle, &d); + d = acpi_fetch_acpi_dev(pr_ahandle); handle = pr_ahandle; if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 9ab7f7184343a..617e52dc415d7 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -694,9 +694,9 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (index) return -ENOENT; - ret = acpi_bus_get_device(obj->reference.handle, &device); - if (ret) - return ret == -ENODEV ? -EINVAL : ret; + device = acpi_fetch_acpi_dev(obj->reference.handle); + if (!device) + return -EINVAL; if (!args) return 0; @@ -729,9 +729,8 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, if (element->type == ACPI_TYPE_LOCAL_REFERENCE) { struct fwnode_handle *ref_fwnode; - ret = acpi_bus_get_device(element->reference.handle, - &device); - if (ret) + device = acpi_fetch_acpi_dev(element->reference.handle); + if (!device) return -EINVAL; nargs = 0; diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 57f34aa4f4295..27ea1317e33a6 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c @@ -982,9 +982,9 @@ static acpi_status acpi_res_consumer_cb(acpi_handle handle, u32 depth, { struct resource *res = context; struct acpi_device **consumer = (struct acpi_device **) ret; - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); - if (acpi_bus_get_device(handle, &adev)) + if (!adev) return AE_OK; if (acpi_dev_consumes_res(adev, res)) { diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 809e12b941235..5482e28c5bc68 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -693,7 +693,6 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; struct acpi_device *dev; - acpi_status status; acpi_handle handle; int i; int j; @@ -711,8 +710,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, for (i = 0; i < tz->trips.passive.devices.count; i++) { handle = tz->trips.passive.devices.handles[i]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_FAILURE(status) || dev != device) + dev = acpi_fetch_acpi_dev(handle); + if (dev != device) continue; if (bind) result = @@ -737,8 +736,8 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, j < tz->trips.active[i].devices.count; j++) { handle = tz->trips.active[i].devices.handles[j]; - status = acpi_bus_get_device(handle, &dev); - if (ACPI_FAILURE(status) || dev != device) + dev = acpi_fetch_acpi_dev(handle); + if (dev != device) continue; if (bind) result = thermal_zone_bind_cooling_device diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index a5cb9e1d48bcc..d55c143a49d7c 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -59,18 +59,16 @@ static void acpi_video_parse_cmdline(void) static acpi_status find_video(acpi_handle handle, u32 lvl, void *context, void **rv) { + struct acpi_device *acpi_dev = acpi_fetch_acpi_dev(handle); long *cap = context; struct pci_dev *dev; - struct acpi_device *acpi_dev; static const struct acpi_device_id video_ids[] = { {ACPI_VIDEO_HID, 0}, {"", 0}, }; - if (acpi_bus_get_device(handle, &acpi_dev)) - return AE_OK; - if (!acpi_match_device_ids(acpi_dev, video_ids)) { + if (acpi_dev && !acpi_match_device_ids(acpi_dev, video_ids)) { dev = acpi_get_pci_dev(handle); if (!dev) return AE_OK; diff --git a/drivers/acpi/x86/s2idle.c b/drivers/acpi/x86/s2idle.c index ada989670d9b8..ee3deea964fd4 100644 --- a/drivers/acpi/x86/s2idle.c +++ b/drivers/acpi/x86/s2idle.c @@ -294,9 +294,9 @@ static void lpi_check_constraints(void) for (i = 0; i < lpi_constraints_table_size; ++i) { acpi_handle handle = lpi_constraints_table[i].handle; - struct acpi_device *adev; + struct acpi_device *adev = acpi_fetch_acpi_dev(handle); - if (!handle || acpi_bus_get_device(handle, &adev)) + if (!adev) continue; acpi_handle_debug(handle, diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a87a6d557e467..8580e33382427 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -727,6 +727,7 @@ static inline void acpi_dev_put(struct acpi_device *adev) } struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle); +struct acpi_device *acpi_fetch_acpi_dev(acpi_handle handle); static inline void acpi_bus_put_acpi_device(struct acpi_device *adev) { From aa3407a03442d02d2ae95244c968839d6c5d7a9a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 4 Oct 2022 18:31:11 +0200 Subject: [PATCH 147/227] ACPI: thermal: Use white space more consistently The usage of white space in the ACPI thermal driver is not very consistent, so improve that a bit. While at it, add missing braces to if()/else in a few places. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano --- drivers/acpi/thermal.c | 190 ++++++++++++++++++++--------------------- 1 file changed, 92 insertions(+), 98 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5482e28c5bc68..852a413d230d5 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -154,7 +154,7 @@ struct acpi_thermal_flags { }; struct acpi_thermal { - struct acpi_device * device; + struct acpi_device *device; acpi_bus_id name; unsigned long temperature; unsigned long last_temperature; @@ -266,8 +266,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* Critical Shutdown */ if (flag & ACPI_TRIPS_CRITICAL) { - status = acpi_evaluate_integer(tz->device->handle, - "_CRT", NULL, &tmp); + status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, &tmp); tz->trips.critical.temperature = tmp; /* * Treat freezing temperatures as invalid as well; some @@ -280,8 +279,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) acpi_handle_debug(tz->device->handle, "No critical threshold\n"); } else if (tmp <= 2732) { - pr_info(FW_BUG "Invalid critical threshold (%llu)\n", - tmp); + pr_info(FW_BUG "Invalid critical threshold (%llu)\n", tmp); tz->trips.critical.flags.valid = 0; } else { tz->trips.critical.flags.valid = 1; @@ -308,8 +306,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* Critical Sleep (optional) */ if (flag & ACPI_TRIPS_HOT) { - status = acpi_evaluate_integer(tz->device->handle, - "_HOT", NULL, &tmp); + status = acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, &tmp); if (ACPI_FAILURE(status)) { tz->trips.hot.flags.valid = 0; acpi_handle_debug(tz->device->handle, @@ -325,7 +322,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* Passive (optional) */ if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) || - (flag == ACPI_TRIPS_INIT)) { + (flag == ACPI_TRIPS_INIT)) { valid = tz->trips.passive.flags.valid; if (psv == -1) { status = AE_SUPPORT; @@ -334,32 +331,31 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) status = AE_OK; } else { status = acpi_evaluate_integer(tz->device->handle, - "_PSV", NULL, &tmp); + "_PSV", NULL, &tmp); } - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { tz->trips.passive.flags.valid = 0; - else { + } else { tz->trips.passive.temperature = tmp; tz->trips.passive.flags.valid = 1; if (flag == ACPI_TRIPS_INIT) { - status = acpi_evaluate_integer( - tz->device->handle, "_TC1", - NULL, &tmp); + status = acpi_evaluate_integer(tz->device->handle, + "_TC1", NULL, &tmp); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; else tz->trips.passive.tc1 = tmp; - status = acpi_evaluate_integer( - tz->device->handle, "_TC2", - NULL, &tmp); + + status = acpi_evaluate_integer(tz->device->handle, + "_TC2", NULL, &tmp); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; else tz->trips.passive.tc2 = tmp; - status = acpi_evaluate_integer( - tz->device->handle, "_TSP", - NULL, &tmp); + + status = acpi_evaluate_integer(tz->device->handle, + "_TSP", NULL, &tmp); if (ACPI_FAILURE(status)) tz->trips.passive.flags.valid = 0; else @@ -370,25 +366,25 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) { memset(&devices, 0, sizeof(struct acpi_handle_list)); status = acpi_evaluate_reference(tz->device->handle, "_PSL", - NULL, &devices); + NULL, &devices); if (ACPI_FAILURE(status)) { acpi_handle_info(tz->device->handle, "Invalid passive threshold\n"); tz->trips.passive.flags.valid = 0; - } - else + } else { tz->trips.passive.flags.valid = 1; + } if (memcmp(&tz->trips.passive.devices, &devices, - sizeof(struct acpi_handle_list))) { + sizeof(struct acpi_handle_list))) { memcpy(&tz->trips.passive.devices, &devices, - sizeof(struct acpi_handle_list)); + sizeof(struct acpi_handle_list)); ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device"); } } if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) { if (valid != tz->trips.passive.flags.valid) - ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "state"); + ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "state"); } /* Active (optional) */ @@ -400,28 +396,30 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) break; /* disable all active trip points */ if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) && - tz->trips.active[i].flags.valid)) { + tz->trips.active[i].flags.valid)) { status = acpi_evaluate_integer(tz->device->handle, - name, NULL, &tmp); + name, NULL, &tmp); if (ACPI_FAILURE(status)) { tz->trips.active[i].flags.valid = 0; if (i == 0) break; + if (act <= 0) break; + if (i == 1) - tz->trips.active[0].temperature = - celsius_to_deci_kelvin(act); + tz->trips.active[0].temperature = celsius_to_deci_kelvin(act); else /* * Don't allow override higher than * the next higher trip point */ - tz->trips.active[i - 1].temperature = - (tz->trips.active[i - 2].temperature < + tz->trips.active[i-1].temperature = + (tz->trips.active[i-2].temperature < celsius_to_deci_kelvin(act) ? - tz->trips.active[i - 2].temperature : + tz->trips.active[i-2].temperature : celsius_to_deci_kelvin(act)); + break; } else { tz->trips.active[i].temperature = tmp; @@ -430,22 +428,22 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) } name[2] = 'L'; - if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) { + if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid) { memset(&devices, 0, sizeof(struct acpi_handle_list)); status = acpi_evaluate_reference(tz->device->handle, - name, NULL, &devices); + name, NULL, &devices); if (ACPI_FAILURE(status)) { acpi_handle_info(tz->device->handle, "Invalid active%d threshold\n", i); tz->trips.active[i].flags.valid = 0; - } - else + } else { tz->trips.active[i].flags.valid = 1; + } if (memcmp(&tz->trips.active[i].devices, &devices, - sizeof(struct acpi_handle_list))) { + sizeof(struct acpi_handle_list))) { memcpy(&tz->trips.active[i].devices, &devices, - sizeof(struct acpi_handle_list)); + sizeof(struct acpi_handle_list)); ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device"); } } @@ -460,9 +458,9 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) if (flag & ACPI_TRIPS_DEVICES) { memset(&devices, 0, sizeof(devices)); status = acpi_evaluate_reference(tz->device->handle, "_TZD", - NULL, &devices); - if (ACPI_SUCCESS(status) - && memcmp(&tz->devices, &devices, sizeof(devices))) { + NULL, &devices); + if (ACPI_SUCCESS(status) && + memcmp(&tz->devices, &devices, sizeof(devices))) { tz->devices = devices; ACPI_THERMAL_TRIPS_EXCEPTION(flag, tz, "device"); } @@ -544,8 +542,7 @@ static int thermal_get_trip_type(struct thermal_zone_device *thermal, trip--; } - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && - tz->trips.active[i].flags.valid; i++) { + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid; i++) { if (!trip) { *type = THERMAL_TRIP_ACTIVE; return 0; @@ -568,8 +565,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.critical.flags.valid) { if (!trip) { *temp = deci_kelvin_to_millicelsius_with_offset( - tz->trips.critical.temperature, - tz->kelvin_offset); + tz->trips.critical.temperature, + tz->kelvin_offset); return 0; } trip--; @@ -578,8 +575,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.hot.flags.valid) { if (!trip) { *temp = deci_kelvin_to_millicelsius_with_offset( - tz->trips.hot.temperature, - tz->kelvin_offset); + tz->trips.hot.temperature, + tz->kelvin_offset); return 0; } trip--; @@ -588,8 +585,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, if (tz->trips.passive.flags.valid) { if (!trip) { *temp = deci_kelvin_to_millicelsius_with_offset( - tz->trips.passive.temperature, - tz->kelvin_offset); + tz->trips.passive.temperature, + tz->kelvin_offset); return 0; } trip--; @@ -599,8 +596,8 @@ static int thermal_get_trip_temp(struct thermal_zone_device *thermal, tz->trips.active[i].flags.valid; i++) { if (!trip) { *temp = deci_kelvin_to_millicelsius_with_offset( - tz->trips.active[i].temperature, - tz->kelvin_offset); + tz->trips.active[i].temperature, + tz->kelvin_offset); return 0; } trip--; @@ -616,15 +613,15 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, if (tz->trips.critical.flags.valid) { *temperature = deci_kelvin_to_millicelsius_with_offset( - tz->trips.critical.temperature, - tz->kelvin_offset); + tz->trips.critical.temperature, + tz->kelvin_offset); return 0; } else return -EINVAL; } static int thermal_get_trend(struct thermal_zone_device *thermal, - int trip, enum thermal_trend *trend) + int trip, enum thermal_trend *trend) { struct acpi_thermal *tz = thermal->devdata; enum thermal_trip_type type; @@ -653,9 +650,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, * tz->temperature has already been updated by generic thermal layer, * before this callback being invoked */ - i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) - + (tz->trips.passive.tc2 - * (tz->temperature - tz->trips.passive.temperature)); + i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) + + (tz->trips.passive.tc2 * (tz->temperature - tz->trips.passive.temperature)); if (i > 0) *trend = THERMAL_TREND_RAISING; @@ -663,6 +659,7 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, *trend = THERMAL_TREND_DROPPING; else *trend = THERMAL_TREND_STABLE; + return 0; } @@ -687,8 +684,8 @@ static void acpi_thermal_zone_device_critical(struct thermal_zone_device *therma } static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev, - bool bind) + struct thermal_cooling_device *cdev, + bool bind) { struct acpi_device *device = cdev->devdata; struct acpi_thermal *tz = thermal->devdata; @@ -707,22 +704,23 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, if (tz->trips.passive.flags.valid) { trip++; - for (i = 0; i < tz->trips.passive.devices.count; - i++) { + for (i = 0; i < tz->trips.passive.devices.count; i++) { handle = tz->trips.passive.devices.handles[i]; dev = acpi_fetch_acpi_dev(handle); if (dev != device) continue; + if (bind) - result = - thermal_zone_bind_cooling_device - (thermal, trip, cdev, - THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); + result = thermal_zone_bind_cooling_device( + thermal, trip, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT, + THERMAL_WEIGHT_DEFAULT); else result = - thermal_zone_unbind_cooling_device - (thermal, trip, cdev); + thermal_zone_unbind_cooling_device( + thermal, trip, cdev); + if (result) goto failed; } @@ -731,22 +729,24 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { if (!tz->trips.active[i].flags.valid) break; + trip++; - for (j = 0; - j < tz->trips.active[i].devices.count; - j++) { + for (j = 0; j < tz->trips.active[i].devices.count; j++) { handle = tz->trips.active[i].devices.handles[j]; dev = acpi_fetch_acpi_dev(handle); if (dev != device) continue; + if (bind) - result = thermal_zone_bind_cooling_device - (thermal, trip, cdev, - THERMAL_NO_LIMIT, THERMAL_NO_LIMIT, - THERMAL_WEIGHT_DEFAULT); + result = thermal_zone_bind_cooling_device( + thermal, trip, cdev, + THERMAL_NO_LIMIT, + THERMAL_NO_LIMIT, + THERMAL_WEIGHT_DEFAULT); else - result = thermal_zone_unbind_cooling_device - (thermal, trip, cdev); + result = thermal_zone_unbind_cooling_device( + thermal, trip, cdev); + if (result) goto failed; } @@ -758,14 +758,14 @@ static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, static int acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) + struct thermal_cooling_device *cdev) { return acpi_thermal_cooling_device_cb(thermal, cdev, true); } static int acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, - struct thermal_cooling_device *cdev) + struct thermal_cooling_device *cdev) { return acpi_thermal_cooling_device_cb(thermal, cdev, false); } @@ -798,20 +798,20 @@ static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) if (tz->trips.passive.flags.valid) trips++; - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && - tz->trips.active[i].flags.valid; i++, trips++); + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && tz->trips.active[i].flags.valid; + i++, trips++); if (tz->trips.passive.flags.valid) - tz->thermal_zone = - thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, NULL, - tz->trips.passive.tsp*100, - tz->polling_frequency*100); + tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, + &acpi_thermal_zone_ops, NULL, + tz->trips.passive.tsp * 100, + tz->polling_frequency * 100); else tz->thermal_zone = thermal_zone_device_register("acpitz", trips, 0, tz, - &acpi_thermal_zone_ops, NULL, - 0, tz->polling_frequency*100); + &acpi_thermal_zone_ops, NULL, + 0, tz->polling_frequency * 100); + if (IS_ERR(tz->thermal_zone)) return -ENODEV; @@ -877,7 +877,6 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event) { struct acpi_thermal *tz = acpi_driver_data(device); - if (!tz) return; @@ -889,13 +888,13 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event) acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS); acpi_queue_thermal_check(tz); acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, 0); + dev_name(&device->dev), event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); acpi_queue_thermal_check(tz); acpi_bus_generate_netlink_event(device->pnp.device_class, - dev_name(&device->dev), event, 0); + dev_name(&device->dev), event, 0); break; default: acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n", @@ -940,7 +939,6 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz) { int result = 0; - if (!tz) return -EINVAL; @@ -1019,7 +1017,6 @@ static int acpi_thermal_add(struct acpi_device *device) int result = 0; struct acpi_thermal *tz = NULL; - if (!device) return -EINVAL; @@ -1095,6 +1092,7 @@ static int acpi_thermal_resume(struct device *dev) for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { if (!tz->trips.active[i].flags.valid) break; + tz->trips.active[i].flags.enabled = 1; for (j = 0; j < tz->trips.active[i].devices.count; j++) { result = acpi_bus_update_power( @@ -1115,7 +1113,6 @@ static int acpi_thermal_resume(struct device *dev) #endif static int thermal_act(const struct dmi_system_id *d) { - if (act == 0) { pr_notice("%s detected: disabling all active thermal trip points\n", d->ident); @@ -1124,14 +1121,12 @@ static int thermal_act(const struct dmi_system_id *d) { return 0; } static int thermal_nocrt(const struct dmi_system_id *d) { - pr_notice("%s detected: disabling all critical thermal trip point actions.\n", d->ident); crt = -1; return 0; } static int thermal_tzp(const struct dmi_system_id *d) { - if (tzp == 0) { pr_notice("%s detected: enabling thermal zone polling\n", d->ident); @@ -1140,7 +1135,6 @@ static int thermal_tzp(const struct dmi_system_id *d) { return 0; } static int thermal_psv(const struct dmi_system_id *d) { - if (psv == 0) { pr_notice("%s detected: disabling all passive thermal trip points\n", d->ident); From f85249746756177886ac27ae15c5e698d33ee935 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 4 Oct 2022 18:32:08 +0200 Subject: [PATCH 148/227] ACPI: thermal: Drop redundant parens from expressions Some expressions in the ACPI thermal driver contain redundant parentheses. Drop them. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano --- drivers/acpi/thermal.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 852a413d230d5..5d084360180f0 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -322,7 +322,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* Passive (optional) */ if (((flag & ACPI_TRIPS_PASSIVE) && tz->trips.passive.flags.valid) || - (flag == ACPI_TRIPS_INIT)) { + flag == ACPI_TRIPS_INIT) { valid = tz->trips.passive.flags.valid; if (psv == -1) { status = AE_SUPPORT; @@ -395,7 +395,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) if (act == -1) break; /* disable all active trip points */ - if ((flag == ACPI_TRIPS_INIT) || ((flag & ACPI_TRIPS_ACTIVE) && + if (flag == ACPI_TRIPS_INIT || ((flag & ACPI_TRIPS_ACTIVE) && tz->trips.active[i].flags.valid)) { status = acpi_evaluate_integer(tz->device->handle, name, NULL, &tmp); @@ -650,8 +650,8 @@ static int thermal_get_trend(struct thermal_zone_device *thermal, * tz->temperature has already been updated by generic thermal layer, * before this callback being invoked */ - i = (tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature)) + - (tz->trips.passive.tc2 * (tz->temperature - tz->trips.passive.temperature)); + i = tz->trips.passive.tc1 * (tz->temperature - tz->last_temperature) + + tz->trips.passive.tc2 * (tz->temperature - tz->trips.passive.temperature); if (i > 0) *trend = THERMAL_TREND_RAISING; From 5f0ea0571180274a62c426a1a2e1c83dd71eb0f1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 4 Oct 2022 18:32:52 +0200 Subject: [PATCH 149/227] ACPI: thermal: Drop some redundant code Drop some redundant initialization of local variables, a redundant return statement and a redundant "else" from the ACPI thermal driver. No functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Daniel Lezcano --- drivers/acpi/thermal.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5d084360180f0..40ecb55b52f8c 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -258,7 +258,7 @@ do { \ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) { - acpi_status status = AE_OK; + acpi_status status; unsigned long long tmp; struct acpi_handle_list devices; int valid = 0; @@ -616,8 +616,9 @@ static int thermal_get_crit_temp(struct thermal_zone_device *thermal, tz->trips.critical.temperature, tz->kelvin_offset); return 0; - } else - return -EINVAL; + } + + return -EINVAL; } static int thermal_get_trend(struct thermal_zone_device *thermal, @@ -937,7 +938,7 @@ static void acpi_thermal_aml_dependency_fix(struct acpi_thermal *tz) static int acpi_thermal_get_info(struct acpi_thermal *tz) { - int result = 0; + int result; if (!tz) return -EINVAL; @@ -1014,8 +1015,8 @@ static void acpi_thermal_check_fn(struct work_struct *work) static int acpi_thermal_add(struct acpi_device *device) { - int result = 0; - struct acpi_thermal *tz = NULL; + struct acpi_thermal *tz; + int result; if (!device) return -EINVAL; @@ -1056,7 +1057,7 @@ static int acpi_thermal_add(struct acpi_device *device) static int acpi_thermal_remove(struct acpi_device *device) { - struct acpi_thermal *tz = NULL; + struct acpi_thermal *tz; if (!device || !acpi_driver_data(device)) return -EINVAL; @@ -1185,7 +1186,7 @@ static const struct dmi_system_id thermal_dmi_table[] __initconst = { static int __init acpi_thermal_init(void) { - int result = 0; + int result; dmi_check_system(thermal_dmi_table); @@ -1212,8 +1213,6 @@ static void __exit acpi_thermal_exit(void) { acpi_bus_unregister_driver(&acpi_thermal_driver); destroy_workqueue(acpi_thermal_pm_queue); - - return; } module_init(acpi_thermal_init); From 135fccb8ce5d5483e450735330ae0950086912f6 Mon Sep 17 00:00:00 2001 From: Dawei Li Date: Mon, 14 Nov 2022 00:26:09 +0800 Subject: [PATCH 150/227] ACPI: make remove callback of ACPI driver void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For bus-based driver, device removal is implemented as: 1 device_remove()-> 2 bus->remove()-> 3 driver->remove() Driver core needs no inform from callee(bus driver) about the result of remove callback. In that case, commit fc7a6209d571 ("bus: Make remove callback return void") forces bus_type::remove be void-returned. Now we have the situation that both 1 & 2 of calling chain are void-returned, so it does not make much sense for 3(driver->remove) to return non-void to its caller. So the basic idea behind this change is making remove() callback of any bus-based driver to be void-returned. This change, for itself, is for device drivers based on acpi-bus. Acked-by: Uwe Kleine-König Acked-by: Lee Jones Acked-by: Dmitry Torokhov Reviewed-by: Hans de Goede Signed-off-by: Dawei Li Reviewed-by: Maximilian Luz # for drivers/platform/surface/* Signed-off-by: Rafael J. Wysocki --- arch/ia64/hp/common/aml_nfw.c | 4 +- arch/x86/platform/olpc/olpc-xo15-sci.c | 3 +- drivers/acpi/ac.c | 8 +- drivers/acpi/acpi_pad.c | 3 +- drivers/acpi/acpi_video.c | 8 +- drivers/acpi/battery.c | 5 +- drivers/acpi/button.c | 5 +- drivers/acpi/ec.c | 5 +- drivers/acpi/hed.c | 3 +- drivers/acpi/nfit/core.c | 3 +- drivers/acpi/sbs.c | 9 +- drivers/acpi/sbshc.c | 7 +- drivers/acpi/thermal.c | 7 +- drivers/acpi/tiny-power-button.c | 10 +- drivers/char/sonypi.c | 3 +- drivers/char/tpm/tpm_crb.c | 4 +- drivers/hv/vmbus_drv.c | 4 +- drivers/hwmon/acpi_power_meter.c | 5 +- drivers/hwmon/asus_atk0110.c | 6 +- drivers/input/misc/atlas_btns.c | 4 +- drivers/net/fjes/fjes_main.c | 4 +- .../platform/chrome/chromeos_privacy_screen.c | 152 ++++++++++++++++++ drivers/platform/chrome/wilco_ec/event.c | 4 +- drivers/platform/surface/surfacepro3_button.c | 3 +- drivers/platform/x86/asus-laptop.c | 3 +- drivers/platform/x86/asus-wireless.c | 3 +- drivers/platform/x86/classmate-laptop.c | 20 ++- drivers/platform/x86/dell/dell-rbtn.c | 6 +- drivers/platform/x86/eeepc-laptop.c | 3 +- drivers/platform/x86/fujitsu-laptop.c | 4 +- drivers/platform/x86/fujitsu-tablet.c | 3 +- drivers/platform/x86/intel/rst.c | 4 +- drivers/platform/x86/lg-laptop.c | 4 +- drivers/platform/x86/panasonic-laptop.c | 8 +- drivers/platform/x86/sony-laptop.c | 9 +- drivers/platform/x86/system76_acpi.c | 4 +- drivers/platform/x86/topstar-laptop.c | 3 +- drivers/platform/x86/toshiba_acpi.c | 4 +- drivers/platform/x86/toshiba_bluetooth.c | 6 +- drivers/platform/x86/toshiba_haps.c | 4 +- drivers/platform/x86/wireless-hotkey.c | 3 +- drivers/platform/x86/xo15-ebook.c | 3 +- drivers/ptp/ptp_vmw.c | 3 +- drivers/thermal/intel/intel_menlow.c | 8 +- drivers/video/backlight/apple_bl.c | 3 +- drivers/watchdog/ni903x_wdt.c | 4 +- drivers/xen/xen-acpi-pad.c | 3 +- include/acpi/acpi_bus.h | 2 +- 48 files changed, 239 insertions(+), 147 deletions(-) create mode 100644 drivers/platform/chrome/chromeos_privacy_screen.c diff --git a/arch/ia64/hp/common/aml_nfw.c b/arch/ia64/hp/common/aml_nfw.c index 684667ade5259..901df49461a05 100644 --- a/arch/ia64/hp/common/aml_nfw.c +++ b/arch/ia64/hp/common/aml_nfw.c @@ -187,9 +187,9 @@ static int aml_nfw_add(struct acpi_device *device) return aml_nfw_add_global_handler(); } -static int aml_nfw_remove(struct acpi_device *device) +static void aml_nfw_remove(struct acpi_device *device) { - return aml_nfw_remove_global_handler(); + aml_nfw_remove_global_handler(); } static const struct acpi_device_id aml_nfw_ids[] = { diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 994a229cb79fe..68244a3422d1d 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -183,13 +183,12 @@ static int xo15_sci_add(struct acpi_device *device) return r; } -static int xo15_sci_remove(struct acpi_device *device) +static void xo15_sci_remove(struct acpi_device *device) { acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); cancel_work_sync(&sci_work); sysfs_remove_file(&device->dev.kobj, &lid_wake_on_close_attr.attr); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 81aff651a0d49..2deb903917467 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -34,7 +34,7 @@ MODULE_LICENSE("GPL"); static int acpi_ac_add(struct acpi_device *device); -static int acpi_ac_remove(struct acpi_device *device); +static void acpi_ac_remove(struct acpi_device *device); static void acpi_ac_notify(struct acpi_device *device, u32 event); struct acpi_ac_bl { @@ -321,13 +321,13 @@ static int acpi_ac_resume(struct device *dev) #define acpi_ac_resume NULL #endif -static int acpi_ac_remove(struct acpi_device *device) +static void acpi_ac_remove(struct acpi_device *device) { struct acpi_ac *ac = NULL; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; ac = acpi_driver_data(device); @@ -335,8 +335,6 @@ static int acpi_ac_remove(struct acpi_device *device) unregister_acpi_notifier(&ac->battery_nb); kfree(ac); - - return 0; } static int __init acpi_ac_init(void) diff --git a/drivers/acpi/acpi_pad.c b/drivers/acpi/acpi_pad.c index ec0e22a1e25d6..edbb28faee2a6 100644 --- a/drivers/acpi/acpi_pad.c +++ b/drivers/acpi/acpi_pad.c @@ -449,7 +449,7 @@ static int acpi_pad_add(struct acpi_device *device) return 0; } -static int acpi_pad_remove(struct acpi_device *device) +static void acpi_pad_remove(struct acpi_device *device) { mutex_lock(&isolated_cpus_lock); acpi_pad_idle_cpus(0); @@ -458,7 +458,6 @@ static int acpi_pad_remove(struct acpi_device *device) acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_pad_notify); acpi_pad_remove_sysfs(device); - return 0; } static const struct acpi_device_id pad_device_ids[] = { diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index e87f4bf82d8b0..ba938b9a43921 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -79,7 +79,7 @@ static DEFINE_MUTEX(register_count_mutex); static DEFINE_MUTEX(video_list_lock); static LIST_HEAD(video_bus_head); static int acpi_video_bus_add(struct acpi_device *device); -static int acpi_video_bus_remove(struct acpi_device *device); +static void acpi_video_bus_remove(struct acpi_device *device); static void acpi_video_bus_notify(struct acpi_device *device, u32 event); void acpi_video_detect_exit(void); @@ -2136,13 +2136,13 @@ static int acpi_video_bus_add(struct acpi_device *device) return error; } -static int acpi_video_bus_remove(struct acpi_device *device) +static void acpi_video_bus_remove(struct acpi_device *device) { struct acpi_video_bus *video = NULL; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; video = acpi_driver_data(device); @@ -2156,8 +2156,6 @@ static int acpi_video_bus_remove(struct acpi_device *device) kfree(video->attached_array); kfree(video); - - return 0; } static int __init is_i740(struct pci_dev *dev) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index c7569151fd02a..dae63a9f80985 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -1259,12 +1259,12 @@ static int acpi_battery_add(struct acpi_device *device) return result; } -static int acpi_battery_remove(struct acpi_device *device) +static void acpi_battery_remove(struct acpi_device *device) { struct acpi_battery *battery = NULL; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; device_init_wakeup(&device->dev, 0); battery = acpi_driver_data(device); unregister_pm_notifier(&battery->pm_nb); @@ -1272,7 +1272,6 @@ static int acpi_battery_remove(struct acpi_device *device) mutex_destroy(&battery->lock); mutex_destroy(&battery->sysfs_lock); kfree(battery); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index d8b1481141385..ef77c14c72a92 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -134,7 +134,7 @@ static const struct dmi_system_id dmi_lid_quirks[] = { }; static int acpi_button_add(struct acpi_device *device); -static int acpi_button_remove(struct acpi_device *device); +static void acpi_button_remove(struct acpi_device *device); static void acpi_button_notify(struct acpi_device *device, u32 event); #ifdef CONFIG_PM_SLEEP @@ -589,14 +589,13 @@ static int acpi_button_add(struct acpi_device *device) return error; } -static int acpi_button_remove(struct acpi_device *device) +static void acpi_button_remove(struct acpi_device *device) { struct acpi_button *button = acpi_driver_data(device); acpi_button_remove_fs(device); input_unregister_device(button->input); kfree(button); - return 0; } static int param_set_lid_init_state(const char *val, diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 472418a0e0cab..93cbb10b42629 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1650,12 +1650,12 @@ static int acpi_ec_add(struct acpi_device *device) return ret; } -static int acpi_ec_remove(struct acpi_device *device) +static void acpi_ec_remove(struct acpi_device *device) { struct acpi_ec *ec; if (!device) - return -EINVAL; + return; ec = acpi_driver_data(device); release_region(ec->data_addr, 1); @@ -1665,7 +1665,6 @@ static int acpi_ec_remove(struct acpi_device *device) ec_remove_handlers(ec); acpi_ec_free(ec); } - return 0; } static acpi_status diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c index 60a2939cde6c5..78d44e3fe1295 100644 --- a/drivers/acpi/hed.c +++ b/drivers/acpi/hed.c @@ -56,10 +56,9 @@ static int acpi_hed_add(struct acpi_device *device) return 0; } -static int acpi_hed_remove(struct acpi_device *device) +static void acpi_hed_remove(struct acpi_device *device) { hed_handle = NULL; - return 0; } static struct acpi_driver acpi_hed_driver = { diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 9cc83df22de49..88b8656c3956d 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -3755,10 +3755,9 @@ static int acpi_nfit_add(struct acpi_device *adev) return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc); } -static int acpi_nfit_remove(struct acpi_device *adev) +static void acpi_nfit_remove(struct acpi_device *adev) { /* see acpi_nfit_unregister */ - return 0; } static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle) diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index 4938010fcac78..811f9eb7292ac 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -96,7 +96,7 @@ struct acpi_sbs { #define to_acpi_sbs(x) power_supply_get_drvdata(x) -static int acpi_sbs_remove(struct acpi_device *device); +static void acpi_sbs_remove(struct acpi_device *device); static int acpi_battery_get_state(struct acpi_battery *battery); static inline int battery_scale(int log) @@ -664,16 +664,16 @@ static int acpi_sbs_add(struct acpi_device *device) return result; } -static int acpi_sbs_remove(struct acpi_device *device) +static void acpi_sbs_remove(struct acpi_device *device) { struct acpi_sbs *sbs; int id; if (!device) - return -EINVAL; + return; sbs = acpi_driver_data(device); if (!sbs) - return -EINVAL; + return; mutex_lock(&sbs->lock); acpi_smbus_unregister_callback(sbs->hc); for (id = 0; id < MAX_SBS_BAT; ++id) @@ -682,7 +682,6 @@ static int acpi_sbs_remove(struct acpi_device *device) mutex_unlock(&sbs->lock); mutex_destroy(&sbs->lock); kfree(sbs); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index 7c62e149a7a16..3fd1cc0dadb87 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -30,7 +30,7 @@ struct acpi_smb_hc { }; static int acpi_smbus_hc_add(struct acpi_device *device); -static int acpi_smbus_hc_remove(struct acpi_device *device); +static void acpi_smbus_hc_remove(struct acpi_device *device); static const struct acpi_device_id sbs_device_ids[] = { {"ACPI0001", 0}, @@ -280,19 +280,18 @@ static int acpi_smbus_hc_add(struct acpi_device *device) extern void acpi_ec_remove_query_handler(struct acpi_ec *ec, u8 query_bit); -static int acpi_smbus_hc_remove(struct acpi_device *device) +static void acpi_smbus_hc_remove(struct acpi_device *device) { struct acpi_smb_hc *hc; if (!device) - return -EINVAL; + return; hc = acpi_driver_data(device); acpi_ec_remove_query_handler(hc->ec, hc->query_bit); acpi_os_wait_events_complete(); kfree(hc); device->driver_data = NULL; - return 0; } module_acpi_driver(acpi_smb_hc_driver); diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 40ecb55b52f8c..642d498d448bb 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -70,7 +70,7 @@ MODULE_PARM_DESC(psv, "Disable or override all passive trip points."); static struct workqueue_struct *acpi_thermal_pm_queue; static int acpi_thermal_add(struct acpi_device *device); -static int acpi_thermal_remove(struct acpi_device *device); +static void acpi_thermal_remove(struct acpi_device *device); static void acpi_thermal_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id thermal_device_ids[] = { @@ -1055,19 +1055,18 @@ static int acpi_thermal_add(struct acpi_device *device) return result; } -static int acpi_thermal_remove(struct acpi_device *device) +static void acpi_thermal_remove(struct acpi_device *device) { struct acpi_thermal *tz; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; flush_workqueue(acpi_thermal_pm_queue); tz = acpi_driver_data(device); acpi_thermal_unregister_thermal_zone(tz); kfree(tz); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/acpi/tiny-power-button.c b/drivers/acpi/tiny-power-button.c index a19f0e4e69f76..598f548b21f36 100644 --- a/drivers/acpi/tiny-power-button.c +++ b/drivers/acpi/tiny-power-button.c @@ -19,11 +19,15 @@ static const struct acpi_device_id tiny_power_button_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, tiny_power_button_device_ids); -static int acpi_noop_add_remove(struct acpi_device *device) +static int acpi_noop_add(struct acpi_device *device) { return 0; } +static void acpi_noop_remove(struct acpi_device *device) +{ +} + static void acpi_tiny_power_button_notify(struct acpi_device *device, u32 event) { kill_cad_pid(power_signal, 1); @@ -34,8 +38,8 @@ static struct acpi_driver acpi_tiny_power_button_driver = { .class = "tiny-power-button", .ids = tiny_power_button_device_ids, .ops = { - .add = acpi_noop_add_remove, - .remove = acpi_noop_add_remove, + .add = acpi_noop_add, + .remove = acpi_noop_remove, .notify = acpi_tiny_power_button_notify, }, }; diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 27e301a6bb7a3..9211531689b28 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1123,10 +1123,9 @@ static int sonypi_acpi_add(struct acpi_device *device) return 0; } -static int sonypi_acpi_remove(struct acpi_device *device) +static void sonypi_acpi_remove(struct acpi_device *device) { sonypi_acpi_device = NULL; - return 0; } static const struct acpi_device_id sonypi_device_ids[] = { diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 16fc481d60950..7e9da671a0e84 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -724,14 +724,12 @@ static int crb_acpi_add(struct acpi_device *device) return rc; } -static int crb_acpi_remove(struct acpi_device *device) +static void crb_acpi_remove(struct acpi_device *device) { struct device *dev = &device->dev; struct tpm_chip *chip = dev_get_drvdata(dev); tpm_chip_unregister(chip); - - return 0; } static const struct dev_pm_ops crb_pm = { diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 115835bd562c7..c0cafd728af7b 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2266,7 +2266,7 @@ static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) return AE_OK; } -static int vmbus_acpi_remove(struct acpi_device *device) +static void vmbus_acpi_remove(struct acpi_device *device) { struct resource *cur_res; struct resource *next_res; @@ -2283,8 +2283,6 @@ static int vmbus_acpi_remove(struct acpi_device *device) kfree(cur_res); } } - - return 0; } static void vmbus_reserve_fb(void) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 11f6e074c984e..27c278431ba9e 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -948,12 +948,12 @@ static int acpi_power_meter_add(struct acpi_device *device) return res; } -static int acpi_power_meter_remove(struct acpi_device *device) +static void acpi_power_meter_remove(struct acpi_device *device) { struct acpi_power_meter_resource *resource; if (!device || !acpi_driver_data(device)) - return -EINVAL; + return; resource = acpi_driver_data(device); hwmon_device_unregister(resource->hwmon_dev); @@ -962,7 +962,6 @@ static int acpi_power_meter_remove(struct acpi_device *device) free_capabilities(resource); kfree(resource); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index ff64a39d56def..d778a2aaefec1 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -187,7 +187,7 @@ struct atk_acpi_input_buf { }; static int atk_add(struct acpi_device *device); -static int atk_remove(struct acpi_device *device); +static void atk_remove(struct acpi_device *device); static void atk_print_sensor(struct atk_data *data, union acpi_object *obj); static int atk_read_value(struct atk_sensor_data *sensor, u64 *value); @@ -1344,7 +1344,7 @@ static int atk_add(struct acpi_device *device) return err; } -static int atk_remove(struct acpi_device *device) +static void atk_remove(struct acpi_device *device) { struct atk_data *data = device->driver_data; dev_dbg(&device->dev, "removing...\n"); @@ -1359,8 +1359,6 @@ static int atk_remove(struct acpi_device *device) if (atk_ec_ctl(data, 0)) dev_err(&device->dev, "Failed to disable EC\n"); } - - return 0; } static int __init atk0110_init(void) diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index 0e77c40e1966b..3c9bbd04e1434 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -106,7 +106,7 @@ static int atlas_acpi_button_add(struct acpi_device *device) return err; } -static int atlas_acpi_button_remove(struct acpi_device *device) +static void atlas_acpi_button_remove(struct acpi_device *device) { acpi_status status; @@ -116,8 +116,6 @@ static int atlas_acpi_button_remove(struct acpi_device *device) pr_err("error removing addr spc handler\n"); input_unregister_device(input_dev); - - return 0; } static const struct acpi_device_id atlas_device_ids[] = { diff --git a/drivers/net/fjes/fjes_main.c b/drivers/net/fjes/fjes_main.c index 1d1808afd5295..6cacc8c3b6ccd 100644 --- a/drivers/net/fjes/fjes_main.c +++ b/drivers/net/fjes/fjes_main.c @@ -166,14 +166,12 @@ static int fjes_acpi_add(struct acpi_device *device) return 0; } -static int fjes_acpi_remove(struct acpi_device *device) +static void fjes_acpi_remove(struct acpi_device *device) { struct platform_device *plat_dev; plat_dev = (struct platform_device *)acpi_driver_data(device); platform_device_unregister(plat_dev); - - return 0; } static acpi_status diff --git a/drivers/platform/chrome/chromeos_privacy_screen.c b/drivers/platform/chrome/chromeos_privacy_screen.c new file mode 100644 index 0000000000000..bb74ddf9af4ac --- /dev/null +++ b/drivers/platform/chrome/chromeos_privacy_screen.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * ChromeOS Privacy Screen support + * + * Copyright (C) 2022 Google LLC + * + * This is the Chromeos privacy screen provider, present on certain chromebooks, + * represented by a GOOG0010 device in the ACPI. This ACPI device, if present, + * will cause the i915 drm driver to probe defer until this driver registers + * the privacy-screen. + */ + +#include +#include + +/* + * The DSM (Device Specific Method) constants below are the agreed API with + * the firmware team, on how to control privacy screen using ACPI methods. + */ +#define PRIV_SCRN_DSM_REVID 1 /* DSM version */ +#define PRIV_SCRN_DSM_FN_GET_STATUS 1 /* Get privacy screen status */ +#define PRIV_SCRN_DSM_FN_ENABLE 2 /* Enable privacy screen */ +#define PRIV_SCRN_DSM_FN_DISABLE 3 /* Disable privacy screen */ + +static const guid_t chromeos_privacy_screen_dsm_guid = + GUID_INIT(0xc7033113, 0x8720, 0x4ceb, + 0x90, 0x90, 0x9d, 0x52, 0xb3, 0xe5, 0x2d, 0x73); + +static void +chromeos_privacy_screen_get_hw_state(struct drm_privacy_screen + *drm_privacy_screen) +{ + union acpi_object *obj; + acpi_handle handle; + struct device *privacy_screen = + drm_privacy_screen_get_drvdata(drm_privacy_screen); + + handle = acpi_device_handle(to_acpi_device(privacy_screen)); + obj = acpi_evaluate_dsm(handle, &chromeos_privacy_screen_dsm_guid, + PRIV_SCRN_DSM_REVID, + PRIV_SCRN_DSM_FN_GET_STATUS, NULL); + if (!obj) { + dev_err(privacy_screen, + "_DSM failed to get privacy-screen state\n"); + return; + } + + if (obj->type != ACPI_TYPE_INTEGER) + dev_err(privacy_screen, + "Bad _DSM to get privacy-screen state\n"); + else if (obj->integer.value == 1) + drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = + PRIVACY_SCREEN_ENABLED; + else + drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = + PRIVACY_SCREEN_DISABLED; + + ACPI_FREE(obj); +} + +static int +chromeos_privacy_screen_set_sw_state(struct drm_privacy_screen + *drm_privacy_screen, + enum drm_privacy_screen_status state) +{ + union acpi_object *obj = NULL; + acpi_handle handle; + struct device *privacy_screen = + drm_privacy_screen_get_drvdata(drm_privacy_screen); + + handle = acpi_device_handle(to_acpi_device(privacy_screen)); + + if (state == PRIVACY_SCREEN_DISABLED) { + obj = acpi_evaluate_dsm(handle, + &chromeos_privacy_screen_dsm_guid, + PRIV_SCRN_DSM_REVID, + PRIV_SCRN_DSM_FN_DISABLE, NULL); + } else if (state == PRIVACY_SCREEN_ENABLED) { + obj = acpi_evaluate_dsm(handle, + &chromeos_privacy_screen_dsm_guid, + PRIV_SCRN_DSM_REVID, + PRIV_SCRN_DSM_FN_ENABLE, NULL); + } else { + dev_err(privacy_screen, + "Bad attempt to set privacy-screen status to %u\n", + state); + return -EINVAL; + } + + if (!obj) { + dev_err(privacy_screen, + "_DSM failed to set privacy-screen state\n"); + return -EIO; + } + + drm_privacy_screen->hw_state = drm_privacy_screen->sw_state = state; + ACPI_FREE(obj); + return 0; +} + +static const struct drm_privacy_screen_ops chromeos_privacy_screen_ops = { + .get_hw_state = chromeos_privacy_screen_get_hw_state, + .set_sw_state = chromeos_privacy_screen_set_sw_state, +}; + +static int chromeos_privacy_screen_add(struct acpi_device *adev) +{ + struct drm_privacy_screen *drm_privacy_screen = + drm_privacy_screen_register(&adev->dev, + &chromeos_privacy_screen_ops, + &adev->dev); + + if (IS_ERR(drm_privacy_screen)) { + dev_err(&adev->dev, "Error registering privacy-screen\n"); + return PTR_ERR(drm_privacy_screen); + } + + adev->driver_data = drm_privacy_screen; + dev_info(&adev->dev, "registered privacy-screen '%s'\n", + dev_name(&drm_privacy_screen->dev)); + + return 0; +} + +static void chromeos_privacy_screen_remove(struct acpi_device *adev) +{ + struct drm_privacy_screen *drm_privacy_screen = acpi_driver_data(adev); + + drm_privacy_screen_unregister(drm_privacy_screen); +} + +static const struct acpi_device_id chromeos_privacy_screen_device_ids[] = { + {"GOOG0010", 0}, /* Google's electronic privacy screen for eDP-1 */ + {} +}; +MODULE_DEVICE_TABLE(acpi, chromeos_privacy_screen_device_ids); + +static struct acpi_driver chromeos_privacy_screen_driver = { + .name = "chromeos_privacy_screen_driver", + .class = "ChromeOS", + .ids = chromeos_privacy_screen_device_ids, + .ops = { + .add = chromeos_privacy_screen_add, + .remove = chromeos_privacy_screen_remove, + }, +}; + +module_acpi_driver(chromeos_privacy_screen_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS ACPI Privacy Screen driver"); +MODULE_AUTHOR("Rajat Jain "); diff --git a/drivers/platform/chrome/wilco_ec/event.c b/drivers/platform/chrome/wilco_ec/event.c index 814518509739d..a873066b03825 100644 --- a/drivers/platform/chrome/wilco_ec/event.c +++ b/drivers/platform/chrome/wilco_ec/event.c @@ -500,15 +500,13 @@ static int event_device_add(struct acpi_device *adev) return error; } -static int event_device_remove(struct acpi_device *adev) +static void event_device_remove(struct acpi_device *adev) { struct event_device_data *dev_data = adev->driver_data; cdev_device_del(&dev_data->cdev, &dev_data->dev); ida_simple_remove(&event_ida, MINOR(dev_data->dev.devt)); hangup_device(dev_data); - - return 0; } static const struct acpi_device_id event_acpi_ids[] = { diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index 242fb690dcaf7..2755601f979cd 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -239,13 +239,12 @@ static int surface_button_add(struct acpi_device *device) return error; } -static int surface_button_remove(struct acpi_device *device) +static void surface_button_remove(struct acpi_device *device) { struct surface_button *button = acpi_driver_data(device); input_unregister_device(button->input); kfree(button); - return 0; } static SIMPLE_DEV_PM_OPS(surface_button_pm, diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 4d2d32bfbe2a6..a6dbe05af271b 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1901,7 +1901,7 @@ static int asus_acpi_add(struct acpi_device *device) return result; } -static int asus_acpi_remove(struct acpi_device *device) +static void asus_acpi_remove(struct acpi_device *device) { struct asus_laptop *asus = acpi_driver_data(device); @@ -1914,7 +1914,6 @@ static int asus_acpi_remove(struct acpi_device *device) kfree(asus->name); kfree(asus); - return 0; } static const struct acpi_device_id asus_device_ids[] = { diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index d3e7171928e5d..abf01e00b799f 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -175,7 +175,7 @@ static int asus_wireless_add(struct acpi_device *adev) return err; } -static int asus_wireless_remove(struct acpi_device *adev) +static void asus_wireless_remove(struct acpi_device *adev) { struct asus_wireless_data *data = acpi_driver_data(adev); @@ -183,7 +183,6 @@ static int asus_wireless_remove(struct acpi_device *adev) devm_led_classdev_unregister(&adev->dev, &data->led); destroy_workqueue(data->wq); } - return 0; } static struct acpi_driver asus_wireless_driver = { diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 9309ab5792cbc..8b6a146118599 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -418,11 +418,11 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) return error; } -static int cmpc_accel_remove_v4(struct acpi_device *acpi) +static void cmpc_accel_remove_v4(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); - return cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(acpi); } static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, @@ -648,10 +648,10 @@ static int cmpc_accel_add(struct acpi_device *acpi) return error; } -static int cmpc_accel_remove(struct acpi_device *acpi) +static void cmpc_accel_remove(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); - return cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(acpi); } static const struct acpi_device_id cmpc_accel_device_ids[] = { @@ -727,9 +727,9 @@ static int cmpc_tablet_add(struct acpi_device *acpi) cmpc_tablet_idev_init); } -static int cmpc_tablet_remove(struct acpi_device *acpi) +static void cmpc_tablet_remove(struct acpi_device *acpi) { - return cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(acpi); } #ifdef CONFIG_PM_SLEEP @@ -974,7 +974,7 @@ static int cmpc_ipml_add(struct acpi_device *acpi) return retval; } -static int cmpc_ipml_remove(struct acpi_device *acpi) +static void cmpc_ipml_remove(struct acpi_device *acpi) { struct ipml200_dev *ipml; @@ -988,8 +988,6 @@ static int cmpc_ipml_remove(struct acpi_device *acpi) } kfree(ipml); - - return 0; } static const struct acpi_device_id cmpc_ipml_device_ids[] = { @@ -1055,9 +1053,9 @@ static int cmpc_keys_add(struct acpi_device *acpi) cmpc_keys_idev_init); } -static int cmpc_keys_remove(struct acpi_device *acpi) +static void cmpc_keys_remove(struct acpi_device *acpi) { - return cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(acpi); } static const struct acpi_device_id cmpc_keys_device_ids[] = { diff --git a/drivers/platform/x86/dell/dell-rbtn.c b/drivers/platform/x86/dell/dell-rbtn.c index a89fad47ff139..aa0e6c9074942 100644 --- a/drivers/platform/x86/dell/dell-rbtn.c +++ b/drivers/platform/x86/dell/dell-rbtn.c @@ -206,7 +206,7 @@ static void rbtn_input_event(struct rbtn_data *rbtn_data) */ static int rbtn_add(struct acpi_device *device); -static int rbtn_remove(struct acpi_device *device); +static void rbtn_remove(struct acpi_device *device); static void rbtn_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id rbtn_ids[] = { @@ -426,7 +426,7 @@ static int rbtn_add(struct acpi_device *device) } -static int rbtn_remove(struct acpi_device *device) +static void rbtn_remove(struct acpi_device *device) { struct rbtn_data *rbtn_data = device->driver_data; @@ -443,8 +443,6 @@ static int rbtn_remove(struct acpi_device *device) rbtn_acquire(device, false); device->driver_data = NULL; - - return 0; } static void rbtn_notify(struct acpi_device *device, u32 event) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ba08c9235f769..c8b3fdabbc5e3 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1440,7 +1440,7 @@ static int eeepc_acpi_add(struct acpi_device *device) return result; } -static int eeepc_acpi_remove(struct acpi_device *device) +static void eeepc_acpi_remove(struct acpi_device *device) { struct eeepc_laptop *eeepc = acpi_driver_data(device); @@ -1451,7 +1451,6 @@ static int eeepc_acpi_remove(struct acpi_device *device) eeepc_platform_exit(eeepc); kfree(eeepc); - return 0; } diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 80929380ec7e3..5c30794c0c344 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -847,15 +847,13 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) return ret; } -static int acpi_fujitsu_laptop_remove(struct acpi_device *device) +static void acpi_fujitsu_laptop_remove(struct acpi_device *device) { struct fujitsu_laptop *priv = acpi_driver_data(device); fujitsu_laptop_platform_remove(device); kfifo_free(&priv->fifo); - - return 0; } static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 7fb7fe5eb55a1..17f08ce7552d7 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -484,12 +484,11 @@ static int acpi_fujitsu_add(struct acpi_device *adev) return 0; } -static int acpi_fujitsu_remove(struct acpi_device *adev) +static void acpi_fujitsu_remove(struct acpi_device *adev) { free_irq(fujitsu.irq, fujitsu_interrupt); release_region(fujitsu.io_base, fujitsu.io_length); input_fujitsu_remove(); - return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/platform/x86/intel/rst.c b/drivers/platform/x86/intel/rst.c index 3b81cb896fedf..35814a7707af7 100644 --- a/drivers/platform/x86/intel/rst.c +++ b/drivers/platform/x86/intel/rst.c @@ -113,12 +113,10 @@ static int irst_add(struct acpi_device *acpi) return error; } -static int irst_remove(struct acpi_device *acpi) +static void irst_remove(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &irst_wakeup_attr); device_remove_file(&acpi->dev, &irst_timeout_attr); - - return 0; } static const struct acpi_device_id irst_ids[] = { diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 88b551caeaaf4..f406efac58300 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -710,7 +710,7 @@ static int acpi_add(struct acpi_device *device) return ret; } -static int acpi_remove(struct acpi_device *device) +static void acpi_remove(struct acpi_device *device) { sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); @@ -721,8 +721,6 @@ static int acpi_remove(struct acpi_device *device) platform_device_unregister(pf_device); pf_device = NULL; platform_driver_unregister(&pf_driver); - - return 0; } static const struct acpi_device_id device_ids[] = { diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index b06382dcecf78..3b1d1b51dc570 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -183,7 +183,7 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0, /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */ static int acpi_pcc_hotkey_add(struct acpi_device *device); -static int acpi_pcc_hotkey_remove(struct acpi_device *device); +static void acpi_pcc_hotkey_remove(struct acpi_device *device); static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id pcc_device_ids[] = { @@ -1090,12 +1090,12 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) return result; } -static int acpi_pcc_hotkey_remove(struct acpi_device *device) +static void acpi_pcc_hotkey_remove(struct acpi_device *device) { struct pcc_acpi *pcc = acpi_driver_data(device); if (!device || !pcc) - return -EINVAL; + return; i8042_remove_filter(panasonic_i8042_filter); @@ -1113,8 +1113,6 @@ static int acpi_pcc_hotkey_remove(struct acpi_device *device) kfree(pcc->sinf); kfree(pcc); - - return 0; } module_acpi_driver(acpi_pcc_driver); diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 336dee9485d4b..07d2e1901f010 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -3280,7 +3280,7 @@ static int sony_nc_add(struct acpi_device *device) return result; } -static int sony_nc_remove(struct acpi_device *device) +static void sony_nc_remove(struct acpi_device *device) { struct sony_nc_value *item; @@ -3297,8 +3297,6 @@ static int sony_nc_remove(struct acpi_device *device) sony_pf_remove(); sony_laptop_remove_input(); dprintk(SONY_NC_DRIVER_NAME " removed.\n"); - - return 0; } static const struct acpi_device_id sony_device_ids[] = { @@ -4652,14 +4650,14 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id) * ACPI driver * *****************/ -static int sony_pic_remove(struct acpi_device *device) +static void sony_pic_remove(struct acpi_device *device) { struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; if (sony_pic_disable(device)) { pr_err("Couldn't disable device\n"); - return -ENXIO; + return; } free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); @@ -4689,7 +4687,6 @@ static int sony_pic_remove(struct acpi_device *device) spic_dev.cur_irq = NULL; dprintk(SONY_PIC_DRIVER_NAME " removed.\n"); - return 0; } static int sony_pic_add(struct acpi_device *device) diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index c14fd22ba1968..85ba3c6acde9e 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -350,7 +350,7 @@ static int system76_add(struct acpi_device *acpi_dev) } // Remove a System76 ACPI device -static int system76_remove(struct acpi_device *acpi_dev) +static void system76_remove(struct acpi_device *acpi_dev) { struct system76_data *data; @@ -363,8 +363,6 @@ static int system76_remove(struct acpi_device *acpi_dev) devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led); system76_get(data, "FINI"); - - return 0; } static struct acpi_driver system76_driver = { diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index f7761d98c0fd0..2e4913b993332 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -332,7 +332,7 @@ static int topstar_acpi_add(struct acpi_device *device) return err; } -static int topstar_acpi_remove(struct acpi_device *device) +static void topstar_acpi_remove(struct acpi_device *device) { struct topstar_laptop *topstar = acpi_driver_data(device); @@ -344,7 +344,6 @@ static int topstar_acpi_remove(struct acpi_device *device) topstar_acpi_exit(topstar); kfree(topstar); - return 0; } static const struct acpi_device_id topstar_device_ids[] = { diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 352508d304675..b50333c952b3f 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -2960,7 +2960,7 @@ static void print_supported_features(struct toshiba_acpi_dev *dev) pr_cont("\n"); } -static int toshiba_acpi_remove(struct acpi_device *acpi_dev) +static void toshiba_acpi_remove(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); @@ -3000,8 +3000,6 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) toshiba_acpi = NULL; kfree(dev); - - return 0; } static const char *find_hci_method(acpi_handle handle) diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 57a5dc60c58a6..d8f81962a2402 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -36,7 +36,7 @@ struct toshiba_bluetooth_dev { }; static int toshiba_bt_rfkill_add(struct acpi_device *device); -static int toshiba_bt_rfkill_remove(struct acpi_device *device); +static void toshiba_bt_rfkill_remove(struct acpi_device *device); static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id bt_device_ids[] = { @@ -279,7 +279,7 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) return result; } -static int toshiba_bt_rfkill_remove(struct acpi_device *device) +static void toshiba_bt_rfkill_remove(struct acpi_device *device) { struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device); @@ -291,7 +291,7 @@ static int toshiba_bt_rfkill_remove(struct acpi_device *device) kfree(bt_dev); - return toshiba_bluetooth_disable(device->handle); + toshiba_bluetooth_disable(device->handle); } module_acpi_driver(toshiba_bt_rfkill_driver); diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index 49e84095bb010..8c9f76286b080 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -138,14 +138,12 @@ static void toshiba_haps_notify(struct acpi_device *device, u32 event) event, 0); } -static int toshiba_haps_remove(struct acpi_device *device) +static void toshiba_haps_remove(struct acpi_device *device) { sysfs_remove_group(&device->dev.kobj, &haps_attr_group); if (toshiba_haps) toshiba_haps = NULL; - - return 0; } /* Helper function */ diff --git a/drivers/platform/x86/wireless-hotkey.c b/drivers/platform/x86/wireless-hotkey.c index 11c60a2734468..c64d9f0844049 100644 --- a/drivers/platform/x86/wireless-hotkey.c +++ b/drivers/platform/x86/wireless-hotkey.c @@ -83,10 +83,9 @@ static int wl_add(struct acpi_device *device) return err; } -static int wl_remove(struct acpi_device *device) +static void wl_remove(struct acpi_device *device) { wireless_input_destroy(); - return 0; } static struct acpi_driver wl_driver = { diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 97440462aa258..391f7ea4431e0 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -143,13 +143,12 @@ static int ebook_switch_add(struct acpi_device *device) return error; } -static int ebook_switch_remove(struct acpi_device *device) +static void ebook_switch_remove(struct acpi_device *device) { struct ebook_switch *button = acpi_driver_data(device); input_unregister_device(button->input); kfree(button); - return 0; } static struct acpi_driver xo15_ebook_driver = { diff --git a/drivers/ptp/ptp_vmw.c b/drivers/ptp/ptp_vmw.c index 5dca26e14bdc8..0dcbabd1533db 100644 --- a/drivers/ptp/ptp_vmw.c +++ b/drivers/ptp/ptp_vmw.c @@ -101,10 +101,9 @@ static int ptp_vmw_acpi_add(struct acpi_device *device) return 0; } -static int ptp_vmw_acpi_remove(struct acpi_device *device) +static void ptp_vmw_acpi_remove(struct acpi_device *device) { ptp_clock_unregister(ptp_vmw_clock); - return 0; } static const struct acpi_device_id ptp_vmw_acpi_device_ids[] = { diff --git a/drivers/thermal/intel/intel_menlow.c b/drivers/thermal/intel/intel_menlow.c index 101d7e791a137..3f885b08a4909 100644 --- a/drivers/thermal/intel/intel_menlow.c +++ b/drivers/thermal/intel/intel_menlow.c @@ -179,22 +179,20 @@ static int intel_menlow_memory_add(struct acpi_device *device) } -static int intel_menlow_memory_remove(struct acpi_device *device) +static void intel_menlow_memory_remove(struct acpi_device *device) { struct thermal_cooling_device *cdev; if (!device) - return -EINVAL; + return; cdev = acpi_driver_data(device); if (!cdev) - return -EINVAL; + return; sysfs_remove_link(&device->dev.kobj, "thermal_cooling"); sysfs_remove_link(&cdev->device.kobj, "device"); thermal_cooling_device_unregister(cdev); - - return 0; } static const struct acpi_device_id intel_menlow_memory_ids[] = { diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c index c0d9339cff873..e9e7acb577bfa 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -193,13 +193,12 @@ static int apple_bl_add(struct acpi_device *dev) return 0; } -static int apple_bl_remove(struct acpi_device *dev) +static void apple_bl_remove(struct acpi_device *dev) { backlight_device_unregister(apple_backlight_device); release_region(hw_data->iostart, hw_data->iolen); hw_data = NULL; - return 0; } static const struct acpi_device_id apple_bl_ids[] = { diff --git a/drivers/watchdog/ni903x_wdt.c b/drivers/watchdog/ni903x_wdt.c index 4cebad324b202..045bb72d9a438 100644 --- a/drivers/watchdog/ni903x_wdt.c +++ b/drivers/watchdog/ni903x_wdt.c @@ -224,14 +224,12 @@ static int ni903x_acpi_add(struct acpi_device *device) return 0; } -static int ni903x_acpi_remove(struct acpi_device *device) +static void ni903x_acpi_remove(struct acpi_device *device) { struct ni903x_wdt *wdt = acpi_driver_data(device); ni903x_wdd_stop(&wdt->wdd); watchdog_unregister_device(&wdt->wdd); - - return 0; } static const struct acpi_device_id ni903x_device_ids[] = { diff --git a/drivers/xen/xen-acpi-pad.c b/drivers/xen/xen-acpi-pad.c index ccd8012020f15..ede69a5278d3f 100644 --- a/drivers/xen/xen-acpi-pad.c +++ b/drivers/xen/xen-acpi-pad.c @@ -122,7 +122,7 @@ static int acpi_pad_add(struct acpi_device *device) return 0; } -static int acpi_pad_remove(struct acpi_device *device) +static void acpi_pad_remove(struct acpi_device *device) { mutex_lock(&xen_cpu_lock); xen_acpi_pad_idle_cpus(0); @@ -130,7 +130,6 @@ static int acpi_pad_remove(struct acpi_device *device) acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY, acpi_pad_notify); - return 0; } static const struct acpi_device_id pad_device_ids[] = { diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 8580e33382427..8035f70a6e5ff 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -150,7 +150,7 @@ struct acpi_hotplug_context { */ typedef int (*acpi_op_add) (struct acpi_device * device); -typedef int (*acpi_op_remove) (struct acpi_device * device); +typedef void (*acpi_op_remove) (struct acpi_device *device); typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event); struct acpi_device_ops { From eac5ace8e836b007697535959826a2b131979c5e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 5 Dec 2022 19:56:14 +0100 Subject: [PATCH 151/227] ACPI: thermal: Adjust critical.flags.valid check It is not necessary to compare critical.flags.valid to 1 in acpi_thermal_trips_update() and doing so is also inconsistent with other similar checks in that code, so simply check if the flag is not 0 instead. No expected functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 642d498d448bb..496c226613ae1 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -287,7 +287,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) "Found critical threshold [%lu]\n", tz->trips.critical.temperature); } - if (tz->trips.critical.flags.valid == 1) { + if (tz->trips.critical.flags.valid) { if (crt == -1) { tz->trips.critical.flags.valid = 0; } else if (crt > 0) { From 7bbebcc919367c24602fc6272cc9a55d39d936dc Mon Sep 17 00:00:00 2001 From: Qi Xi Date: Fri, 4 Jul 2025 13:57:43 +0800 Subject: [PATCH 152/227] Revert "arm64: add HWCAP for FEAT_HBC (hinted conditional branches)" This reverts commit a5eb84a142299b796c94b3d43c21409a22f18c4a. This reverts commit 1ade045233751daefb4f57ccd7ea2d2311c5ded0. Signed-off-by: Qi Xi --- arch/arm64/include/asm/hwcap.h | 1 - arch/arm64/include/asm/sysreg.h | 4 ---- arch/arm64/include/uapi/asm/hwcap.h | 1 - arch/arm64/kernel/cpufeature.c | 21 +-------------------- arch/arm64/kernel/cpuinfo.c | 1 - arch/arm64/tools/cpucaps | 1 - 6 files changed, 1 insertion(+), 28 deletions(-) diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index 2155094ef9f73..f68fbb2074730 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -108,7 +108,6 @@ #define KERNEL_HWCAP_ECV __khwcap2_feature(ECV) #define KERNEL_HWCAP_AFP __khwcap2_feature(AFP) #define KERNEL_HWCAP_RPRES __khwcap2_feature(RPRES) -#define KERNEL_HWCAP_HBC __khwcap2_feature(HBC) /* * This yields a mask that user programs can use to figure out what diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index b8d00be796cf0..3cafd4ef0c74a 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -907,7 +907,6 @@ /* id_aa64mmfr1 */ #define ID_AA64MMFR1_ECBHB_SHIFT 60 -#define ID_AA64MMFR1_TIDCP1_SHIFT 52 #define ID_AA64MMFR1_AFP_SHIFT 44 #define ID_AA64MMFR1_ETS_SHIFT 36 #define ID_AA64MMFR1_TWED_SHIFT 32 @@ -923,9 +922,6 @@ #define ID_AA64MMFR1_VMIDBITS_8 0 #define ID_AA64MMFR1_VMIDBITS_16 2 -#define ID_AA64MMFR1_TIDCP1_NI 0 -#define ID_AA64MMFR1_TIDCP1_IMP 1 - /* id_aa64mmfr2 */ #define ID_AA64MMFR2_E0PD_SHIFT 60 #define ID_AA64MMFR2_EVT_SHIFT 56 diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h index 0a3df77b8b649..f03731847d9df 100644 --- a/arch/arm64/include/uapi/asm/hwcap.h +++ b/arch/arm64/include/uapi/asm/hwcap.h @@ -78,6 +78,5 @@ #define HWCAP2_ECV (1 << 19) #define HWCAP2_AFP (1 << 20) #define HWCAP2_RPRES (1 << 21) -#define HWCAP2_HBC (1UL << 22) #endif /* _UAPI__ASM_HWCAP_H */ diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index e0b79f932a5f3..752e1001a5c7e 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -232,7 +232,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar1[] = { }; static const struct arm64_ftr_bits ftr_id_aa64isar2[] = { - ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_SAFE, ID_AA64ISAR2_CLEARBHB_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_HIGHER_SAFE, ID_AA64ISAR2_CLEARBHB_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_RPRES_SHIFT, 4, 0), ARM64_FTR_END, }; @@ -337,7 +337,6 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = { }; static const struct arm64_ftr_bits ftr_id_aa64mmfr1[] = { - ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_TIDCP1_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_AFP_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_ETS_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_TWED_SHIFT, 4, 0), @@ -1944,11 +1943,6 @@ static bool is_kvm_protected_mode(const struct arm64_cpu_capabilities *entry, in } #endif /* CONFIG_KVM */ -static void cpu_trap_el0_impdef(const struct arm64_cpu_capabilities *__unused) -{ - sysreg_clear_set(sctlr_el1, 0, SCTLR_EL1_TIDCP); -} - /* Internal helper functions to match cpu capability type */ static bool cpucap_late_cpu_optional(const struct arm64_cpu_capabilities *cap) @@ -2391,18 +2385,6 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, .min_field_value = 1, }, - { - .desc = "Trap EL0 IMPLEMENTATION DEFINED functionality", - .capability = ARM64_HAS_TIDCP1, - .type = ARM64_CPUCAP_SYSTEM_FEATURE, - .sys_reg = SYS_ID_AA64MMFR1_EL1, - .sign = FTR_UNSIGNED, - .field_pos = ID_AA64MMFR1_TIDCP1_SHIFT, - .field_width = 4, - .min_field_value = ID_AA64MMFR1_TIDCP1_IMP, - .matches = has_cpuid_feature, - .cpu_enable = cpu_trap_el0_impdef, - }, {}, }; @@ -2525,7 +2507,6 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { HWCAP_CAP(SYS_ID_AA64MMFR0_EL1, ID_AA64MMFR0_ECV_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_ECV), HWCAP_CAP(SYS_ID_AA64MMFR1_EL1, ID_AA64MMFR1_AFP_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_AFP), HWCAP_CAP(SYS_ID_AA64ISAR2_EL1, ID_AA64ISAR2_RPRES_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_RPRES), - HWCAP_CAP(ID_AA64ISAR2_EL1, BC, IMP, CAP_HWCAP, KERNEL_HWCAP_HBC), {}, }; diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index 04a7d2a974ce7..d72898af05b4c 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -97,7 +97,6 @@ static const char *const hwcap_str[] = { [KERNEL_HWCAP_ECV] = "ecv", [KERNEL_HWCAP_AFP] = "afp", [KERNEL_HWCAP_RPRES] = "rpres", - [KERNEL_HWCAP_HBC] = "hbc", }; #ifdef CONFIG_COMPAT diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index c44cd3b076e9a..a590cabe890c5 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -34,7 +34,6 @@ HAS_RNG HAS_SB HAS_STAGE2_FWB HAS_SYSREG_GIC_CPUIF -HAS_TIDCP1 HAS_TLB_RANGE HAS_VIRT_HOST_EXTN HW_DBM From f18d668601b0febf461c30a8784b3428e800238e Mon Sep 17 00:00:00 2001 From: Kristina Martsenko Date: Fri, 4 Jul 2025 13:57:44 +0800 Subject: [PATCH 153/227] arm64: trap implementation defined functionality in userspace The Arm v8.8 extension adds a new control FEAT_TIDCP1 that allows the kernel to disable all implementation-defined system registers and instructions in userspace. This can improve robustness against covert channels between processes, for example in cases where the firmware or hardware didn't disable that functionality by default. The kernel does not currently support any implementation-defined features, as there are no hwcaps for any such features, so disable all imp-def features unconditionally. Any use of imp-def instructions will result in a SIGILL being delivered to the process (same as for undefined instructions). Signed-off-by: Kristina Martsenko Link: https://lore.kernel.org/r/20220622115424.683520-1-kristina.martsenko@arm.com Signed-off-by: Will Deacon Signed-off-by: Qi Xi --- arch/arm64/include/asm/sysreg.h | 5 +++++ arch/arm64/kernel/cpufeature.c | 17 +++++++++++++++++ arch/arm64/tools/cpucaps | 1 + 3 files changed, 23 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 3cafd4ef0c74a..227085e91344f 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -678,6 +678,7 @@ #define SCTLR_EL1_TCF0_ASYNC (UL(0x2) << SCTLR_EL1_TCF0_SHIFT) #define SCTLR_EL1_TCF0_MASK (UL(0x3) << SCTLR_EL1_TCF0_SHIFT) +#define SCTLR_EL1_TIDCP (BIT(63)) #define SCTLR_EL1_BT1 (BIT(36)) #define SCTLR_EL1_BT0 (BIT(35)) #define SCTLR_EL1_UCI (BIT(26)) @@ -907,6 +908,7 @@ /* id_aa64mmfr1 */ #define ID_AA64MMFR1_ECBHB_SHIFT 60 +#define ID_AA64MMFR1_TIDCP1_SHIFT 52 #define ID_AA64MMFR1_AFP_SHIFT 44 #define ID_AA64MMFR1_ETS_SHIFT 36 #define ID_AA64MMFR1_TWED_SHIFT 32 @@ -922,6 +924,9 @@ #define ID_AA64MMFR1_VMIDBITS_8 0 #define ID_AA64MMFR1_VMIDBITS_16 2 +#define ID_AA64MMFR1_TIDCP1_NI 0 +#define ID_AA64MMFR1_TIDCP1_IMP 1 + /* id_aa64mmfr2 */ #define ID_AA64MMFR2_E0PD_SHIFT 60 #define ID_AA64MMFR2_EVT_SHIFT 56 diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 752e1001a5c7e..ef57a8fbc4833 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -337,6 +337,7 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = { }; static const struct arm64_ftr_bits ftr_id_aa64mmfr1[] = { + ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_TIDCP1_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_AFP_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_ETS_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_TWED_SHIFT, 4, 0), @@ -1943,6 +1944,11 @@ static bool is_kvm_protected_mode(const struct arm64_cpu_capabilities *entry, in } #endif /* CONFIG_KVM */ +static void cpu_trap_el0_impdef(const struct arm64_cpu_capabilities *__unused) +{ + sysreg_clear_set(sctlr_el1, 0, SCTLR_EL1_TIDCP); +} + /* Internal helper functions to match cpu capability type */ static bool cpucap_late_cpu_optional(const struct arm64_cpu_capabilities *cap) @@ -2385,6 +2391,17 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, .min_field_value = 1, }, + { + .desc = "Trap EL0 IMPLEMENTATION DEFINED functionality", + .capability = ARM64_HAS_TIDCP1, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .sys_reg = SYS_ID_AA64MMFR1_EL1, + .sign = FTR_UNSIGNED, + .field_pos = ID_AA64MMFR1_TIDCP1_SHIFT, + .min_field_value = ID_AA64MMFR1_TIDCP1_IMP, + .matches = has_cpuid_feature, + .cpu_enable = cpu_trap_el0_impdef, + }, {}, }; diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index a590cabe890c5..c44cd3b076e9a 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -34,6 +34,7 @@ HAS_RNG HAS_SB HAS_STAGE2_FWB HAS_SYSREG_GIC_CPUIF +HAS_TIDCP1 HAS_TLB_RANGE HAS_VIRT_HOST_EXTN HW_DBM From fb269457dc49285f29dd088caec7dd75132b933f Mon Sep 17 00:00:00 2001 From: Joey Gouly Date: Fri, 4 Jul 2025 13:57:45 +0800 Subject: [PATCH 154/227] arm64: add HWCAP for FEAT_HBC (hinted conditional branches) Add a HWCAP for FEAT_HBC, so that userspace can make a decision on using this feature. Signed-off-by: Joey Gouly Cc: Catalin Marinas Cc: Will Deacon Link: https://lore.kernel.org/r/20230804143746.3900803-2-joey.gouly@arm.com Signed-off-by: Will Deacon Signed-off-by: Qi Xi --- arch/arm64/include/asm/hwcap.h | 1 + arch/arm64/include/uapi/asm/hwcap.h | 1 + arch/arm64/kernel/cpufeature.c | 3 ++- arch/arm64/kernel/cpuinfo.c | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h index f68fbb2074730..2155094ef9f73 100644 --- a/arch/arm64/include/asm/hwcap.h +++ b/arch/arm64/include/asm/hwcap.h @@ -108,6 +108,7 @@ #define KERNEL_HWCAP_ECV __khwcap2_feature(ECV) #define KERNEL_HWCAP_AFP __khwcap2_feature(AFP) #define KERNEL_HWCAP_RPRES __khwcap2_feature(RPRES) +#define KERNEL_HWCAP_HBC __khwcap2_feature(HBC) /* * This yields a mask that user programs can use to figure out what diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h index f03731847d9df..0a3df77b8b649 100644 --- a/arch/arm64/include/uapi/asm/hwcap.h +++ b/arch/arm64/include/uapi/asm/hwcap.h @@ -78,5 +78,6 @@ #define HWCAP2_ECV (1 << 19) #define HWCAP2_AFP (1 << 20) #define HWCAP2_RPRES (1 << 21) +#define HWCAP2_HBC (1UL << 22) #endif /* _UAPI__ASM_HWCAP_H */ diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index ef57a8fbc4833..faf61556f5b1e 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -232,7 +232,7 @@ static const struct arm64_ftr_bits ftr_id_aa64isar1[] = { }; static const struct arm64_ftr_bits ftr_id_aa64isar2[] = { - ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_HIGHER_SAFE, ID_AA64ISAR2_CLEARBHB_SHIFT, 4, 0), + ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_SAFE, ID_AA64ISAR2_CLEARBHB_SHIFT, 4, 0), ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64ISAR2_RPRES_SHIFT, 4, 0), ARM64_FTR_END, }; @@ -2524,6 +2524,7 @@ static const struct arm64_cpu_capabilities arm64_elf_hwcaps[] = { HWCAP_CAP(SYS_ID_AA64MMFR0_EL1, ID_AA64MMFR0_ECV_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_ECV), HWCAP_CAP(SYS_ID_AA64MMFR1_EL1, ID_AA64MMFR1_AFP_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_AFP), HWCAP_CAP(SYS_ID_AA64ISAR2_EL1, ID_AA64ISAR2_RPRES_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_RPRES), + HWCAP_CAP(SYS_ID_AA64ISAR2_EL1, ID_AA64ISAR2_CLEARBHB_SHIFT, FTR_UNSIGNED, 1, CAP_HWCAP, KERNEL_HWCAP_HBC), {}, }; diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index d72898af05b4c..04a7d2a974ce7 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -97,6 +97,7 @@ static const char *const hwcap_str[] = { [KERNEL_HWCAP_ECV] = "ecv", [KERNEL_HWCAP_AFP] = "afp", [KERNEL_HWCAP_RPRES] = "rpres", + [KERNEL_HWCAP_HBC] = "hbc", }; #ifdef CONFIG_COMPAT From 8870088af2fe954980a687cda193229231617a78 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jul 2025 13:57:46 +0800 Subject: [PATCH 155/227] arm64: Add feature detection for fine grained traps In order to allow us to have shared code for managing fine grained traps for KVM guests add it as a detected feature rather than relying on it being a dependency of other features. Acked-by: Catalin Marinas Reviewed-by: Eric Auger Signed-off-by: Mark Brown [maz: converted to ARM64_CPUID_FIELDS()] Link: https://lore.kernel.org/r/20230301-kvm-arm64-fgt-v4-1-1bf8d235ac1f@kernel.org Reviewed-by: Zenghui Yu Reviewed-by: Miguel Luis Reviewed-by: Jing Zhang Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20230815183903.2735724-10-maz@kernel.org Signed-off-by: Qi Xi --- arch/arm64/include/asm/sysreg.h | 1 + arch/arm64/kernel/cpufeature.c | 10 ++++++++++ arch/arm64/tools/cpucaps | 1 + 3 files changed, 12 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 227085e91344f..8609fa094bc48 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -743,6 +743,7 @@ #define ID_AA64ISAR0_TLB_RANGE 0x2 /* id_aa64isar1 */ +#define ID_AA64MMFR0_EL1_FGT_SHIFT 56 #define ID_AA64ISAR1_I8MM_SHIFT 52 #define ID_AA64ISAR1_DGH_SHIFT 48 #define ID_AA64ISAR1_BF16_SHIFT 44 diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index faf61556f5b1e..e48894fb575d7 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -2391,6 +2391,16 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_cpuid_feature, .min_field_value = 1, }, + { + .desc = "Fine Grained Traps", + .capability = ARM64_HAS_FGT, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .sys_reg = SYS_ID_AA64MMFR0_EL1, + .sign = FTR_UNSIGNED, + .field_pos = ID_AA64MMFR0_EL1_FGT_SHIFT, + .matches = has_cpuid_feature, + .min_field_value = 1, + }, { .desc = "Trap EL0 IMPLEMENTATION DEFINED functionality", .capability = ARM64_HAS_TIDCP1, diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index c44cd3b076e9a..07891af8aa94a 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -20,6 +20,7 @@ HAS_DCPOP HAS_E0PD HAS_ECV HAS_EPAN +HAS_FGT HAS_GENERIC_AUTH HAS_GENERIC_AUTH_ARCH HAS_GENERIC_AUTH_IMP_DEF From dfe7f88b394467806a24f0cf3778d8f15d6af195 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:56 +0200 Subject: [PATCH 156/227] xhci: remove xhci_test_trb_in_td_math early development check Time to remove this test trb in td math check that was added in early stage of xhci driver development. It verified that the size, alignment and boundaries of the event and command rings allocated by the driver itself are correct. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-3-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 160 ------------------------------------ 1 file changed, 160 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index f9e3aed40984b..f4bec72be362f 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1941,164 +1941,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->usb3_rhub.bus_state.bus_suspended = 0; } -static int xhci_test_trb_in_td(struct xhci_hcd *xhci, - struct xhci_segment *input_seg, - union xhci_trb *start_trb, - union xhci_trb *end_trb, - dma_addr_t input_dma, - struct xhci_segment *result_seg, - char *test_name, int test_number) -{ - unsigned long long start_dma; - unsigned long long end_dma; - struct xhci_segment *seg; - - start_dma = xhci_trb_virt_to_dma(input_seg, start_trb); - end_dma = xhci_trb_virt_to_dma(input_seg, end_trb); - - seg = trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, false); - if (seg != result_seg) { - xhci_warn(xhci, "WARN: %s TRB math test %d failed!\n", - test_name, test_number); - xhci_warn(xhci, "Tested TRB math w/ seg %p and " - "input DMA 0x%llx\n", - input_seg, - (unsigned long long) input_dma); - xhci_warn(xhci, "starting TRB %p (0x%llx DMA), " - "ending TRB %p (0x%llx DMA)\n", - start_trb, start_dma, - end_trb, end_dma); - xhci_warn(xhci, "Expected seg %p, got seg %p\n", - result_seg, seg); - trb_in_td(xhci, input_seg, start_trb, end_trb, input_dma, - true); - return -1; - } - return 0; -} - -/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */ -static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci) -{ - struct { - dma_addr_t input_dma; - struct xhci_segment *result_seg; - } simple_test_vector [] = { - /* A zeroed DMA field should fail */ - { 0, NULL }, - /* One TRB before the ring start should fail */ - { xhci->event_ring->first_seg->dma - 16, NULL }, - /* One byte before the ring start should fail */ - { xhci->event_ring->first_seg->dma - 1, NULL }, - /* Starting TRB should succeed */ - { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg }, - /* Ending TRB should succeed */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16, - xhci->event_ring->first_seg }, - /* One byte after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 1)*16 + 1, NULL }, - /* One TRB after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT)*16, NULL }, - /* An address of all ones should fail */ - { (dma_addr_t) (~0), NULL }, - }; - struct { - struct xhci_segment *input_seg; - union xhci_trb *start_trb; - union xhci_trb *end_trb; - dma_addr_t input_dma; - struct xhci_segment *result_seg; - } complex_test_vector [] = { - /* Test feeding a valid DMA address from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* Test feeding a valid end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* Test feeding a valid start and end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->cmd_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg = NULL, - }, - /* TRB in this ring, but after this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[0], - .end_trb = &xhci->event_ring->first_seg->trbs[3], - .input_dma = xhci->event_ring->first_seg->dma + 4*16, - .result_seg = NULL, - }, - /* TRB in this ring, but before this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[3], - .end_trb = &xhci->event_ring->first_seg->trbs[6], - .input_dma = xhci->event_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - /* TRB in this ring, but after this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->event_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - /* TRB in this ring, but before this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT - 4)*16, - .result_seg = NULL, - }, - /* TRB not in this ring, and we have a wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 3], - .end_trb = &xhci->event_ring->first_seg->trbs[1], - .input_dma = xhci->cmd_ring->first_seg->dma + 2*16, - .result_seg = NULL, - }, - }; - - unsigned int num_tests; - int i, ret; - - num_tests = ARRAY_SIZE(simple_test_vector); - for (i = 0; i < num_tests; i++) { - ret = xhci_test_trb_in_td(xhci, - xhci->event_ring->first_seg, - xhci->event_ring->first_seg->trbs, - &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT - 1], - simple_test_vector[i].input_dma, - simple_test_vector[i].result_seg, - "Simple", i); - if (ret < 0) - return ret; - } - - num_tests = ARRAY_SIZE(complex_test_vector); - for (i = 0; i < num_tests; i++) { - ret = xhci_test_trb_in_td(xhci, - complex_test_vector[i].input_seg, - complex_test_vector[i].start_trb, - complex_test_vector[i].end_trb, - complex_test_vector[i].input_dma, - complex_test_vector[i].result_seg, - "Complex", i); - if (ret < 0) - return ret; - } - xhci_dbg(xhci, "TRB math tests passed.\n"); - return 0; -} - static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) { u64 temp; @@ -2549,8 +2391,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) 0, flags); if (!xhci->event_ring) goto fail; - if (xhci_check_trb_in_td_math(xhci) < 0) - goto fail; ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); if (ret) From 26318ad445d6fb7604b9c2ad3dee0f5b5699b25d Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:57 +0200 Subject: [PATCH 157/227] xhci: Refactor interrupter code for initial multi interrupter support. xHC supports several interrupters, each with its own mmio register set, event ring and MSI/MSI-X vector. Transfers can be assigned different interrupters when queued. See xhci 4.17 for details. Current driver only supports one interrupter. Create a xhci_interrupter structure containing an event ring, pointer to mmio registers for this interrupter, variables to store registers over s3 suspend, erst, etc. Add functions to create and free an interrupter, and pass an interrupter pointer to functions that deal with events. Secondary interrupters are also useful without having an interrupt vector. One use case is the xHCI audio sideband offloading where a DSP can take care of specific audio endpoints. When all transfer events of an offloaded endpoint can be mapped to a separate interrupter event ring the DSP can poll this ring, and we can mask these events preventing waking up the CPU. Only minor functional changes such as clearing some of the interrupter registers when freeing the interrupter. Still create only one primary interrupter. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 168 +++++++++++++++++++++----------- drivers/usb/host/xhci-ring.c | 68 +++++++------ drivers/usb/host/xhci.c | 54 ++++++---- drivers/usb/host/xhci.h | 24 +++-- 5 files changed, 196 insertions(+), 120 deletions(-) diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c index bd40caeeb21c6..99baa60ef50fe 100644 --- a/drivers/usb/host/xhci-debugfs.c +++ b/drivers/usb/host/xhci-debugfs.c @@ -693,7 +693,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci) "command-ring", xhci->debugfs_root); - xhci_debugfs_create_ring_dir(xhci, &xhci->event_ring, + xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring, "event-ring", xhci->debugfs_root); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index f4bec72be362f..bb5b8f20368d9 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1828,17 +1828,43 @@ int xhci_alloc_erst(struct xhci_hcd *xhci, return 0; } -void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) +static void +xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { - size_t size; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + size_t erst_size; + u64 tmp64; + u32 tmp; - size = sizeof(struct xhci_erst_entry) * (erst->num_entries); - if (erst->entries) - dma_free_coherent(dev, size, - erst->entries, - erst->erst_dma_addr); - erst->entries = NULL; + if (!ir) + return; + + erst_size = sizeof(struct xhci_erst_entry) * (ir->erst.num_entries); + if (ir->erst.entries) + dma_free_coherent(dev, erst_size, + ir->erst.entries, + ir->erst.erst_dma_addr); + ir->erst.entries = NULL; + + /* + * Clean out interrupter registers except ERSTBA. Clearing either the + * low or high 32 bits of ERSTBA immediately causes the controller to + * dereference the partially cleared 64 bit address, causing IOMMU error. + */ + tmp = readl(&ir->ir_set->erst_size); + tmp &= ERST_SIZE_MASK; + writel(tmp, &ir->ir_set->erst_size); + + tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + tmp64 &= (u64) ERST_PTR_MASK; + xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + + /* free interrrupter event ring */ + if (ir->event_ring) + xhci_ring_free(xhci, ir->event_ring); + ir->event_ring = NULL; + + kfree(ir); } void xhci_mem_cleanup(struct xhci_hcd *xhci) @@ -1848,12 +1874,9 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) cancel_delayed_work_sync(&xhci->cmd_timer); - xhci_free_erst(xhci, &xhci->erst); - - if (xhci->event_ring) - xhci_ring_free(xhci, xhci->event_ring); - xhci->event_ring = NULL; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring"); + xhci_free_interrupter(xhci, xhci->interrupter); + xhci->interrupter = NULL; + xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring"); if (xhci->lpm_command) xhci_free_command(xhci, xhci->lpm_command); @@ -1941,18 +1964,18 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->usb3_rhub.bus_state.bus_suspended = 0; } -static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) +static void xhci_set_hc_event_deq(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { u64 temp; dma_addr_t deq; - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); + deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue); if (!deq) xhci_warn(xhci, "WARN something wrong with SW event ring " "dequeue ptr.\n"); /* Update HC event ring dequeue pointer */ - temp = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp &= ERST_PTR_MASK; /* Don't clear the EHB bit (which is RW1C) because * there might be more events to service. @@ -1962,7 +1985,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) "// Write event ring dequeue pointer, " "preserving EHB bit"); xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp, - &xhci->ir_set->erst_dequeue); + &ir->ir_set->erst_dequeue); } static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, @@ -2248,6 +2271,68 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) return 0; } +static struct xhci_interrupter * +xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags) +{ + struct device *dev = xhci_to_hcd(xhci)->self.sysdev; + struct xhci_interrupter *ir; + u64 erst_base; + u32 erst_size; + int ret; + + if (intr_num > xhci->max_interrupters) { + xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n", + intr_num, xhci->max_interrupters); + return NULL; + } + + if (xhci->interrupter) { + xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num); + return NULL; + } + + ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); + if (!ir) + return NULL; + + ir->ir_set = &xhci->run_regs->ir_set[intr_num]; + ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, + 0, flags); + if (!ir->event_ring) { + xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num); + goto fail_ir; + } + + ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags); + if (ret) { + xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num); + goto fail_ev; + + } + /* set ERST count with the number of entries in the segment table */ + erst_size = readl(&ir->ir_set->erst_size); + erst_size &= ERST_SIZE_MASK; + erst_size |= ERST_NUM_SEGS; + writel(erst_size, &ir->ir_set->erst_size); + + erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + erst_base &= ERST_PTR_MASK; + erst_base |= (ir->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); + xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base); + + /* Set the event ring dequeue address of this interrupter */ + xhci_set_hc_event_deq(xhci, ir); + + return ir; + +fail_ev: + xhci_ring_free(xhci, ir->event_ring); +fail_ir: + kfree(ir); + + return NULL; +} + int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) { dma_addr_t dma; @@ -2255,7 +2340,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) unsigned int val, val2; u64 val_64; u32 page_size, temp; - int i, ret; + int i; INIT_LIST_HEAD(&xhci->cmd_list); @@ -2380,46 +2465,13 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) " from cap regs base addr", val); xhci->dba = (void __iomem *) xhci->cap_regs + val; /* Set ir_set to interrupt register set 0 */ - xhci->ir_set = &xhci->run_regs->ir_set[0]; - - /* - * Event ring setup: Allocate a normal ring, but also setup - * the event ring segment table (ERST). Section 4.9.3. - */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Allocating event ring"); - xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, - 0, flags); - if (!xhci->event_ring) - goto fail; - - ret = xhci_alloc_erst(xhci, xhci->event_ring, &xhci->erst, flags); - if (ret) - goto fail; - - /* set ERST count with the number of entries in the segment table */ - val = readl(&xhci->ir_set->erst_size); - val &= ERST_SIZE_MASK; - val |= ERST_NUM_SEGS; - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Write ERST size = %i to ir_set 0 (some bits preserved)", - val); - writel(val, &xhci->ir_set->erst_size); + /* allocate and set up primary interrupter with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST entries to point to event ring."); - /* set the segment table base address */ - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "// Set ERST base address for ir_set 0 = 0x%llx", - (unsigned long long)xhci->erst.erst_dma_addr); - val_64 = xhci_read_64(xhci, &xhci->ir_set->erst_base); - val_64 &= ERST_PTR_MASK; - val_64 |= (xhci->erst.erst_dma_addr & (u64) ~ERST_PTR_MASK); - xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base); - - /* Set the event ring dequeue address */ - xhci_set_hc_event_deq(xhci); - xhci_dbg_trace(xhci, trace_xhci_dbg_init, - "Wrote ERST address to ir_set 0."); + "Allocating primary event ring"); + xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags); + if (!xhci->interrupter) + goto fail; xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3caa09dc64dbc..fc0aa06ab3091 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1900,7 +1900,8 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci) } static void handle_port_status(struct xhci_hcd *xhci, - union xhci_trb *event) + struct xhci_interrupter *ir, + union xhci_trb *event) { struct usb_hcd *hcd; u32 port_id; @@ -1923,7 +1924,7 @@ static void handle_port_status(struct xhci_hcd *xhci, if ((port_id <= 0) || (port_id > max_ports)) { xhci_warn(xhci, "Port change event with invalid port ID %d\n", port_id); - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); return; } @@ -2052,7 +2053,7 @@ static void handle_port_status(struct xhci_hcd *xhci, cleanup: /* Update event ring dequeue pointer before dropping the lock */ - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* Don't make the USB core poll the roothub if we got a bad port status * change event. Besides, at that point we can't tell which roothub @@ -2614,7 +2615,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, * At this point, the host controller is probably hosed and should be reset. */ static int handle_tx_event(struct xhci_hcd *xhci, - struct xhci_transfer_event *event) + struct xhci_interrupter *ir, + struct xhci_transfer_event *event) { struct xhci_virt_ep *ep; struct xhci_ring *ep_ring; @@ -3000,7 +3002,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, * processing missed tds. */ if (!handling_skipped_tds) - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* * If ep->skip is set, it means there are missed tds on the @@ -3015,8 +3017,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, err_out: xhci_err(xhci, "@%016llx %08x %08x %08x %08x\n", (unsigned long long) xhci_trb_virt_to_dma( - xhci->event_ring->deq_seg, - xhci->event_ring->dequeue), + ir->event_ring->deq_seg, + ir->event_ring->dequeue), lower_32_bits(le64_to_cpu(event->buffer)), upper_32_bits(le64_to_cpu(event->buffer)), le32_to_cpu(event->transfer_len), @@ -3030,7 +3032,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, * Returns >0 for "possibly more events to process" (caller should call again), * otherwise 0 if done. In future, <0 returns should indicate error code. */ -static int xhci_handle_event(struct xhci_hcd *xhci) +static int xhci_handle_event(struct xhci_hcd *xhci, struct xhci_interrupter *ir) { union xhci_trb *event; int update_ptrs = 1; @@ -3038,18 +3040,18 @@ static int xhci_handle_event(struct xhci_hcd *xhci) int ret; /* Event ring hasn't been allocated yet. */ - if (!xhci->event_ring || !xhci->event_ring->dequeue) { - xhci_err(xhci, "ERROR event ring not ready\n"); + if (!ir || !ir->event_ring || !ir->event_ring->dequeue) { + xhci_err(xhci, "ERROR interrupter not ready\n"); return -ENOMEM; } - event = xhci->event_ring->dequeue; + event = ir->event_ring->dequeue; /* Does the HC or OS own the TRB? */ if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != - xhci->event_ring->cycle_state) + ir->event_ring->cycle_state) return 0; - trace_xhci_handle_event(xhci->event_ring, &event->generic); + trace_xhci_handle_event(ir->event_ring, &event->generic); /* * Barrier between reading the TRB_CYCLE (valid) flag above and any @@ -3064,11 +3066,11 @@ static int xhci_handle_event(struct xhci_hcd *xhci) handle_cmd_completion(xhci, &event->event_cmd); break; case TRB_PORT_STATUS: - handle_port_status(xhci, event); + handle_port_status(xhci, ir, event); update_ptrs = 0; break; case TRB_TRANSFER: - ret = handle_tx_event(xhci, &event->trans_event); + ret = handle_tx_event(xhci, ir, &event->trans_event); if (ret >= 0) update_ptrs = 0; break; @@ -3092,7 +3094,7 @@ static int xhci_handle_event(struct xhci_hcd *xhci) if (update_ptrs) /* Update SW event ring dequeue pointer */ - inc_deq(xhci, xhci->event_ring); + inc_deq(xhci, ir->event_ring); /* Are there more items on the event ring? Caller will call us again to * check. @@ -3106,16 +3108,17 @@ static int xhci_handle_event(struct xhci_hcd *xhci) * - To avoid "Event Ring Full Error" condition */ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, - union xhci_trb *event_ring_deq) + struct xhci_interrupter *ir, + union xhci_trb *event_ring_deq) { u64 temp_64; dma_addr_t deq; - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); /* If necessary, update the HW's version of the event ring deq ptr. */ - if (event_ring_deq != xhci->event_ring->dequeue) { - deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, - xhci->event_ring->dequeue); + if (event_ring_deq != ir->event_ring->dequeue) { + deq = xhci_trb_virt_to_dma(ir->event_ring->deq_seg, + ir->event_ring->dequeue); if (deq == 0) xhci_warn(xhci, "WARN something wrong with SW event ring dequeue ptr\n"); /* @@ -3133,7 +3136,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci, /* Clear the event handler busy flag (RW1C) */ temp_64 |= ERST_EHB; - xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue); + xhci_write_64(xhci, temp_64, &ir->ir_set->erst_dequeue); } /* @@ -3145,6 +3148,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); union xhci_trb *event_ring_deq; + struct xhci_interrupter *ir; irqreturn_t ret = IRQ_NONE; u64 temp_64; u32 status; @@ -3177,11 +3181,13 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) status |= STS_EINT; writel(status, &xhci->op_regs->status); + /* This is the handler of the primary interrupter */ + ir = xhci->interrupter; if (!hcd->msi_enabled) { u32 irq_pending; - irq_pending = readl(&xhci->ir_set->irq_pending); + irq_pending = readl(&ir->ir_set->irq_pending); irq_pending |= IMAN_IP; - writel(irq_pending, &xhci->ir_set->irq_pending); + writel(irq_pending, &ir->ir_set->irq_pending); } if (xhci->xhc_state & XHCI_STATE_DYING || @@ -3191,22 +3197,22 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) /* Clear the event handler busy flag (RW1C); * the event ring should be empty. */ - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); xhci_write_64(xhci, temp_64 | ERST_EHB, - &xhci->ir_set->erst_dequeue); + &ir->ir_set->erst_dequeue); ret = IRQ_HANDLED; goto out; } - event_ring_deq = xhci->event_ring->dequeue; + event_ring_deq = ir->event_ring->dequeue; /* FIXME this should be a delayed service routine * that clears the EHB. */ - while (xhci_handle_event(xhci) > 0) { + while (xhci_handle_event(xhci, ir) > 0) { if (event_loop++ < TRBS_PER_SEGMENT / 2) continue; - xhci_update_erst_dequeue(xhci, event_ring_deq); - event_ring_deq = xhci->event_ring->dequeue; + xhci_update_erst_dequeue(xhci, ir, event_ring_deq); + event_ring_deq = ir->event_ring->dequeue; /* ring is half-full, force isoc trbs to interrupt more often */ if (xhci->isoc_bei_interval > AVOID_BEI_INTERVAL_MIN) @@ -3215,7 +3221,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) event_loop = 0; } - xhci_update_erst_dequeue(xhci, event_ring_deq); + xhci_update_erst_dequeue(xhci, ir, event_ring_deq); ret = IRQ_HANDLED; out: diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5c9d3be136d2c..fb0df5df7f9e0 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -611,6 +611,7 @@ static int xhci_init(struct usb_hcd *hcd) static int xhci_run_finished(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; unsigned long flags; u32 temp; @@ -626,8 +627,8 @@ static int xhci_run_finished(struct xhci_hcd *xhci) writel(temp, &xhci->op_regs->command); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending); if (xhci_start(xhci)) { xhci_halt(xhci); @@ -666,7 +667,7 @@ int xhci_run(struct usb_hcd *hcd) u64 temp_64; int ret; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - + struct xhci_interrupter *ir = xhci->interrupter; /* Start the xHCI host controller running only after the USB 2.0 roothub * is setup. */ @@ -681,17 +682,17 @@ int xhci_run(struct usb_hcd *hcd) if (ret) return ret; - temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); + temp_64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); temp_64 &= ~ERST_PTR_MASK; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "ERST deq = 64'h%0lx", (long unsigned int) temp_64); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Set the interrupt modulation register"); - temp = readl(&xhci->ir_set->irq_control); + temp = readl(&ir->ir_set->irq_control); temp &= ~ER_IRQ_INTERVAL_MASK; temp |= (xhci->imod_interval / 250) & ER_IRQ_INTERVAL_MASK; - writel(temp, &xhci->ir_set->irq_control); + writel(temp, &ir->ir_set->irq_control); if (xhci->quirks & XHCI_NEC_HOST) { struct xhci_command *command; @@ -767,8 +768,8 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&xhci->interrupter->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -830,28 +831,36 @@ EXPORT_SYMBOL_GPL(xhci_shutdown); #ifdef CONFIG_PM static void xhci_save_registers(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; + xhci->s3.command = readl(&xhci->op_regs->command); xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification); xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci->s3.config_reg = readl(&xhci->op_regs->config_reg); - xhci->s3.erst_size = readl(&xhci->ir_set->erst_size); - xhci->s3.erst_base = xhci_read_64(xhci, &xhci->ir_set->erst_base); - xhci->s3.erst_dequeue = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); - xhci->s3.irq_pending = readl(&xhci->ir_set->irq_pending); - xhci->s3.irq_control = readl(&xhci->ir_set->irq_control); + + if (!ir) + return; + + ir->s3_erst_size = readl(&ir->ir_set->erst_size); + ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base); + ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + ir->s3_irq_pending = readl(&ir->ir_set->irq_pending); + ir->s3_irq_control = readl(&ir->ir_set->irq_control); } static void xhci_restore_registers(struct xhci_hcd *xhci) { + struct xhci_interrupter *ir = xhci->interrupter; + writel(xhci->s3.command, &xhci->op_regs->command); writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification); xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr); writel(xhci->s3.config_reg, &xhci->op_regs->config_reg); - writel(xhci->s3.erst_size, &xhci->ir_set->erst_size); - xhci_write_64(xhci, xhci->s3.erst_base, &xhci->ir_set->erst_base); - xhci_write_64(xhci, xhci->s3.erst_dequeue, &xhci->ir_set->erst_dequeue); - writel(xhci->s3.irq_pending, &xhci->ir_set->irq_pending); - writel(xhci->s3.irq_control, &xhci->ir_set->irq_control); + writel(ir->s3_erst_size, &ir->ir_set->erst_size); + xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base); + xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue); + writel(ir->s3_irq_pending, &ir->ir_set->irq_pending); + writel(ir->s3_irq_control, &ir->ir_set->irq_control); } static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci) @@ -1212,8 +1221,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); + temp = readl(&xhci->interrupter->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -5345,6 +5354,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) if (xhci->hci_version > 0x100) xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); + /* xhci-plat or xhci-pci might have set max_interrupters already */ + if ((!xhci->max_interrupters) || + xhci->max_interrupters > HCS_MAX_INTRS(xhci->hcs_params1)) + xhci->max_interrupters = HCS_MAX_INTRS(xhci->hcs_params1); + xhci->quirks |= quirks; get_quirks(dev, xhci); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 60e5e30e428e7..c2a4bca35d98e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1687,11 +1687,6 @@ struct s3_save { u32 dev_nt; u64 dcbaa_ptr; u32 config_reg; - u32 irq_pending; - u32 irq_control; - u32 erst_size; - u64 erst_base; - u64 erst_dequeue; }; /* Use for lpm */ @@ -1713,7 +1708,18 @@ struct xhci_bus_state { unsigned long resuming_ports; }; - +struct xhci_interrupter { + struct xhci_ring *event_ring; + struct xhci_erst erst; + struct xhci_intr_reg __iomem *ir_set; + unsigned int intr_num; + /* For interrupter registers save and restore over suspend/resume */ + u32 s3_irq_pending; + u32 s3_irq_control; + u32 s3_erst_size; + u64 s3_erst_base; + u64 s3_erst_dequeue; +}; /* * It can take up to 20 ms to transition from RExit to U0 on the * Intel Lynx Point LP xHCI host. @@ -1760,8 +1766,6 @@ struct xhci_hcd { struct xhci_op_regs __iomem *op_regs; struct xhci_run_regs __iomem *run_regs; struct xhci_doorbell_array __iomem *dba; - /* Our HCD's current interrupter register set */ - struct xhci_intr_reg __iomem *ir_set; /* Cached register copies of read-only HC data */ __u32 hcs_params1; @@ -1796,6 +1800,7 @@ struct xhci_hcd { struct reset_control *reset; /* data structures */ struct xhci_device_context_array *dcbaa; + struct xhci_interrupter *interrupter; struct xhci_ring *cmd_ring; unsigned int cmd_ring_state; #define CMD_RING_STATE_RUNNING (1 << 0) @@ -1806,8 +1811,7 @@ struct xhci_hcd { struct delayed_work cmd_timer; struct completion cmd_ring_stop_completion; struct xhci_command *current_cmd; - struct xhci_ring *event_ring; - struct xhci_erst erst; + /* Scratchpad */ struct xhci_scratchpad *scratchpad; /* Store LPM test failed devices' information */ From 26e48f1994edd929ae9b94e7676252a75931c466 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 2 Feb 2023 17:04:58 +0200 Subject: [PATCH 158/227] xhci: add helpers for enabling and disabling interrupters Simple helpers to set and clear the IE (interrupter enable) bit for an interrupter. Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20230202150505.618915-5-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index fb0df5df7f9e0..809419fe22e6c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -293,6 +293,32 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) xhci_info(xhci, "Fault detected\n"); } +static int xhci_enable_interrupter(struct xhci_interrupter *ir) +{ + u32 iman; + + if (!ir || !ir->ir_set) + return -EINVAL; + + iman = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_ENABLE(iman), &ir->ir_set->irq_pending); + + return 0; +} + +static int xhci_disable_interrupter(struct xhci_interrupter *ir) +{ + u32 iman; + + if (!ir || !ir->ir_set) + return -EINVAL; + + iman = readl(&ir->ir_set->irq_pending); + writel(ER_IRQ_DISABLE(iman), &ir->ir_set->irq_pending); + + return 0; +} + #ifdef CONFIG_USB_PCI /* * Set up MSI @@ -608,7 +634,6 @@ static int xhci_init(struct usb_hcd *hcd) /*-------------------------------------------------------------------------*/ - static int xhci_run_finished(struct xhci_hcd *xhci) { struct xhci_interrupter *ir = xhci->interrupter; @@ -627,8 +652,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci) writel(temp, &xhci->op_regs->command); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Enable primary interrupter"); - temp = readl(&ir->ir_set->irq_pending); - writel(ER_IRQ_ENABLE(temp), &ir->ir_set->irq_pending); + xhci_enable_interrupter(ir); if (xhci_start(xhci)) { xhci_halt(xhci); @@ -732,6 +756,7 @@ static void xhci_stop(struct usb_hcd *hcd) { u32 temp; struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_interrupter *ir = xhci->interrupter; mutex_lock(&xhci->mutex); @@ -768,8 +793,7 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->interrupter->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); + xhci_disable_interrupter(ir); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "cleaning up memory"); xhci_mem_cleanup(xhci); @@ -1221,8 +1245,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) xhci_dbg(xhci, "// Disabling event ring interrupts\n"); temp = readl(&xhci->op_regs->status); writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status); - temp = readl(&xhci->interrupter->ir_set->irq_pending); - writel(ER_IRQ_DISABLE(temp), &xhci->interrupter->ir_set->irq_pending); + xhci_disable_interrupter(xhci->interrupter); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); From 76b9efb248f96efa2ee07d849ac7441986240910 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 10 Apr 2025 18:18:27 +0300 Subject: [PATCH 159/227] xhci: Limit time spent with xHC interrupts disabled during bus resume mainline inclusion from mainline-v6.15-rc1 commit bea5892d0ed274e03655223d1977cf59f9aff2f2 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IC6D3S CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=bea5892d0ed274e03655223d1977cf59f9aff2f2 ---------------------------------------------------------------------- Current xhci bus resume implementation prevents xHC host from generating interrupts during high-speed USB 2 and super-speed USB 3 bus resume. Only reason to disable interrupts during bus resume would be to prevent the interrupt handler from interfering with the resume process of USB 2 ports. Host initiated resume of USB 2 ports is done in two stages. The xhci driver first transitions the port from 'U3' to 'Resume' state, then wait in Resume for 20ms, and finally moves port to U0 state. xhci driver can't prevent interrupts by keeping the xhci spinlock due to this 20ms sleep. Limit interrupt disabling to the USB 2 port resume case only. resuming USB 2 ports in bus resume is only done in special cases where USB 2 ports had to be forced to suspend during bus suspend. The current way of preventing interrupts by clearing the 'Interrupt Enable' (INTE) bit in USBCMD register won't prevent the Interrupter registers 'Interrupt Pending' (IP), 'Event Handler Busy' (EHB) and USBSTS register Event Interrupt (EINT) bits from being set. New interrupts can't be issued before those bits are properly clered. Disable interrupts by clearing the interrupter register 'Interrupt Enable' (IE) bit instead. This way IP, EHB and INTE won't be set before IE is enabled again and a new interrupt is triggered. Fixes: 52dd0483e822 ("xhci: add helpers for enabling and disabling interrupters") Reported-by: Devyn Liu Closes: https://lore.kernel.org/linux-usb/b1a9e2d51b4d4ff7a304f77c5be8164e@huawei.com/ Cc: stable@vger.kernel.org Tested-by: Devyn Liu Signed-off-by: Mathias Nyman Link: https://lore.kernel.org/r/20250410151828.2868740-6-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman Signed-off-by: lujunhua --- drivers/usb/host/xhci-hub.c | 30 ++++++++++++++++-------------- drivers/usb/host/xhci.c | 4 ++-- drivers/usb/host/xhci.h | 2 ++ 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 723201394eaa8..355ed8a56a5d8 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1861,9 +1861,10 @@ int xhci_bus_resume(struct usb_hcd *hcd) int slot_id; int sret; u32 next_state; - u32 temp, portsc; + u32 portsc; struct xhci_hub *rhub; struct xhci_port **ports; + bool disabled_irq = false; rhub = xhci_get_rhub(hcd); ports = rhub->ports; @@ -1879,17 +1880,20 @@ int xhci_bus_resume(struct usb_hcd *hcd) return -ESHUTDOWN; } - /* delay the irqs */ - temp = readl(&xhci->op_regs->command); - temp &= ~CMD_EIE; - writel(temp, &xhci->op_regs->command); - /* bus specific resume for ports we suspended at bus_suspend */ - if (hcd->speed >= HCD_USB3) + if (hcd->speed >= HCD_USB3) { next_state = XDEV_U0; - else + } else { next_state = XDEV_RESUME; - + if (bus_state->bus_suspended) { + /* + * prevent port event interrupts from interfering + * with usb2 port resume process + */ + xhci_disable_interrupter(xhci->interrupter); + disabled_irq = true; + } + } port_index = max_ports; while (port_index--) { portsc = readl(ports[port_index]->addr); @@ -1958,11 +1962,9 @@ int xhci_bus_resume(struct usb_hcd *hcd) (void) readl(&xhci->op_regs->command); bus_state->next_statechange = jiffies + msecs_to_jiffies(5); - /* re-enable irqs */ - temp = readl(&xhci->op_regs->command); - temp |= CMD_EIE; - writel(temp, &xhci->op_regs->command); - temp = readl(&xhci->op_regs->command); + /* re-enable interrupter */ + if (disabled_irq) + xhci_enable_interrupter(xhci->interrupter); spin_unlock_irqrestore(&xhci->lock, flags); return 0; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 809419fe22e6c..8e8df1a613c96 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -293,7 +293,7 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci) xhci_info(xhci, "Fault detected\n"); } -static int xhci_enable_interrupter(struct xhci_interrupter *ir) +int xhci_enable_interrupter(struct xhci_interrupter *ir) { u32 iman; @@ -306,7 +306,7 @@ static int xhci_enable_interrupter(struct xhci_interrupter *ir) return 0; } -static int xhci_disable_interrupter(struct xhci_interrupter *ir) +int xhci_disable_interrupter(struct xhci_interrupter *ir) { u32 iman; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index c2a4bca35d98e..8e67c190fa4e3 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2129,6 +2129,8 @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci, struct xhci_virt_device *virt_dev, struct usb_device *hdev, struct usb_tt *tt, gfp_t mem_flags); +int xhci_enable_interrupter(struct xhci_interrupter *ir); +int xhci_disable_interrupter(struct xhci_interrupter *ir); /* xHCI ring, segment, TRB, and TD functions */ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb); From e60691f1b4977b5f1c8d086fe46b587d2d7ec9f7 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 4 Jul 2025 16:44:36 +0800 Subject: [PATCH 160/227] arm64/sysreg: Update ID_AA64MMFR1_EL1 register mainline inclusion from mainline-v6.13-rc1 commit aa47dcda2708e571695dae2e3f9537d9a8eb804c category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB4YD4 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=aa47dcda2708e571695dae2e3f9537d9a8eb804c ---------------------------------------------------------------------- Update ID_AA64MMFR1_EL1 register fields definition per DDI0601 (ID092424) 2024-09. ID_AA64MMFR1_EL1.ETS adds definition for FEAT_ETS2 and FEAT_ETS3. ID_AA64MMFR1_EL1.HAFDBS adds definition for FEAT_HAFT and FEAT_HDBSS. Reviewed-by: Mark Brown Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/20241102104235.62560-2-yangyicong@huawei.com Signed-off-by: Catalin Marinas Signed-off-by: JiangShui Yang --- arch/arm64/include/asm/sysreg.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 8609fa094bc48..d728408df058a 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -1073,6 +1073,12 @@ #define ID_PFR1_SECURITY_SHIFT 4 #define ID_PFR1_PROGMOD_SHIFT 0 +#define ID_AA64MMFR1_EL1_ETS_ETS2 UL(0b0010) +#define ID_AA64MMFR1_EL1_ETS_ETS3 UL(0b0011) + +#define ID_AA64MMFR1_EL1_HAFDBS_HAFT UL(0b0011) +#define ID_AA64MMFR1_EL1_HAFDBS_HDBSS UL(0b0100) + #if defined(CONFIG_ARM64_4K_PAGES) #define ID_AA64MMFR0_TGRAN_SHIFT ID_AA64MMFR0_TGRAN4_SHIFT #define ID_AA64MMFR0_TGRAN_SUPPORTED_MIN ID_AA64MMFR0_TGRAN4_SUPPORTED_MIN From 2df99617f5c63ced595e32a6b3423219df0370b8 Mon Sep 17 00:00:00 2001 From: Joey Gouly Date: Fri, 4 Jul 2025 16:44:37 +0800 Subject: [PATCH 161/227] arm64: setup: name 'tcr2' register mainline inclusion from mainline-v6.13-rc1 commit 926b66e2ebc8c055b9fea3fb3e5f5b67c80e8e7a category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB4YD4 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=926b66e2ebc8c055b9fea3fb3e5f5b67c80e8e7a ---------------------------------------------------------------------- TCR2_EL1 introduced some additional controls besides TCR_EL1. Currently only PIE is supported and enabled by writing TCR2_EL1 directly if PIE detected. Introduce a named register 'tcr2' just like 'tcr' we've already had. It'll be initialized to 0 and updated if certain feature detected and needs to be enabled. Touch the TCR2_EL1 registers at last with the updated 'tcr2' value if FEAT_TCR2 supported by checking ID_AA64MMFR3_EL1.TCRX. Then we can extend the support of other features controlled by TCR2_EL1. Reviewed-by: Catalin Marinas Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/20241102104235.62560-3-yangyicong@huawei.com Signed-off-by: Catalin Marinas Conflicts: arch/arm64/mm/proc.S [Context conflicts] Signed-off-by: JiangShui Yang --- arch/arm64/include/asm/sysreg.h | 17 +++++++++++++++++ arch/arm64/mm/proc.S | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index d728408df058a..6f4c09bd3f7e5 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -623,6 +623,23 @@ #define SYS_CNTV_CTL_EL02 sys_reg(3, 5, 14, 3, 1) #define SYS_CNTV_CVAL_EL02 sys_reg(3, 5, 14, 3, 2) +#define REG_ID_AA64MMFR3_EL1 S3_0_C0_C7_3 +#define SYS_ID_AA64MMFR3_EL1 sys_reg(3, 0, 0, 7, 3) +#define ID_AA64MMFR3_EL1_TCRX_SHIFT 0 +#define ID_AA64MMFR3_EL1_S1PIE_SHIFT 8 + +#define REG_TCR2_EL1 S3_0_C2_C0_3 +#define SYS_TCR2_EL1 sys_reg(3, 0, 2, 0, 3) +#define SYS_TCR2_EL1 sys_reg(3, 0, 2, 0, 3) +#define SYS_TCR2_EL1_Op0 3 +#define SYS_TCR2_EL1_Op1 0 +#define SYS_TCR2_EL1_CRn 2 +#define SYS_TCR2_EL1_CRm 0 +#define SYS_TCR2_EL1_Op2 3 + +#define TCR2_EL1x_HAFT GENMASK(11, 11) +#define TCR2_EL1x_PIE GENMASK(1, 1) + /* Common SCTLR_ELx flags. */ #define SCTLR_ELx_DSSBS (BIT(44)) #define SCTLR_ELx_ATA (BIT(43)) diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 1a9684b114745..25194ad4e4614 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -420,10 +420,12 @@ SYM_FUNC_START(__cpu_setup) */ mair .req x17 tcr .req x16 + tcr2 .req x15 mov_q mair, MAIR_EL1_SET mov_q tcr, TCR_TxSZ(VA_BITS) | TCR_CACHE_FLAGS | TCR_SMP_FLAGS | \ TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \ TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS + mov tcr2, xzr tcr_clear_errata_bits tcr, x9, x5 @@ -455,6 +457,21 @@ SYM_FUNC_START(__cpu_setup) #endif /* CONFIG_ARM64_HW_AFDBM */ msr mair_el1, mair msr tcr_el1, tcr + + mrs_s x1, SYS_ID_AA64MMFR3_EL1 + ubfx x1, x1, #ID_AA64MMFR3_EL1_S1PIE_SHIFT, #4 + cbz x1, .Lskip_indirection + + orr tcr2, tcr2, TCR2_EL1x_PIE + +.Lskip_indirection: + + mrs_s x1, SYS_ID_AA64MMFR3_EL1 + ubfx x1, x1, #ID_AA64MMFR3_EL1_TCRX_SHIFT, #4 + cbz x1, 1f + msr REG_TCR2_EL1, tcr2 +1: + /* * Prepare SCTLR */ @@ -463,4 +480,5 @@ SYM_FUNC_START(__cpu_setup) .unreq mair .unreq tcr + .unreq tcr2 SYM_FUNC_END(__cpu_setup) From e6fe98230704d881364a7e472d8dc1ca7372219c Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Fri, 4 Jul 2025 16:44:38 +0800 Subject: [PATCH 162/227] arm64/mm: Replace an open coding with ID_AA64MMFR1_EL1_HAFDBS_MASK Replace '0xf' with ID_AA64MMFR1_EL1_HAFDBS_MASK while evaluating if the cpu supports implicit page table entry access flag update in HW. Cc: Catalin Marinas Cc: Will Deacon Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Anshuman Khandual Link: https://lore.kernel.org/r/20230711090458.238346-1-anshuman.khandual@arm.com Signed-off-by: Will Deacon --- arch/arm64/mm/proc.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 25194ad4e4614..3d3c081e78816 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -450,7 +450,7 @@ SYM_FUNC_START(__cpu_setup) * via capabilities. */ mrs x9, ID_AA64MMFR1_EL1 - and x9, x9, #0xf + and x9, x9, ID_AA64MMFR1_EL1_HAFDBS_MASK cbz x9, 1f orr tcr, tcr, #TCR_HA // hardware Access flag update 1: From 779bd695219e9c567aed9e821185b6cc33ce79f9 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 4 Jul 2025 16:44:39 +0800 Subject: [PATCH 163/227] arm64: Add support for FEAT_HAFT mainline inclusion from mainline-v6.13-rc1 commit efe72541355d4d40a4f076af453f6533e98e058c category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB4YD4 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=efe72541355d4d40a4f076af453f6533e98e058c ---------------------------------------------------------------------- Armv8.9/v9.4 introduces the feature Hardware managed Access Flag for Table descriptors (FEAT_HAFT). The feature is indicated by ID_AA64MMFR1_EL1.HAFDBS == 0b0011 and can be enabled by TCR2_EL1.HAFT so it has a dependency on FEAT_TCR2. Adds the Kconfig for FEAT_HAFT and support detecting and enabling the feature. The feature is enabled in __cpu_setup() before MMU on just like HA. A CPU capability is added to notify the user of the feature. Add definition of P{G,4,U,M}D_TABLE_AF bit and set the AF bit when creating the page table, which will save the hardware from having to update them at runtime. This will be ignored if FEAT_HAFT is not enabled. The AF bit of table descriptors cannot be managed by the software per spec, unlike the HA. So this should be used only if it's supported system wide by system_supports_haft(). Signed-off-by: Yicong Yang Link: https://lore.kernel.org/r/20241102104235.62560-4-yangyicong@huawei.com Reviewed-by: Catalin Marinas [catalin.marinas@arm.com: added the ID check back to __cpu_setup in case of future CPU errata] Signed-off-by: Catalin Marinas Conflicts: arch/arm64/Kconfig arch/arm64/include/asm/cpufeature.h arch/arm64/kernel/cpufeature.c arch/arm64/mm/mmu.c arch/arm64/tools/cpucaps [Context conflicts] Signed-off-by: JiangShui Yang --- arch/arm64/Kconfig | 16 ++++++++++++++++ arch/arm64/include/asm/cpufeature.h | 6 ++++++ arch/arm64/include/asm/pgalloc.h | 10 ++++++---- arch/arm64/include/asm/pgtable-hwdef.h | 3 +++ arch/arm64/include/asm/sysreg.h | 1 + arch/arm64/kernel/cpufeature.c | 18 ++++++++++++++++++ arch/arm64/mm/mmu.c | 6 +++--- arch/arm64/mm/proc.S | 7 ++++++- arch/arm64/tools/cpucaps | 1 + 9 files changed, 60 insertions(+), 8 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 6890847c1a134..750ed20347fef 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1945,6 +1945,22 @@ config ARM64_EPAN The feature is detected at runtime, and will remain disabled if the cpu does not implement the feature. + +config ARM64_HAFT + bool "Support for Hardware managed Access Flag for Table Descriptors" + depends on ARM64_HW_AFDBM + default y + help + The ARMv8.9/ARMv9.5 introduces the feature Hardware managed Access + Flag for Table descriptors. When enabled an architectural executed + memory access will update the Access Flag in each Table descriptor + which is accessed during the translation table walk and for which + the Access Flag is 0. The Access Flag of the Table descriptor use + the same bit of PTE_AF. + + The feature will only be enabled if all the CPUs in the system + support this feature. If unsure, say Y. + endmenu config ARM64_SVE diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 5df2188f8839e..266ebca324ea3 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -808,6 +808,12 @@ static inline bool system_supports_tlb_range(void) cpus_have_const_cap(ARM64_HAS_TLB_RANGE); } +static inline bool system_supports_haft(void) +{ + return IS_ENABLED(CONFIG_ARM64_HAFT) && + cpus_have_final_cap(ARM64_HAFT); +} + int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt); bool try_emulate_mrs(struct pt_regs *regs, u32 isn); diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h index 237224484d0f6..661964e99b9d5 100644 --- a/arch/arm64/include/asm/pgalloc.h +++ b/arch/arm64/include/asm/pgalloc.h @@ -27,7 +27,7 @@ static inline void __pud_populate(pud_t *pudp, phys_addr_t pmdp, pudval_t prot) static inline void pud_populate(struct mm_struct *mm, pud_t *pudp, pmd_t *pmdp) { - pudval_t pudval = PUD_TYPE_TABLE; + pudval_t pudval = PUD_TYPE_TABLE | PUD_TABLE_AF; pudval |= (mm == &init_mm) ? PUD_TABLE_UXN : PUD_TABLE_PXN; __pud_populate(pudp, __pa(pmdp), pudval); @@ -48,7 +48,7 @@ static inline void __p4d_populate(p4d_t *p4dp, phys_addr_t pudp, p4dval_t prot) static inline void p4d_populate(struct mm_struct *mm, p4d_t *p4dp, pud_t *pudp) { - p4dval_t p4dval = P4D_TYPE_TABLE; + p4dval_t p4dval = P4D_TYPE_TABLE | P4D_TABLE_AF; p4dval |= (mm == &init_mm) ? P4D_TABLE_UXN : P4D_TABLE_PXN; __p4d_populate(p4dp, __pa(pudp), p4dval); @@ -77,14 +77,16 @@ static inline void pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep) { VM_BUG_ON(mm && mm != &init_mm); - __pmd_populate(pmdp, __pa(ptep), PMD_TYPE_TABLE | PMD_TABLE_UXN); + __pmd_populate(pmdp, __pa(ptep), + PMD_TYPE_TABLE | PMD_TABLE_AF | PMD_TABLE_UXN); } static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep) { VM_BUG_ON(mm == &init_mm); - __pmd_populate(pmdp, page_to_phys(ptep), PMD_TYPE_TABLE | PMD_TABLE_PXN); + __pmd_populate(pmdp, page_to_phys(ptep), + PMD_TYPE_TABLE | PMD_TABLE_AF | PMD_TABLE_PXN); } #endif diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h index 66671ff051835..115e0dced25bc 100644 --- a/arch/arm64/include/asm/pgtable-hwdef.h +++ b/arch/arm64/include/asm/pgtable-hwdef.h @@ -94,6 +94,7 @@ #define P4D_TYPE_MASK (_AT(p4dval_t, 3) << 0) #define P4D_TYPE_SECT (_AT(p4dval_t, 1) << 0) #define P4D_SECT_RDONLY (_AT(p4dval_t, 1) << 7) /* AP[2] */ +#define P4D_TABLE_AF (_AT(p4dval_t, 1) << 10) /* Ignored if no FEAT_HAFT */ #define P4D_TABLE_PXN (_AT(p4dval_t, 1) << 59) #define P4D_TABLE_UXN (_AT(p4dval_t, 1) << 60) @@ -105,6 +106,7 @@ #define PUD_TYPE_MASK (_AT(pudval_t, 3) << 0) #define PUD_TYPE_SECT (_AT(pudval_t, 1) << 0) #define PUD_SECT_RDONLY (_AT(pudval_t, 1) << 7) /* AP[2] */ +#define PUD_TABLE_AF (_AT(pudval_t, 1) << 10) /* Ignored if no FEAT_HAFT */ #define PUD_TABLE_PXN (_AT(pudval_t, 1) << 59) #define PUD_TABLE_UXN (_AT(pudval_t, 1) << 60) @@ -115,6 +117,7 @@ #define PMD_TYPE_TABLE (_AT(pmdval_t, 3) << 0) #define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0) #define PMD_TABLE_BIT (_AT(pmdval_t, 1) << 1) +#define PMD_TABLE_AF (_AT(pmdval_t, 1) << 10) /* Ignored if no FEAT_HAFT */ /* * Section diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 6f4c09bd3f7e5..995a1286cbc21 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -1093,6 +1093,7 @@ #define ID_AA64MMFR1_EL1_ETS_ETS2 UL(0b0010) #define ID_AA64MMFR1_EL1_ETS_ETS3 UL(0b0011) +#define ID_AA64MMFR1_EL1_HAFDBS_SHIFT 0 #define ID_AA64MMFR1_EL1_HAFDBS_HAFT UL(0b0011) #define ID_AA64MMFR1_EL1_HAFDBS_HDBSS UL(0b0100) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index e48894fb575d7..3b7bd58665f38 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -2216,6 +2216,24 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .matches = has_hw_dbm, .cpu_enable = cpu_enable_hw_dbm, }, +#endif +#ifdef CONFIG_ARM64_HAFT + { + .desc = "Hardware managed Access Flag for Table Descriptors", + /* + * Contrary to the page/block access flag, the table access flag + * cannot be emulated in software (no access fault will occur). + * Therefore this should be used only if it's supported system + * wide. + */ + .capability = ARM64_HAFT, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, + .sys_reg = SYS_ID_AA64MMFR1_EL1, + .sign = FTR_UNSIGNED, + .field_pos = ID_AA64MMFR1_EL1_HAFDBS_SHIFT, + .min_field_value = ID_AA64MMFR1_EL1_HAFDBS_HAFT, + }, #endif { .desc = "CRC32 instructions", diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index d357d3960a269..e21eb04cc0d90 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -188,7 +188,7 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr, BUG_ON(pmd_sect(pmd)); if (pmd_none(pmd)) { - pmdval_t pmdval = PMD_TYPE_TABLE | PMD_TABLE_UXN; + pmdval_t pmdval = PMD_TYPE_TABLE | PMD_TABLE_UXN | PMD_TABLE_AF; phys_addr_t pte_phys; if (flags & NO_EXEC_MAPPINGS) @@ -266,7 +266,7 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr, */ BUG_ON(pud_sect(pud)); if (pud_none(pud)) { - pudval_t pudval = PUD_TYPE_TABLE | PUD_TABLE_UXN; + pudval_t pudval = PUD_TYPE_TABLE | PUD_TABLE_UXN | PUD_TABLE_AF; phys_addr_t pmd_phys; if (flags & NO_EXEC_MAPPINGS) @@ -317,7 +317,7 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end, p4d_t p4d = READ_ONCE(*p4dp); if (p4d_none(p4d)) { - p4dval_t p4dval = P4D_TYPE_TABLE | P4D_TABLE_UXN; + p4dval_t p4dval = P4D_TYPE_TABLE | P4D_TABLE_UXN | P4D_TABLE_AF; phys_addr_t pud_phys; if (flags & NO_EXEC_MAPPINGS) diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S index 3d3c081e78816..af7c5139b6216 100644 --- a/arch/arm64/mm/proc.S +++ b/arch/arm64/mm/proc.S @@ -450,9 +450,14 @@ SYM_FUNC_START(__cpu_setup) * via capabilities. */ mrs x9, ID_AA64MMFR1_EL1 - and x9, x9, ID_AA64MMFR1_EL1_HAFDBS_MASK + ubfx x9, x9, ID_AA64MMFR1_EL1_HAFDBS_SHIFT, #4 cbz x9, 1f orr tcr, tcr, #TCR_HA // hardware Access flag update +#ifdef CONFIG_ARM64_HAFT + cmp x9, ID_AA64MMFR1_EL1_HAFDBS_HAFT + b.lt 1f + orr tcr2, tcr2, TCR2_EL1x_HAFT +#endif /* CONFIG_ARM64_HAFT */ 1: #endif /* CONFIG_ARM64_HW_AFDBM */ msr mair_el1, mair diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index 07891af8aa94a..c7282a5d47266 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -38,6 +38,7 @@ HAS_SYSREG_GIC_CPUIF HAS_TIDCP1 HAS_TLB_RANGE HAS_VIRT_HOST_EXTN +HAFT HW_DBM KVM_PROTECTED_MODE MISMATCHED_CACHE_TYPE From f4e9709dabb0f8d9b4eeea910ff863c52caeb8bf Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 4 Jul 2025 16:44:40 +0800 Subject: [PATCH 164/227] arm64: Enable ARCH_HAS_NONLEAF_PMD_YOUNG mainline inclusion from mainline-v6.13-rc1 commit 62df5870ebf7cec96a51c9b9008daf167e22db14 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB4YD4 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=62df5870ebf7cec96a51c9b9008daf167e22db14 ---------------------------------------------------------------------- With the support of FEAT_HAFT, the NONLEAF_PMD_YOUNG can be enabled on arm64 since the hardware is capable of updating the AF flag for PMD table descriptor. Since the AF bit of the table descriptor shares the same bit position in block descriptors, we only need to implement arch_has_hw_nonleaf_pmd_young() and select related configs. The related pmd_young test/update operations keeps the same with and already implemented for transparent page support. Currently ARCH_HAS_NONLEAF_PMD_YOUNG is used to improve the efficiency of lru-gen aging. Signed-off-by: Yicong Yang Reviewed-by: Catalin Marinas Link: https://lore.kernel.org/r/20241102104235.62560-5-yangyicong@huawei.com Signed-off-by: Catalin Marinas Conflicts: arch/arm64/Kconfig [Context conflicts] Signed-off-by: JiangShui Yang --- arch/arm64/Kconfig | 1 + arch/arm64/include/asm/pgtable.h | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 750ed20347fef..eef487d364bb6 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -30,6 +30,7 @@ config ARM64 select ARCH_HAS_KEEPINITRD select ARCH_HAS_MEMBARRIER_SYNC_CORE select ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE + select ARCH_HAS_NONLEAF_PMD_YOUNG if ARM64_HAFT select ARCH_HAS_PTE_DEVMAP select ARCH_HAS_PTE_SPECIAL select ARCH_HAS_SETUP_DMA_OPS diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index b5e969bc074d3..3c82bae2602eb 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -852,7 +852,7 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma, return young; } -#ifdef CONFIG_TRANSPARENT_HUGEPAGE +#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) #define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, unsigned long address, @@ -860,7 +860,7 @@ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, { return ptep_test_and_clear_young(vma, address, (pte_t *)pmdp); } -#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ +#endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG */ #define __HAVE_ARCH_PTEP_GET_AND_CLEAR static inline pte_t ptep_get_and_clear(struct mm_struct *mm, @@ -1013,6 +1013,10 @@ static inline bool arch_faults_on_old_pte(void) } #define arch_faults_on_old_pte arch_faults_on_old_pte +#ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG +#define arch_has_hw_nonleaf_pmd_young system_supports_haft +#endif + /* * Experimentally, it's cheap to set the access flag in hardware and we * benefit from prefaulting mappings as 'old' to start with. From 76d7a22b7dc32f9e77a264d77517e30b55ad0c44 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 4 Jul 2025 16:44:41 +0800 Subject: [PATCH 165/227] arm64: pgtable: Warn unexpected pmdp_test_and_clear_young() mainline inclusion from mainline-v6.13-rc1 commit b349a5a2b6e236b25095c6ff886b3451de5ea041 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB4YD4 CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=b349a5a2b6e236b25095c6ff886b3451de5ea041 ---------------------------------------------------------------------- Young bit operation on PMD table entry is only supported if FEAT_HAFT enabled system wide. Add a warning for notifying the misbehaviour. Signed-off-by: Yicong Yang Reviewed-by: Catalin Marinas Link: https://lore.kernel.org/r/20241102104235.62560-6-yangyicong@huawei.com Signed-off-by: Catalin Marinas Signed-off-by: JiangShui Yang --- arch/arm64/include/asm/pgtable.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h index 3c82bae2602eb..1b176f23da097 100644 --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h @@ -858,6 +858,8 @@ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma, unsigned long address, pmd_t *pmdp) { + /* Operation applies to PMD table entry only if FEAT_HAFT is enabled */ + VM_WARN_ON(pmd_table(READ_ONCE(*pmdp)) && !system_supports_haft()); return ptep_test_and_clear_young(vma, address, (pte_t *)pmdp); } #endif /* CONFIG_TRANSPARENT_HUGEPAGE || CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG */ From 1bafe5ef840ca6d76e3672c420eedada581d58a8 Mon Sep 17 00:00:00 2001 From: JiangShui Yang Date: Fri, 4 Jul 2025 16:44:42 +0800 Subject: [PATCH 166/227] openeuler_defconfig: Add configuration to enable FEAT_HAFT haft inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IB4YD4 CVE: NA ---------------------------------------------------------------------- The FEAT_HAFT feature depends on the LRU_GEN and ARCH_HAS configurations Signed-off-by: JiangShui Yang --- config.aarch64 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config.aarch64 b/config.aarch64 index 3bb003a14a964..998d7fb5b0f3f 100644 --- a/config.aarch64 +++ b/config.aarch64 @@ -967,6 +967,7 @@ CONFIG_BALLOON_COMPACTION=y CONFIG_COMPACTION=y CONFIG_PAGE_REPORTING=y CONFIG_MIGRATION=y +CONFIG_ARM64_HAFT=y CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y CONFIG_ARCH_ENABLE_THP_MIGRATION=y CONFIG_CONTIG_ALLOC=y @@ -9889,6 +9890,7 @@ CONFIG_ARCH_HAS_TEARDOWN_DMA_OPS=y CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE=y CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU=y CONFIG_ARCH_HAS_DMA_PREP_COHERENT=y +CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y CONFIG_SWIOTLB=y # CONFIG_DMA_RESTRICTED_POOL is not set CONFIG_DMA_NONCOHERENT_MMAP=y From 32631674a1c6399d3f30ac447de632f961795a40 Mon Sep 17 00:00:00 2001 From: chenyi Date: Fri, 4 Jul 2025 10:14:12 +0800 Subject: [PATCH 167/227] Revert "drm: Move nomodeset kernel parameter to the DRM subsystem" This reverts commit 84a1a12ab048180bd0160e9ec92f3bf2d45b25a8. --- drivers/gpu/drm/Makefile | 2 -- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 ++- drivers/gpu/drm/ast/ast_drv.c | 3 ++- drivers/gpu/drm/i915/i915_module.c | 4 ++-- drivers/gpu/drm/mgag200/mgag200_drv.c | 3 ++- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 ++-- drivers/gpu/drm/qxl/qxl_drv.c | 3 ++- drivers/gpu/drm/radeon/radeon_drv.c | 3 ++- drivers/gpu/drm/tiny/bochs.c | 3 ++- drivers/gpu/drm/tiny/cirrus.c | 4 ++-- drivers/gpu/drm/vboxvideo/vbox_drv.c | 3 ++- drivers/gpu/drm/virtio/virtgpu_drv.c | 3 ++- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 3 ++- drivers/video/console/vgacon.c | 21 ++++++++++++++++++++ gpu/drm/drm_nomodeset.c | 26 ------------------------- include/drm/drm_drv.h | 5 ----- include/linux/console.h | 6 ++++++ 17 files changed, 51 insertions(+), 48 deletions(-) delete mode 100644 gpu/drm/drm_nomodeset.c diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 316888251eff6..d89c2c6aab5cf 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -36,8 +36,6 @@ drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o obj-$(CONFIG_DRM_DP_AUX_BUS) += drm_dp_aux_bus.o -obj-$(CONFIG_VGA_CONSOLE) += drm_nomodeset.o - drm_vram_helper-y := drm_gem_vram_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 2dd6eae1dc189..da7e87de3b2b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -30,6 +30,7 @@ #include "amdgpu_drv.h" #include +#include #include #include #include @@ -2646,7 +2647,7 @@ static int __init amdgpu_init(void) { int r; - if (drm_firmware_drivers_only()) + if (vgacon_text_force()) { DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 6d8613f6fe1cd..86d5cd7b6318c 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -26,6 +26,7 @@ * Authors: Dave Airlie */ +#include #include #include @@ -232,7 +233,7 @@ static struct pci_driver ast_pci_driver = { static int __init ast_init(void) { - if (drm_firmware_drivers_only() && ast_modeset == -1) + if (vgacon_text_force() && ast_modeset == -1) return -EINVAL; if (ast_modeset == 0) diff --git a/drivers/gpu/drm/i915/i915_module.c b/drivers/gpu/drm/i915/i915_module.c index 6c49ac3d4458a..d8b4482c69d0e 100644 --- a/drivers/gpu/drm/i915/i915_module.c +++ b/drivers/gpu/drm/i915/i915_module.c @@ -4,7 +4,7 @@ * Copyright © 2021 Intel Corporation */ -#include +#include #include "gem/i915_gem_context.h" #include "gem/i915_gem_object.h" @@ -31,7 +31,7 @@ static int i915_check_nomodeset(void) if (i915_modparams.modeset == 0) use_kms = false; - if (drm_firmware_drivers_only() && i915_modparams.modeset == -1) + if (vgacon_text_force() && i915_modparams.modeset == -1) use_kms = false; if (!use_kms) { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 740108a006ba5..6b9243713b3c6 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -6,6 +6,7 @@ * Dave Airlie */ +#include #include #include #include @@ -377,7 +378,7 @@ static struct pci_driver mgag200_pci_driver = { static int __init mgag200_init(void) { - if (drm_firmware_drivers_only() && mgag200_modeset == -1) + if (vgacon_text_force() && mgag200_modeset == -1) return -EINVAL; if (mgag200_modeset == 0) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 1674b606965da..3a7e9983c408c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ +#include #include #include #include @@ -31,7 +32,6 @@ #include #include -#include #include #include #include @@ -1363,7 +1363,7 @@ nouveau_drm_init(void) nouveau_display_options(); if (nouveau_modeset == -1) { - if (drm_firmware_drivers_only()) + if (vgacon_text_force()) nouveau_modeset = 0; } diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index e4b16421500b4..fc47b0deb021d 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -29,6 +29,7 @@ #include "qxl_drv.h" +#include #include #include #include @@ -294,7 +295,7 @@ static struct drm_driver qxl_driver = { static int __init qxl_init(void) { - if (drm_firmware_drivers_only() && qxl_modeset == -1) + if (vgacon_text_force() && qxl_modeset == -1) return -EINVAL; if (qxl_modeset == 0) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index ac21f36f73d9b..68c3338a5a8d4 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -31,6 +31,7 @@ #include +#include #include #include #include @@ -635,7 +636,7 @@ static struct pci_driver radeon_kms_pci_driver = { static int __init radeon_module_init(void) { - if (drm_firmware_drivers_only() && radeon_modeset == -1) + if (vgacon_text_force() && radeon_modeset == -1) { DRM_INFO("VGACON disable radeon kernel modesetting.\n"); radeon_modeset = 0; } diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index 2da8019b8e3ef..9d342cd02dd90 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include @@ -711,7 +712,7 @@ static struct pci_driver bochs_pci_driver = { static int __init bochs_init(void) { - if (drm_firmware_drivers_only() && bochs_modeset == -1) + if (vgacon_text_force() && bochs_modeset == -1) return -EINVAL; if (bochs_modeset == 0) diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index 8dfb3ada4fc06..2a81311b22172 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -16,6 +16,7 @@ * Copyright 1999-2001 Jeff Garzik */ +#include #include #include #include @@ -635,9 +636,8 @@ static struct pci_driver cirrus_pci_driver = { static int __init cirrus_init(void) { - if (drm_firmware_drivers_only()) + if (vgacon_text_force()) return -EINVAL; - return pci_register_driver(&cirrus_pci_driver); } diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 1f270398de992..2b81cb259d234 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -7,6 +7,7 @@ * Michael Thayer */ +#include #include #include #include @@ -196,7 +197,7 @@ static const struct drm_driver driver = { static int __init vbox_init(void) { #ifdef CONFIG_VGA_CONSOLE - if (drm_firmware_drivers_only() && vbox_modeset == -1) + if (vgacon_text_force() && vbox_modeset == -1) return -EINVAL; #endif diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index e457c71c1a81a..cd92cdc145626 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -27,6 +27,7 @@ */ #include +#include #include #include @@ -101,7 +102,7 @@ static int virtio_gpu_probe(struct virtio_device *vdev) struct drm_device *dev; int ret; - if (drm_firmware_drivers_only() && virtio_gpu_modeset == -1) + if (vgacon_text_force() && virtio_gpu_modeset == -1) return -EINVAL; if (virtio_gpu_modeset == 0) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 03937e710ab3d..8449d09c06f7a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -25,6 +25,7 @@ * **************************************************************************/ +#include #include #include #include @@ -1658,7 +1659,7 @@ static int __init vmwgfx_init(void) { int ret; - if (drm_firmware_drivers_only()) + if (vgacon_text_force()) return -EINVAL; ret = pci_register_driver(&vmw_pci_driver); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 576612f18d596..9a49ea6b5112f 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -97,9 +97,30 @@ static int vga_video_font_height; static int vga_scan_lines __read_mostly; static unsigned int vga_rolled_over; /* last vc_origin offset before wrap */ +static bool vgacon_text_mode_force; static bool vga_hardscroll_enabled; static bool vga_hardscroll_user_enable = true; +bool vgacon_text_force(void) +{ + return vgacon_text_mode_force; +} +EXPORT_SYMBOL(vgacon_text_force); + +static int __init text_mode(char *str) +{ + vgacon_text_mode_force = true; + + pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); + pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); + pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); + + return 1; +} + +/* force text mode - used by kernel modesetting */ +__setup("nomodeset", text_mode); + static int __init no_scroll(char *str) { /* diff --git a/gpu/drm/drm_nomodeset.c b/gpu/drm/drm_nomodeset.c deleted file mode 100644 index 287edfb18b5d7..0000000000000 --- a/gpu/drm/drm_nomodeset.c +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include - -static bool drm_nomodeset; - -bool drm_firmware_drivers_only(void) -{ - return drm_nomodeset; -} -EXPORT_SYMBOL(drm_firmware_drivers_only); - -static int __init disable_modeset(char *str) -{ - drm_nomodeset = true; - - pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); - pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); - pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); - - return 1; -} - -/* Disable kernel modesetting */ -__setup("nomodeset", disable_modeset); diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index eec308da88f40..84d642e9faa35 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -595,10 +595,5 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) int drm_dev_set_unique(struct drm_device *dev, const char *name); -#ifdef CONFIG_VGA_CONSOLE -extern bool drm_firmware_drivers_only(void); -#else -static inline bool drm_firmware_drivers_only(void) { return false; } -#endif #endif diff --git a/include/linux/console.h b/include/linux/console.h index 7cd758a4f44ed..a97f277cfdfa3 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -219,6 +219,12 @@ extern atomic_t ignore_console_lock_warning; #define VESA_HSYNC_SUSPEND 2 #define VESA_POWERDOWN 3 +#ifdef CONFIG_VGA_CONSOLE +extern bool vgacon_text_force(void); +#else +static inline bool vgacon_text_force(void) { return false; } +#endif + extern void console_init(void); /* For deferred console takeover */ From 75cb9e8fd0208c54d4fe5b64850e555cd62f078a Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 12 Nov 2021 14:32:27 +0100 Subject: [PATCH 168/227] drm: Move nomodeset kernel parameter to the DRM subsystem The "nomodeset" kernel cmdline parameter is handled by the vgacon driver but the exported vgacon_text_force() symbol is only used by DRM drivers. It makes much more sense for the parameter logic to be in the subsystem of the drivers that are making use of it. Let's move the vgacon_text_force() function and related logic to the DRM subsystem. While doing that, rename it to drm_firmware_drivers_only() and make it return true if "nomodeset" was used and false otherwise. This is a better description of the condition that the drivers are testing for. Suggested-by: Daniel Vetter Signed-off-by: Javier Martinez Canillas Acked-by: Thomas Zimmermann Acked-by: Jani Nikula Acked-by: Pekka Paalanen Acked-by: Greg Kroah-Hartman Link: https://patchwork.freedesktop.org/patch/msgid/20211112133230.1595307-4-javierm@redhat.com Signed-off-by: chenyi --- drivers/gpu/drm/Makefile | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 +-- drivers/gpu/drm/ast/ast_drv.c | 3 +-- drivers/gpu/drm/i915/i915_module.c | 4 ++-- drivers/gpu/drm/mgag200/mgag200_drv.c | 3 +-- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 ++-- drivers/gpu/drm/qxl/qxl_drv.c | 3 +-- drivers/gpu/drm/radeon/radeon_drv.c | 3 +-- drivers/gpu/drm/tiny/bochs.c | 3 +-- drivers/gpu/drm/tiny/cirrus.c | 4 ++-- drivers/gpu/drm/vboxvideo/vbox_drv.c | 3 +-- drivers/gpu/drm/virtio/virtgpu_drv.c | 3 +-- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 3 +-- drivers/video/console/vgacon.c | 21 -------------------- gpu/drm/drm_nomodeset.c | 26 +++++++++++++++++++++++++ include/drm/drm_drv.h | 5 +++++ include/linux/console.h | 6 ------ 17 files changed, 48 insertions(+), 51 deletions(-) create mode 100644 gpu/drm/drm_nomodeset.c diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index d89c2c6aab5cf..316888251eff6 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -36,6 +36,8 @@ drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o obj-$(CONFIG_DRM_DP_AUX_BUS) += drm_dp_aux_bus.o +obj-$(CONFIG_VGA_CONSOLE) += drm_nomodeset.o + drm_vram_helper-y := drm_gem_vram_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index da7e87de3b2b2..2dd6eae1dc189 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -30,7 +30,6 @@ #include "amdgpu_drv.h" #include -#include #include #include #include @@ -2647,7 +2646,7 @@ static int __init amdgpu_init(void) { int r; - if (vgacon_text_force()) { + if (drm_firmware_drivers_only()) DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 86d5cd7b6318c..6d8613f6fe1cd 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -26,7 +26,6 @@ * Authors: Dave Airlie */ -#include #include #include @@ -233,7 +232,7 @@ static struct pci_driver ast_pci_driver = { static int __init ast_init(void) { - if (vgacon_text_force() && ast_modeset == -1) + if (drm_firmware_drivers_only() && ast_modeset == -1) return -EINVAL; if (ast_modeset == 0) diff --git a/drivers/gpu/drm/i915/i915_module.c b/drivers/gpu/drm/i915/i915_module.c index d8b4482c69d0e..6c49ac3d4458a 100644 --- a/drivers/gpu/drm/i915/i915_module.c +++ b/drivers/gpu/drm/i915/i915_module.c @@ -4,7 +4,7 @@ * Copyright © 2021 Intel Corporation */ -#include +#include #include "gem/i915_gem_context.h" #include "gem/i915_gem_object.h" @@ -31,7 +31,7 @@ static int i915_check_nomodeset(void) if (i915_modparams.modeset == 0) use_kms = false; - if (vgacon_text_force() && i915_modparams.modeset == -1) + if (drm_firmware_drivers_only() && i915_modparams.modeset == -1) use_kms = false; if (!use_kms) { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 6b9243713b3c6..740108a006ba5 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -6,7 +6,6 @@ * Dave Airlie */ -#include #include #include #include @@ -378,7 +377,7 @@ static struct pci_driver mgag200_pci_driver = { static int __init mgag200_init(void) { - if (vgacon_text_force() && mgag200_modeset == -1) + if (drm_firmware_drivers_only() && mgag200_modeset == -1) return -EINVAL; if (mgag200_modeset == 0) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 3a7e9983c408c..1674b606965da 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -22,7 +22,6 @@ * Authors: Ben Skeggs */ -#include #include #include #include @@ -32,6 +31,7 @@ #include #include +#include #include #include #include @@ -1363,7 +1363,7 @@ nouveau_drm_init(void) nouveau_display_options(); if (nouveau_modeset == -1) { - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) nouveau_modeset = 0; } diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index fc47b0deb021d..e4b16421500b4 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -29,7 +29,6 @@ #include "qxl_drv.h" -#include #include #include #include @@ -295,7 +294,7 @@ static struct drm_driver qxl_driver = { static int __init qxl_init(void) { - if (vgacon_text_force() && qxl_modeset == -1) + if (drm_firmware_drivers_only() && qxl_modeset == -1) return -EINVAL; if (qxl_modeset == 0) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 68c3338a5a8d4..e6db74e1c7192 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -31,7 +31,6 @@ #include -#include #include #include #include @@ -636,7 +635,7 @@ static struct pci_driver radeon_kms_pci_driver = { static int __init radeon_module_init(void) { - if (vgacon_text_force() && radeon_modeset == -1) { + if (drm_firmware_drivers_only() && radeon_modeset == -1) { DRM_INFO("VGACON disable radeon kernel modesetting.\n"); radeon_modeset = 0; } diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index 9d342cd02dd90..2da8019b8e3ef 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include @@ -712,7 +711,7 @@ static struct pci_driver bochs_pci_driver = { static int __init bochs_init(void) { - if (vgacon_text_force() && bochs_modeset == -1) + if (drm_firmware_drivers_only() && bochs_modeset == -1) return -EINVAL; if (bochs_modeset == 0) diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index 2a81311b22172..8dfb3ada4fc06 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -16,7 +16,6 @@ * Copyright 1999-2001 Jeff Garzik */ -#include #include #include #include @@ -636,8 +635,9 @@ static struct pci_driver cirrus_pci_driver = { static int __init cirrus_init(void) { - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) return -EINVAL; + return pci_register_driver(&cirrus_pci_driver); } diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 2b81cb259d234..1f270398de992 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -7,7 +7,6 @@ * Michael Thayer */ -#include #include #include #include @@ -197,7 +196,7 @@ static const struct drm_driver driver = { static int __init vbox_init(void) { #ifdef CONFIG_VGA_CONSOLE - if (vgacon_text_force() && vbox_modeset == -1) + if (drm_firmware_drivers_only() && vbox_modeset == -1) return -EINVAL; #endif diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index cd92cdc145626..e457c71c1a81a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -27,7 +27,6 @@ */ #include -#include #include #include @@ -102,7 +101,7 @@ static int virtio_gpu_probe(struct virtio_device *vdev) struct drm_device *dev; int ret; - if (vgacon_text_force() && virtio_gpu_modeset == -1) + if (drm_firmware_drivers_only() && virtio_gpu_modeset == -1) return -EINVAL; if (virtio_gpu_modeset == 0) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 8449d09c06f7a..03937e710ab3d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -25,7 +25,6 @@ * **************************************************************************/ -#include #include #include #include @@ -1659,7 +1658,7 @@ static int __init vmwgfx_init(void) { int ret; - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) return -EINVAL; ret = pci_register_driver(&vmw_pci_driver); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 9a49ea6b5112f..576612f18d596 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -97,30 +97,9 @@ static int vga_video_font_height; static int vga_scan_lines __read_mostly; static unsigned int vga_rolled_over; /* last vc_origin offset before wrap */ -static bool vgacon_text_mode_force; static bool vga_hardscroll_enabled; static bool vga_hardscroll_user_enable = true; -bool vgacon_text_force(void) -{ - return vgacon_text_mode_force; -} -EXPORT_SYMBOL(vgacon_text_force); - -static int __init text_mode(char *str) -{ - vgacon_text_mode_force = true; - - pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); - pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); - pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); - - return 1; -} - -/* force text mode - used by kernel modesetting */ -__setup("nomodeset", text_mode); - static int __init no_scroll(char *str) { /* diff --git a/gpu/drm/drm_nomodeset.c b/gpu/drm/drm_nomodeset.c new file mode 100644 index 0000000000000..287edfb18b5d7 --- /dev/null +++ b/gpu/drm/drm_nomodeset.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +static bool drm_nomodeset; + +bool drm_firmware_drivers_only(void) +{ + return drm_nomodeset; +} +EXPORT_SYMBOL(drm_firmware_drivers_only); + +static int __init disable_modeset(char *str) +{ + drm_nomodeset = true; + + pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); + pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); + pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); + + return 1; +} + +/* Disable kernel modesetting */ +__setup("nomodeset", disable_modeset); diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 84d642e9faa35..eec308da88f40 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -595,5 +595,10 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) int drm_dev_set_unique(struct drm_device *dev, const char *name); +#ifdef CONFIG_VGA_CONSOLE +extern bool drm_firmware_drivers_only(void); +#else +static inline bool drm_firmware_drivers_only(void) { return false; } +#endif #endif diff --git a/include/linux/console.h b/include/linux/console.h index a97f277cfdfa3..7cd758a4f44ed 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -219,12 +219,6 @@ extern atomic_t ignore_console_lock_warning; #define VESA_HSYNC_SUSPEND 2 #define VESA_POWERDOWN 3 -#ifdef CONFIG_VGA_CONSOLE -extern bool vgacon_text_force(void); -#else -static inline bool vgacon_text_force(void) { return false; } -#endif - extern void console_init(void); /* For deferred console takeover */ From de8489b9eac46c9333581cbc09e2daa7eb115685 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 16 Apr 2025 16:24:57 +0800 Subject: [PATCH 169/227] drm: Call drm_atomic_helper_shutdown() at shutdown time for misc drivers maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/drivers/gpu/drm?id=ce3d99c8349584bc0fbe1e21918a3ea1155343aa ---------------------------------------------------------------------- Based on grepping through the source code these drivers appear to be missing a call to drm_atomic_helper_shutdown() at system shutdown time. Among other things, this means that if a panel is in use that it won't be cleanly powered off at system shutdown time. The fact that we should call drm_atomic_helper_shutdown() in the case of OS shutdown/restart comes straight out of the kernel doc "driver instance overview" in drm_drv.c. All of the drivers in this patch were fairly straightforward to fix since they already had a call to drm_atomic_helper_shutdown() at remove/unbind time but were just lacking one at system shutdown. The only hitch is that some of these drivers use the component model to register/unregister their DRM devices. The shutdown callback is part of the original device. The typical solution here, based on how other DRM drivers do this, is to keep track of whether the device is bound based on drvdata. In most cases the drvdata is the drm_device, so we can just make sure it is NULL when the device is not bound. In some drivers, this required minor code changes. To make things simpler, drm_atomic_helper_shutdown() has been modified to consider a NULL drm_device as a noop in the patch ("drm/atomic-helper: drm_atomic_helper_shutdown(NULL) should be a noop"). Suggested-by: Maxime Ripard Reviewed-by: Tomi Valkeinen Tested-by: Tomi Valkeinen Acked-by: Maxime Ripard Tested-by: Jernej Skrabec Reviewed-by: Jernej Skrabec Reviewed-by: Sui Jingfeng Tested-by: Sui Jingfeng Signed-off-by: Douglas Anderson Link: https://patchwork.freedesktop.org/patch/msgid/20230901163944.RFT.2.I9115e5d094a43e687978b0699cc1fe9f2a3452ea@changeid Signed-off-by: Baihan Li Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 897dcb8d89cb4..21daffd845d06 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -370,6 +370,11 @@ static void hibmc_pci_remove(struct pci_dev *pdev) hibmc_unload(dev); } +static void hibmc_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} + static const struct pci_device_id hibmc_pci_table[] = { { PCI_VDEVICE(HUAWEI, 0x1711) }, {0,} @@ -380,6 +385,7 @@ static struct pci_driver hibmc_pci_driver = { .id_table = hibmc_pci_table, .probe = hibmc_pci_probe, .remove = hibmc_pci_remove, + .shutdown = hibmc_pci_shutdown, .driver.pm = &hibmc_pm_ops, }; From 160c2dbb219110923dd775bfc643ea7f61dfe061 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 16 Apr 2025 16:24:58 +0800 Subject: [PATCH 170/227] drm/hisilicon/hibmc: select CONFIG_DRM_DISPLAY_DP_HELPER maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?h=v6.15-rc2&id=9ab127a18018fb06bd42a54ed38bb7b8c449d686 ---------------------------------------------------------------------- Without the DP helper code, the newly added displayport support causes a link failure: x86_64-linux-ld: drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.o: in function `hibmc_dp_aux_init': dp_aux.c:(.text+0x37e): undefined reference to `drm_dp_aux_init' x86_64-linux-ld: drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.o: in function `hibmc_dp_link_set_pattern': dp_link.c:(.text+0xae): undefined reference to `drm_dp_dpcd_write' x86_64-linux-ld: drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.o: in function `hibmc_dp_link_get_adjust_train': dp_link.c:(.text+0x121): undefined reference to `drm_dp_get_adjust_request_voltage' x86_64-linux-ld: dp_link.c:(.text+0x12e): undefined reference to `drm_dp_get_adjust_request_pre_emphasis' x86_64-linux-ld: drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.o: in function `hibmc_dp_link_training': dp_link.c:(.text+0x2b0): undefined reference to `drm_dp_dpcd_write' x86_64-linux-ld: dp_link.c:(.text+0x2e3): undefined reference to `drm_dp_dpcd_write' Add both DRM_DISPLAY_DP_HELPER and DRM_DISPLAY_HELPER, which is in turn required by the former. Fixes: 8d6349a4232a ("drm/hisilicon/hibmc: add dp module in hibmc") Signed-off-by: Arnd Bergmann Reviewed-by: Dmitry Baryshkov Link: https://patchwork.freedesktop.org/patch/msgid/20250127071059.617567-1-arnd@kernel.org Signed-off-by: Dmitry Baryshkov Signed-off-by: Baihan Li Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/hisilicon/hibmc/Kconfig b/drivers/gpu/drm/hisilicon/hibmc/Kconfig index 126504318a4f9..8f1f666e30afb 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Kconfig +++ b/drivers/gpu/drm/hisilicon/hibmc/Kconfig @@ -3,6 +3,8 @@ config DRM_HISI_HIBMC tristate "DRM Support for Hisilicon Hibmc" depends on DRM && PCI && (ARM64 || COMPILE_TEST) depends on MMU + select DRM_DISPLAY_HELPER + select DRM_DISPLAY_DP_HELPER select DRM_KMS_HELPER select DRM_VRAM_HELPER select DRM_TTM From 0b45817e234216675f668239675395b6f20a80b3 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:24:59 +0800 Subject: [PATCH 171/227] drm/hisilicon/hibmc: Restructuring the header dp_reg.h maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=f9698f802e50fbe696b3ac6f82c0e966574a3edb ---------------------------------------------------------------------- Move the macros below their corresponding registers to make them more obvious. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-2-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 98 +++++++++++++-------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index 4a515c726d524..dc2bd3f80b70d 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -5,72 +5,94 @@ #define DP_REG_H #define HIBMC_DP_AUX_CMD_ADDR 0x50 + #define HIBMC_DP_AUX_WR_DATA0 0x54 #define HIBMC_DP_AUX_WR_DATA1 0x58 #define HIBMC_DP_AUX_WR_DATA2 0x5c #define HIBMC_DP_AUX_WR_DATA3 0x60 #define HIBMC_DP_AUX_RD_DATA0 0x64 + #define HIBMC_DP_AUX_REQ 0x74 +#define HIBMC_DP_CFG_AUX_REQ BIT(0) +#define HIBMC_DP_CFG_AUX_SYNC_LEN_SEL BIT(1) +#define HIBMC_DP_CFG_AUX_TIMER_TIMEOUT BIT(2) +#define HIBMC_DP_CFG_AUX_MIN_PULSE_NUM GENMASK(13, 9) + #define HIBMC_DP_AUX_STATUS 0x78 +#define HIBMC_DP_CFG_AUX_TIMEOUT BIT(0) +#define HIBMC_DP_CFG_AUX_STATUS GENMASK(11, 4) +#define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12) +#define HIBMC_DP_CFG_AUX GENMASK(24, 17) + #define HIBMC_DP_PHYIF_CTRL0 0xa0 +#define HIBMC_DP_CFG_SCRAMBLE_EN BIT(0) +#define HIBMC_DP_CFG_PAT_SEL GENMASK(7, 4) +#define HIBMC_DP_CFG_LANE_DATA_EN GENMASK(11, 8) + #define HIBMC_DP_VIDEO_CTRL 0x100 +#define HIBMC_DP_CFG_STREAM_RGB_ENABLE BIT(1) +#define HIBMC_DP_CFG_STREAM_VIDEO_MAPPING GENMASK(5, 2) +#define HIBMC_DP_CFG_STREAM_FRAME_MODE BIT(6) +#define HIBMC_DP_CFG_STREAM_HSYNC_POLARITY BIT(7) +#define HIBMC_DP_CFG_STREAM_VSYNC_POLARITY BIT(8) + #define HIBMC_DP_VIDEO_CONFIG0 0x104 +#define HIBMC_DP_CFG_STREAM_HACTIVE GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_HBLANK GENMASK(15, 0) + #define HIBMC_DP_VIDEO_CONFIG1 0x108 +#define HIBMC_DP_CFG_STREAM_VACTIVE GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_VBLANK GENMASK(15, 0) + #define HIBMC_DP_VIDEO_CONFIG2 0x10c +#define HIBMC_DP_CFG_STREAM_HSYNC_WIDTH GENMASK(15, 0) + #define HIBMC_DP_VIDEO_CONFIG3 0x110 +#define HIBMC_DP_CFG_STREAM_VSYNC_WIDTH GENMASK(15, 0) +#define HIBMC_DP_CFG_STREAM_VFRONT_PORCH GENMASK(31, 16) + #define HIBMC_DP_VIDEO_PACKET 0x114 +#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE GENMASK(5, 0) +#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE GENMASK(9, 6) + #define HIBMC_DP_VIDEO_MSA0 0x118 +#define HIBMC_DP_CFG_STREAM_VSTART GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_HSTART GENMASK(15, 0) + #define HIBMC_DP_VIDEO_MSA1 0x11c #define HIBMC_DP_VIDEO_MSA2 0x120 + #define HIBMC_DP_VIDEO_HORIZONTAL_SIZE 0X124 +#define HIBMC_DP_CFG_STREAM_HTOTAL_SIZE GENMASK(31, 16) +#define HIBMC_DP_CFG_STREAM_HBLANK_SIZE GENMASK(15, 0) + #define HIBMC_DP_TIMING_GEN_CONFIG0 0x26c +#define HIBMC_DP_CFG_TIMING_GEN0_HACTIVE GENMASK(31, 16) +#define HIBMC_DP_CFG_TIMING_GEN0_HBLANK GENMASK(15, 0) + #define HIBMC_DP_TIMING_GEN_CONFIG2 0x274 +#define HIBMC_DP_CFG_TIMING_GEN0_VACTIVE GENMASK(31, 16) +#define HIBMC_DP_CFG_TIMING_GEN0_VBLANK GENMASK(15, 0) + #define HIBMC_DP_TIMING_GEN_CONFIG3 0x278 +#define HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH GENMASK(31, 16) + #define HIBMC_DP_HDCP_CFG 0x600 + #define HIBMC_DP_DPTX_RST_CTRL 0x700 +#define HIBMC_DP_CFG_AUX_RST_N BIT(4) + #define HIBMC_DP_DPTX_CLK_CTRL 0x704 + #define HIBMC_DP_DPTX_GCTL0 0x708 +#define HIBMC_DP_CFG_PHY_LANE_NUM GENMASK(2, 1) + #define HIBMC_DP_INTR_ENABLE 0x720 #define HIBMC_DP_INTR_ORIGINAL_STATUS 0x728 -#define HIBMC_DP_TIMING_MODEL_CTRL 0x884 -#define HIBMC_DP_TIMING_SYNC_CTRL 0xFF0 -#define HIBMC_DP_CFG_AUX_SYNC_LEN_SEL BIT(1) -#define HIBMC_DP_CFG_AUX_TIMER_TIMEOUT BIT(2) -#define HIBMC_DP_CFG_STREAM_FRAME_MODE BIT(6) -#define HIBMC_DP_CFG_AUX_MIN_PULSE_NUM GENMASK(13, 9) -#define HIBMC_DP_CFG_LANE_DATA_EN GENMASK(11, 8) -#define HIBMC_DP_CFG_PHY_LANE_NUM GENMASK(2, 1) -#define HIBMC_DP_CFG_AUX_REQ BIT(0) -#define HIBMC_DP_CFG_AUX_RST_N BIT(4) -#define HIBMC_DP_CFG_AUX_TIMEOUT BIT(0) -#define HIBMC_DP_CFG_AUX_READY_DATA_BYTE GENMASK(16, 12) -#define HIBMC_DP_CFG_AUX GENMASK(24, 17) -#define HIBMC_DP_CFG_AUX_STATUS GENMASK(11, 4) -#define HIBMC_DP_CFG_SCRAMBLE_EN BIT(0) -#define HIBMC_DP_CFG_PAT_SEL GENMASK(7, 4) -#define HIBMC_DP_CFG_TIMING_GEN0_HACTIVE GENMASK(31, 16) -#define HIBMC_DP_CFG_TIMING_GEN0_HBLANK GENMASK(15, 0) -#define HIBMC_DP_CFG_TIMING_GEN0_VACTIVE GENMASK(31, 16) -#define HIBMC_DP_CFG_TIMING_GEN0_VBLANK GENMASK(15, 0) -#define HIBMC_DP_CFG_TIMING_GEN0_VFRONT_PORCH GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_HACTIVE GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_HBLANK GENMASK(15, 0) -#define HIBMC_DP_CFG_STREAM_HSYNC_WIDTH GENMASK(15, 0) -#define HIBMC_DP_CFG_STREAM_VACTIVE GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_VBLANK GENMASK(15, 0) -#define HIBMC_DP_CFG_STREAM_VFRONT_PORCH GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_VSYNC_WIDTH GENMASK(15, 0) -#define HIBMC_DP_CFG_STREAM_VSTART GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_HSTART GENMASK(15, 0) -#define HIBMC_DP_CFG_STREAM_VSYNC_POLARITY BIT(8) -#define HIBMC_DP_CFG_STREAM_HSYNC_POLARITY BIT(7) -#define HIBMC_DP_CFG_STREAM_RGB_ENABLE BIT(1) -#define HIBMC_DP_CFG_STREAM_VIDEO_MAPPING GENMASK(5, 2) +#define HIBMC_DP_TIMING_MODEL_CTRL 0x884 #define HIBMC_DP_CFG_PIXEL_NUM_TIMING_MODE_SEL1 GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE GENMASK(5, 0) -#define HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE GENMASK(9, 6) -#define HIBMC_DP_CFG_STREAM_HTOTAL_SIZE GENMASK(31, 16) -#define HIBMC_DP_CFG_STREAM_HBLANK_SIZE GENMASK(15, 0) + +#define HIBMC_DP_TIMING_SYNC_CTRL 0xFF0 #endif From ed270def30af7d4b08809c5f78d7cb6d356bf781 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:25:00 +0800 Subject: [PATCH 172/227] drm/hisilicon/hibmc: Add dp serdes cfg to adjust serdes rate, voltage and pre-emphasis maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=9e736cd444f49efa2334e405f7a59773ea02155b ---------------------------------------------------------------------- This dp controller need features of digital-to-analog conversion and high-speed transmission in chip by its extern serdes controller. Our serdes cfg is relatively simple, just need two register configurations. Don't need too much functions, like: power on/off, initialize, and some complex configurations, so I'm not going to use the phy framework. This serdes is inited and configured in dp initialization, and also integrating them into link training process. For rate changing, we can change from 1.62-8.2Gpbs by cfg reg. For voltage and pre-emphasis levels changing, we can cfg different serdes ffe value. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-3-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/Makefile | 2 +- drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 4 ++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 5 ++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 23 ++++++ .../gpu/drm/hisilicon/hibmc/dp/dp_serdes.c | 71 +++++++++++++++++++ 5 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile index 95a4ed599d980..43de077d6769a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \ - dp/dp_aux.o dp/dp_link.o dp/dp_hw.o hibmc_drm_dp.o + dp/dp_aux.o dp/dp_link.o dp/dp_hw.o dp/dp_serdes.o hibmc_drm_dp.o obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h index 29d0bdc478506..02a148b776468 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h @@ -38,6 +38,7 @@ struct hibmc_dp_dev { struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */ struct hibmc_dp_link link; u8 dpcd[DP_RECEIVER_CAP_SIZE]; + void __iomem *serdes_base; }; #define dp_field_modify(reg_value, mask, val) \ @@ -59,5 +60,8 @@ struct hibmc_dp_dev { void hibmc_dp_aux_init(struct hibmc_dp_dev *dp); int hibmc_dp_link_training(struct hibmc_dp_dev *dp); +int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp); +int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp); +int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]); #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c index a8d543881c099..3612f3c5ab231 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c @@ -151,6 +151,7 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp) { struct drm_device *drm_dev = dp->drm_dev; struct hibmc_dp_dev *dp_dev; + int ret; dp_dev = devm_kzalloc(drm_dev->dev, sizeof(struct hibmc_dp_dev), GFP_KERNEL); if (!dp_dev) @@ -165,6 +166,10 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp) hibmc_dp_aux_init(dp_dev); + ret = hibmc_dp_serdes_init(dp_dev); + if (ret) + return ret; + dp_dev->link.cap.lanes = 0x2; dp_dev->link.cap.link_rate = DP_LINK_BW_2_7; diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index dc2bd3f80b70d..16ea589035980 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -95,4 +95,27 @@ #define HIBMC_DP_TIMING_SYNC_CTRL 0xFF0 +/* dp serdes reg */ +#define HIBMC_DP_HOST_OFFSET 0x10000 +#define HIBMC_DP_LANE0_RATE_OFFSET 0x4 +#define HIBMC_DP_LANE1_RATE_OFFSET 0xc +#define HIBMC_DP_LANE_STATUS_OFFSET 0x10 +#define HIBMC_DP_PMA_LANE0_OFFSET 0x18 +#define HIBMC_DP_PMA_LANE1_OFFSET 0x1c +#define HIBMC_DP_PMA_TXDEEMPH GENMASK(18, 1) +#define DP_SERDES_DONE 0x3 + +/* dp serdes TX-Deempth Configuration */ +#define DP_SERDES_VOL0_PRE0 0x280 +#define DP_SERDES_VOL0_PRE1 0x2300 +#define DP_SERDES_VOL0_PRE2 0x53c0 +#define DP_SERDES_VOL0_PRE3 0x8400 +#define DP_SERDES_VOL1_PRE0 0x380 +#define DP_SERDES_VOL1_PRE1 0x3440 +#define DP_SERDES_VOL1_PRE2 0x6480 +#define DP_SERDES_VOL2_PRE0 0x4c1 +#define DP_SERDES_VOL2_PRE1 0x4500 +#define DP_SERDES_VOL3_PRE0 0x600 +#define DP_SERDES_BW_8_1 0x3 + #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c new file mode 100644 index 0000000000000..676059d4c1e60 --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (c) 2025 Hisilicon Limited. + +#include +#include +#include +#include "dp_comm.h" +#include "dp_config.h" +#include "dp_reg.h" + +int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]) +{ + static const u32 serdes_tx_cfg[4][4] = { {DP_SERDES_VOL0_PRE0, DP_SERDES_VOL0_PRE1, + DP_SERDES_VOL0_PRE2, DP_SERDES_VOL0_PRE3}, + {DP_SERDES_VOL1_PRE0, DP_SERDES_VOL1_PRE1, + DP_SERDES_VOL1_PRE2}, {DP_SERDES_VOL2_PRE0, + DP_SERDES_VOL2_PRE1}, {DP_SERDES_VOL3_PRE0}}; + int cfg[2]; + int i; + + for (i = 0; i < HIBMC_DP_LANE_NUM_MAX; i++) { + cfg[i] = serdes_tx_cfg[FIELD_GET(DP_TRAIN_VOLTAGE_SWING_MASK, train_set[i])] + [FIELD_GET(DP_TRAIN_PRE_EMPHASIS_MASK, train_set[i])]; + if (!cfg[i]) + return -EINVAL; + + /* lane1 offset is 4 */ + writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, cfg[i]), + dp->serdes_base + HIBMC_DP_PMA_LANE0_OFFSET + i * 4); + } + + usleep_range(300, 500); + + if (readl(dp->serdes_base + HIBMC_DP_LANE_STATUS_OFFSET) != DP_SERDES_DONE) { + drm_dbg_dp(dp->dev, "dp serdes cfg failed\n"); + return -EAGAIN; + } + + return 0; +} + +int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp) +{ + writel(rate, dp->serdes_base + HIBMC_DP_LANE0_RATE_OFFSET); + writel(rate, dp->serdes_base + HIBMC_DP_LANE1_RATE_OFFSET); + + usleep_range(300, 500); + + if (readl(dp->serdes_base + HIBMC_DP_LANE_STATUS_OFFSET) != DP_SERDES_DONE) { + drm_dbg_dp(dp->dev, "dp serdes rate switching failed\n"); + return -EAGAIN; + } + + if (rate < DP_SERDES_BW_8_1) + drm_dbg_dp(dp->dev, "reducing serdes rate to :%d\n", + rate ? rate * HIBMC_DP_LINK_RATE_CAL * 10 : 162); + + return 0; +} + +int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp) +{ + dp->serdes_base = dp->base + HIBMC_DP_HOST_OFFSET; + + writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, DP_SERDES_VOL0_PRE0), + dp->serdes_base + HIBMC_DP_PMA_LANE0_OFFSET); + writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, DP_SERDES_VOL0_PRE0), + dp->serdes_base + HIBMC_DP_PMA_LANE1_OFFSET); + + return hibmc_dp_serdes_rate_switch(DP_SERDES_BW_8_1, dp); +} From 60b670aa06f6ff7c558896fd2ad7be45f9c8e639 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:25:01 +0800 Subject: [PATCH 173/227] drm/hisilicon/hibmc: Add dp serdes cfg in dp process maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=5f80fb4d6abd1f7f4007e4bf8dd75a8c71d2f724 ---------------------------------------------------------------------- Add dp serdes cfg in link training process, and related adapting and modificating. Change some init values about training, because we want completely to negotiation process, so we start with the maximum rate and the electrical characteristic level is 0. Because serdes default cfgs is changed and used in hibmc_kms_init(), we changed the if-statement to check whether the value is 0. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-4-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- .../gpu/drm/hisilicon/hibmc/dp/dp_config.h | 1 + drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 5 +- drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c | 70 ++++++++++++++++--- drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 5 ++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 13 ++-- 5 files changed, 77 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h index 74dd9956144e4..c5feef8dc27d1 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h @@ -15,5 +15,6 @@ #define HIBMC_DP_CLK_EN 0x7 #define HIBMC_DP_SYNC_EN_MASK 0x3 #define HIBMC_DP_LINK_RATE_CAL 27 +#define HIBMC_DP_SYNC_DELAY(lanes) ((lanes) == 0x2 ? 86 : 46) #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c index 3612f3c5ab231..dcb2ab5ea6bb8 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c @@ -72,6 +72,9 @@ static void hibmc_dp_set_sst(struct hibmc_dp_dev *dp, struct drm_display_mode *m HIBMC_DP_CFG_STREAM_HTOTAL_SIZE, htotal_size); hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_HORIZONTAL_SIZE, HIBMC_DP_CFG_STREAM_HBLANK_SIZE, hblank_size); + hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_PACKET, + HIBMC_DP_CFG_STREAM_SYNC_CALIBRATION, + HIBMC_DP_SYNC_DELAY(dp->link.cap.lanes)); } static void hibmc_dp_link_cfg(struct hibmc_dp_dev *dp, struct drm_display_mode *mode) @@ -171,7 +174,7 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp) return ret; dp_dev->link.cap.lanes = 0x2; - dp_dev->link.cap.link_rate = DP_LINK_BW_2_7; + dp_dev->link.cap.link_rate = DP_LINK_BW_8_1; /* hdcp data */ writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG); diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c index f6355c16cc0ab..b1fcfaa8354f6 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c @@ -9,6 +9,22 @@ #define HIBMC_EQ_MAX_RETRY 5 +static inline int hibmc_dp_get_serdes_rate_cfg(struct hibmc_dp_dev *dp) +{ + switch (dp->link.cap.link_rate) { + case DP_LINK_BW_1_62: + return DP_SERDES_BW_1_62; + case DP_LINK_BW_2_7: + return DP_SERDES_BW_2_7; + case DP_LINK_BW_5_4: + return DP_SERDES_BW_5_4; + case DP_LINK_BW_8_1: + return DP_SERDES_BW_8_1; + default: + return -EINVAL; + } +} + static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp) { u8 buf[2]; @@ -41,11 +57,7 @@ static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp) return ret >= 0 ? -EIO : ret; } - ret = drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd); - if (ret) - drm_err(dp->dev, "dp aux read dpcd failed, ret: %d\n", ret); - - return ret; + return 0; } static int hibmc_dp_link_set_pattern(struct hibmc_dp_dev *dp, int pattern) @@ -108,7 +120,11 @@ static int hibmc_dp_link_training_cr_pre(struct hibmc_dp_dev *dp) return ret; for (i = 0; i < dp->link.cap.lanes; i++) - train_set[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL_2; + train_set[i] = DP_TRAIN_VOLTAGE_SWING_LEVEL_0; + + ret = hibmc_dp_serdes_set_tx_cfg(dp, dp->link.train_set); + if (ret) + return ret; ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->link.cap.lanes); if (ret != dp->link.cap.lanes) { @@ -137,21 +153,29 @@ static bool hibmc_dp_link_get_adjust_train(struct hibmc_dp_dev *dp, return false; } -static inline int hibmc_dp_link_reduce_rate(struct hibmc_dp_dev *dp) +static int hibmc_dp_link_reduce_rate(struct hibmc_dp_dev *dp) { + int ret; + switch (dp->link.cap.link_rate) { case DP_LINK_BW_2_7: dp->link.cap.link_rate = DP_LINK_BW_1_62; - return 0; + break; case DP_LINK_BW_5_4: dp->link.cap.link_rate = DP_LINK_BW_2_7; - return 0; + break; case DP_LINK_BW_8_1: dp->link.cap.link_rate = DP_LINK_BW_5_4; - return 0; + break; default: return -EINVAL; } + + ret = hibmc_dp_get_serdes_rate_cfg(dp); + if (ret < 0) + return ret; + + return hibmc_dp_serdes_rate_switch(ret, dp); } static inline int hibmc_dp_link_reduce_lane(struct hibmc_dp_dev *dp) @@ -159,6 +183,7 @@ static inline int hibmc_dp_link_reduce_lane(struct hibmc_dp_dev *dp) switch (dp->link.cap.lanes) { case 0x2: dp->link.cap.lanes--; + drm_dbg_dp(dp->dev, "dp link training reduce to 1 lane\n"); break; case 0x1: drm_err(dp->dev, "dp link training reduce lane failed, already reach minimum\n"); @@ -206,6 +231,11 @@ static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp) } level_changed = hibmc_dp_link_get_adjust_train(dp, lane_status); + + ret = hibmc_dp_serdes_set_tx_cfg(dp, dp->link.train_set); + if (ret) + return ret; + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set, dp->link.cap.lanes); if (ret != dp->link.cap.lanes) { @@ -255,6 +285,11 @@ static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp) } hibmc_dp_link_get_adjust_train(dp, lane_status); + + ret = hibmc_dp_serdes_set_tx_cfg(dp, dp->link.train_set); + if (ret) + return ret; + ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set, dp->link.cap.lanes); if (ret != dp->link.cap.lanes) { @@ -295,6 +330,21 @@ int hibmc_dp_link_training(struct hibmc_dp_dev *dp) struct hibmc_dp_link *link = &dp->link; int ret; + ret = drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd); + if (ret) + drm_err(dp->dev, "dp aux read dpcd failed, ret: %d\n", ret); + + dp->link.cap.link_rate = dp->dpcd[DP_MAX_LINK_RATE]; + dp->link.cap.lanes = 0x2; + + ret = hibmc_dp_get_serdes_rate_cfg(dp); + if (ret < 0) + return ret; + + ret = hibmc_dp_serdes_rate_switch(ret, dp); + if (ret) + return ret; + while (true) { ret = hibmc_dp_link_training_cr_pre(dp); if (ret) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index 16ea589035980..6eb76decc636e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -54,6 +54,7 @@ #define HIBMC_DP_VIDEO_PACKET 0x114 #define HIBMC_DP_CFG_STREAM_TU_SYMBOL_SIZE GENMASK(5, 0) #define HIBMC_DP_CFG_STREAM_TU_SYMBOL_FRAC_SIZE GENMASK(9, 6) +#define HIBMC_DP_CFG_STREAM_SYNC_CALIBRATION GENMASK(31, 20) #define HIBMC_DP_VIDEO_MSA0 0x118 #define HIBMC_DP_CFG_STREAM_VSTART GENMASK(31, 16) @@ -102,6 +103,7 @@ #define HIBMC_DP_LANE_STATUS_OFFSET 0x10 #define HIBMC_DP_PMA_LANE0_OFFSET 0x18 #define HIBMC_DP_PMA_LANE1_OFFSET 0x1c +#define HIBMC_DP_HOST_SERDES_CTRL 0x1f001c #define HIBMC_DP_PMA_TXDEEMPH GENMASK(18, 1) #define DP_SERDES_DONE 0x3 @@ -117,5 +119,8 @@ #define DP_SERDES_VOL2_PRE1 0x4500 #define DP_SERDES_VOL3_PRE0 0x600 #define DP_SERDES_BW_8_1 0x3 +#define DP_SERDES_BW_5_4 0x2 +#define DP_SERDES_BW_2_7 0x1 +#define DP_SERDES_BW_1_62 0x0 #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 21daffd845d06..ce0955b8678d7 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -26,9 +26,7 @@ #include "hibmc_drm_drv.h" #include "hibmc_drm_regs.h" -#define HIBMC_DP_HOST_SERDES_CTRL 0x1f001c -#define HIBMC_DP_HOST_SERDES_CTRL_VAL 0x8a00 -#define HIBMC_DP_HOST_SERDES_CTRL_MASK 0x7ffff +#include "dp/dp_reg.h" DEFINE_DRM_GEM_FOPS(hibmc_fops); @@ -119,9 +117,12 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) return ret; } - /* if DP existed, init DP */ - if ((readl(priv->mmio + HIBMC_DP_HOST_SERDES_CTRL) & - HIBMC_DP_HOST_SERDES_CTRL_MASK) == HIBMC_DP_HOST_SERDES_CTRL_VAL) { + /* + * If the serdes reg is readable and is not equal to 0, + * DP block exists and initializes it. + */ + ret = readl(priv->mmio + HIBMC_DP_HOST_SERDES_CTRL); + if (ret) { ret = hibmc_dp_init(priv); if (ret) drm_err(dev, "failed to init dp: %d\n", ret); From b7fd38a148695aebcb0e933a76309d775eafd311 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:25:02 +0800 Subject: [PATCH 174/227] drm/hisilicon/hibmc: Refactor the member of drm_aux in struct hibmc_dp maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=1e7f35512e77dd7276e91ade4e03807f88b97eb3 ---------------------------------------------------------------------- Because the drm_aux of struct hibmc_dp_dev's member is not easy to get in hibmc_drm_dp.c, move the drm_aux to struct hibmc_dp. Then there are some adaptations and modifications to make this patch compile. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-5-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c | 13 +++++++----- drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 6 ++++-- drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 2 +- drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 2 ++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c | 22 ++++++++++---------- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c index 0a903cce1fa95..ded9e7ce887a1 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c @@ -8,6 +8,7 @@ #include #include "dp_comm.h" #include "dp_reg.h" +#include "dp_hw.h" #define HIBMC_AUX_CMD_REQ_LEN GENMASK(7, 4) #define HIBMC_AUX_CMD_ADDR GENMASK(27, 8) @@ -124,7 +125,8 @@ static int hibmc_dp_aux_parse_xfer(struct hibmc_dp_dev *dp, struct drm_dp_aux_ms /* ret >= 0 ,ret is size; ret < 0, ret is err code */ static ssize_t hibmc_dp_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg) { - struct hibmc_dp_dev *dp = container_of(aux, struct hibmc_dp_dev, aux); + struct hibmc_dp *dp_priv = container_of(aux, struct hibmc_dp, aux); + struct hibmc_dp_dev *dp = dp_priv->dp_dev; u32 aux_cmd; int ret; u32 val; /* val will be assigned at the beginning of readl_poll_timeout function */ @@ -151,14 +153,15 @@ static ssize_t hibmc_dp_aux_xfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg * return hibmc_dp_aux_parse_xfer(dp, msg); } -void hibmc_dp_aux_init(struct hibmc_dp_dev *dp) +void hibmc_dp_aux_init(struct hibmc_dp *dp) { - hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0); - hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1); - hibmc_dp_reg_write_field(dp, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_MIN_PULSE_NUM, + hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0); + hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1); + hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_MIN_PULSE_NUM, HIBMC_DP_MIN_PULSE_NUM); dp->aux.transfer = hibmc_dp_aux_xfer; dp->aux.is_remote = 0; drm_dp_aux_init(&dp->aux); + dp->dp_dev->aux = &dp->aux; } diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h index 02a148b776468..a789bdb5867ee 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h @@ -13,6 +13,8 @@ #include #include +#include "dp_hw.h" + #define HIBMC_DP_LANE_NUM_MAX 2 struct hibmc_link_status { @@ -32,7 +34,7 @@ struct hibmc_dp_link { }; struct hibmc_dp_dev { - struct drm_dp_aux aux; + struct drm_dp_aux *aux; struct drm_device *dev; void __iomem *base; struct mutex lock; /* protects concurrent RW in hibmc_dp_reg_write_field() */ @@ -58,7 +60,7 @@ struct hibmc_dp_dev { mutex_unlock(&_dp->lock); \ } while (0) -void hibmc_dp_aux_init(struct hibmc_dp_dev *dp); +void hibmc_dp_aux_init(struct hibmc_dp *dp); int hibmc_dp_link_training(struct hibmc_dp_dev *dp); int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp); int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp); diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c index dcb2ab5ea6bb8..aa9354a996c9a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c @@ -167,7 +167,7 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp) dp_dev->dev = drm_dev; dp_dev->base = dp->mmio + HIBMC_DP_OFFSET; - hibmc_dp_aux_init(dp_dev); + hibmc_dp_aux_init(dp); ret = hibmc_dp_serdes_init(dp_dev); if (ret) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h index 4dc13b3d9875f..13c1f1c0be04d 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -10,6 +10,7 @@ #include #include #include +#include struct hibmc_dp_dev; @@ -19,6 +20,7 @@ struct hibmc_dp { struct drm_encoder encoder; struct drm_connector connector; void __iomem *mmio; + struct drm_dp_aux aux; }; int hibmc_dp_hw_init(struct hibmc_dp *dp); diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c index b1fcfaa8354f6..21cad69c37575 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c @@ -42,7 +42,7 @@ static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp) /* set rate and lane count */ buf[0] = dp->link.cap.link_rate; buf[1] = DP_LANE_COUNT_ENHANCED_FRAME_EN | dp->link.cap.lanes; - ret = drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, buf, sizeof(buf)); + ret = drm_dp_dpcd_write(dp->aux, DP_LINK_BW_SET, buf, sizeof(buf)); if (ret != sizeof(buf)) { drm_dbg_dp(dp->dev, "dp aux write link rate and lanes failed, ret: %d\n", ret); return ret >= 0 ? -EIO : ret; @@ -51,7 +51,7 @@ static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp) /* set 8b/10b and downspread */ buf[0] = DP_SPREAD_AMP_0_5; buf[1] = DP_SET_ANSI_8B10B; - ret = drm_dp_dpcd_write(&dp->aux, DP_DOWNSPREAD_CTRL, buf, sizeof(buf)); + ret = drm_dp_dpcd_write(dp->aux, DP_DOWNSPREAD_CTRL, buf, sizeof(buf)); if (ret != sizeof(buf)) { drm_dbg_dp(dp->dev, "dp aux write 8b/10b and downspread failed, ret: %d\n", ret); return ret >= 0 ? -EIO : ret; @@ -96,7 +96,7 @@ static int hibmc_dp_link_set_pattern(struct hibmc_dp_dev *dp, int pattern) hibmc_dp_reg_write_field(dp, HIBMC_DP_PHYIF_CTRL0, HIBMC_DP_CFG_PAT_SEL, val); - ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_PATTERN_SET, &buf, sizeof(buf)); + ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_PATTERN_SET, &buf, sizeof(buf)); if (ret != sizeof(buf)) { drm_dbg_dp(dp->dev, "dp aux write training pattern set failed\n"); return ret >= 0 ? -EIO : ret; @@ -126,7 +126,7 @@ static int hibmc_dp_link_training_cr_pre(struct hibmc_dp_dev *dp) if (ret) return ret; - ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->link.cap.lanes); + ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_LANE0_SET, train_set, dp->link.cap.lanes); if (ret != dp->link.cap.lanes) { drm_dbg_dp(dp->dev, "dp aux write training lane set failed\n"); return ret >= 0 ? -EIO : ret; @@ -210,9 +210,9 @@ static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp) voltage_tries = 1; for (cr_tries = 0; cr_tries < 80; cr_tries++) { - drm_dp_link_train_clock_recovery_delay(&dp->aux, dp->dpcd); + drm_dp_link_train_clock_recovery_delay(dp->aux, dp->dpcd); - ret = drm_dp_dpcd_read_link_status(&dp->aux, lane_status); + ret = drm_dp_dpcd_read_link_status(dp->aux, lane_status); if (ret != DP_LINK_STATUS_SIZE) { drm_err(dp->dev, "Get lane status failed\n"); return ret; @@ -236,7 +236,7 @@ static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp) if (ret) return ret; - ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set, + ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set, dp->link.cap.lanes); if (ret != dp->link.cap.lanes) { drm_dbg_dp(dp->dev, "Update link training failed\n"); @@ -263,9 +263,9 @@ static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp) return ret; for (eq_tries = 0; eq_tries < HIBMC_EQ_MAX_RETRY; eq_tries++) { - drm_dp_link_train_channel_eq_delay(&dp->aux, dp->dpcd); + drm_dp_link_train_channel_eq_delay(dp->aux, dp->dpcd); - ret = drm_dp_dpcd_read_link_status(&dp->aux, lane_status); + ret = drm_dp_dpcd_read_link_status(dp->aux, lane_status); if (ret != DP_LINK_STATUS_SIZE) { drm_err(dp->dev, "get lane status failed\n"); break; @@ -290,7 +290,7 @@ static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp) if (ret) return ret; - ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, + ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set, dp->link.cap.lanes); if (ret != dp->link.cap.lanes) { drm_dbg_dp(dp->dev, "Update link training failed\n"); @@ -330,7 +330,7 @@ int hibmc_dp_link_training(struct hibmc_dp_dev *dp) struct hibmc_dp_link *link = &dp->link; int ret; - ret = drm_dp_read_dpcd_caps(&dp->aux, dp->dpcd); + ret = drm_dp_read_dpcd_caps(dp->aux, dp->dpcd); if (ret) drm_err(dp->dev, "dp aux read dpcd failed, ret: %d\n", ret); From 67617b55a1f4fc94aa0c376726cb6948ffa93ba3 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:25:03 +0800 Subject: [PATCH 175/227] drm/hisilicon/hibmc: Getting connector info and EDID by using AUX channel maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=bd1c935811ae6bd112321c50ed83444eca4facc8 ---------------------------------------------------------------------- Add registering drm_aux and use it to get connector edid with drm functions. Add ddc channel in connector initialization to put drm_aux in drm_connector. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-6-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c | 3 +- .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 31 ++++++++++++++++--- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 5 +++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c index ded9e7ce887a1..8732cd1d8cb6c 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_aux.c @@ -161,7 +161,8 @@ void hibmc_dp_aux_init(struct hibmc_dp *dp) HIBMC_DP_MIN_PULSE_NUM); dp->aux.transfer = hibmc_dp_aux_xfer; - dp->aux.is_remote = 0; + dp->aux.name = "HIBMC DRM dp aux"; + dp->aux.drm_dev = dp->drm_dev; drm_dp_aux_init(&dp->aux); dp->dp_dev->aux = &dp->aux; } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index 603d6b198a540..8e5ee816dfbd9 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -15,11 +15,16 @@ static int hibmc_dp_connector_get_modes(struct drm_connector *connector) { + const struct drm_edid *drm_edid; int count; - count = drm_add_modes_noedid(connector, connector->dev->mode_config.max_width, - connector->dev->mode_config.max_height); - drm_set_preferred_mode(connector, 1024, 768); // temporary implementation + drm_edid = drm_edid_read(connector); + + drm_edid_connector_update(connector, drm_edid); + + count = drm_edid_connector_add_modes(connector); + + drm_edid_free(drm_edid); return count; } @@ -28,12 +33,28 @@ static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = { .get_modes = hibmc_dp_connector_get_modes, }; +static int hibmc_dp_late_register(struct drm_connector *connector) +{ + struct hibmc_dp *dp = to_hibmc_dp(connector); + + return drm_dp_aux_register(&dp->aux); +} + +static void hibmc_dp_early_unregister(struct drm_connector *connector) +{ + struct hibmc_dp *dp = to_hibmc_dp(connector); + + drm_dp_aux_unregister(&dp->aux); +} + static const struct drm_connector_funcs hibmc_dp_conn_funcs = { .reset = drm_atomic_helper_connector_reset, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = drm_connector_cleanup, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .late_register = hibmc_dp_late_register, + .early_unregister = hibmc_dp_early_unregister, }; static inline int hibmc_dp_prepare(struct hibmc_dp *dp, struct drm_display_mode *mode) @@ -103,8 +124,8 @@ int hibmc_dp_init(struct hibmc_drm_private *priv) drm_encoder_helper_add(encoder, &hibmc_dp_encoder_helper_funcs); - ret = drm_connector_init(dev, connector, &hibmc_dp_conn_funcs, - DRM_MODE_CONNECTOR_DisplayPort); + ret = drm_connector_init_with_ddc(dev, connector, &hibmc_dp_conn_funcs, + DRM_MODE_CONNECTOR_DisplayPort, &dp->aux.ddc); if (ret) { drm_err(dev, "init dp connector failed: %d\n", ret); return ret; diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index a2dd1261035a3..a7994001862d4 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -48,6 +48,11 @@ static inline struct hibmc_vdac *to_hibmc_vdac(struct drm_connector *connector) return container_of(connector, struct hibmc_vdac, connector); } +static inline struct hibmc_dp *to_hibmc_dp(struct drm_connector *connector) +{ + return container_of(connector, struct hibmc_dp, connector); +} + static inline struct hibmc_drm_private *to_hibmc_drm_private(struct drm_device *dev) { return container_of(dev, struct hibmc_drm_private, dev); From b4f0240ab38d21dc74f566e1db6b06efb100f618 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:25:04 +0800 Subject: [PATCH 176/227] drm/hisilicon/hibmc: Add colorbar-cfg feature and its debugfs file maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=2f6182616cfdb154e2ecfe9554bb814b8a6378e9 ---------------------------------------------------------------------- DP controller can support generating a color bar signal over the DisplayPort interface. This can be useful to check for possible DDR or GPU problems, as the signal generator resides completely in the DP block. Add debugfs file that controls colorbar generator. echo: config the color bar register to display cat: print the color bar configuration Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-7-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/Makefile | 3 +- drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 43 ++++++++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 29 +++++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 3 + .../drm/hisilicon/hibmc/hibmc_drm_debugfs.c | 104 ++++++++++++++++++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 1 + .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 2 + 7 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c diff --git a/drivers/gpu/drm/hisilicon/hibmc/Makefile b/drivers/gpu/drm/hisilicon/hibmc/Makefile index 43de077d6769a..1f65c683282f9 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/Makefile +++ b/drivers/gpu/drm/hisilicon/hibmc/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only hibmc-drm-y := hibmc_drm_drv.o hibmc_drm_de.o hibmc_drm_vdac.o hibmc_drm_i2c.o \ - dp/dp_aux.o dp/dp_link.o dp/dp_hw.o dp/dp_serdes.o hibmc_drm_dp.o + dp/dp_aux.o dp/dp_link.o dp/dp_hw.o dp/dp_serdes.o hibmc_drm_dp.o \ + hibmc_drm_debugfs.o obj-$(CONFIG_DRM_HISI_HIBMC) += hibmc-drm.o diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c index aa9354a996c9a..ce7cb07815b22 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c @@ -226,3 +226,46 @@ int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode) return 0; } + +static const struct hibmc_dp_color_raw g_rgb_raw[] = { + {CBAR_COLOR_BAR, 0x000, 0x000, 0x000}, + {CBAR_WHITE, 0xfff, 0xfff, 0xfff}, + {CBAR_RED, 0xfff, 0x000, 0x000}, + {CBAR_ORANGE, 0xfff, 0x800, 0x000}, + {CBAR_YELLOW, 0xfff, 0xfff, 0x000}, + {CBAR_GREEN, 0x000, 0xfff, 0x000}, + {CBAR_CYAN, 0x000, 0x800, 0x800}, + {CBAR_BLUE, 0x000, 0x000, 0xfff}, + {CBAR_PURPLE, 0x800, 0x000, 0x800}, + {CBAR_BLACK, 0x000, 0x000, 0x000}, +}; + +void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg) +{ + struct hibmc_dp_dev *dp_dev = dp->dp_dev; + struct hibmc_dp_color_raw raw_data; + + if (cfg->enable) { + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(9), + cfg->self_timing); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, GENMASK(8, 1), + cfg->dynamic_rate); + if (cfg->pattern == CBAR_COLOR_BAR) { + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(10), 0); + } else { + raw_data = g_rgb_raw[cfg->pattern]; + drm_dbg_dp(dp->drm_dev, "r:%x g:%x b:%x\n", raw_data.r_value, + raw_data.g_value, raw_data.b_value); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(10), 1); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, GENMASK(23, 12), + raw_data.r_value); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL1, GENMASK(23, 12), + raw_data.g_value); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL1, GENMASK(11, 0), + raw_data.b_value); + } + } + + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(0), cfg->enable); + writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h index 13c1f1c0be04d..9c591576a6c70 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -14,6 +14,33 @@ struct hibmc_dp_dev; +enum hibmc_dp_cbar_pattern { + CBAR_COLOR_BAR, + CBAR_WHITE, + CBAR_RED, + CBAR_ORANGE, + CBAR_YELLOW, + CBAR_GREEN, + CBAR_CYAN, + CBAR_BLUE, + CBAR_PURPLE, + CBAR_BLACK, +}; + +struct hibmc_dp_color_raw { + enum hibmc_dp_cbar_pattern pattern; + u32 r_value; + u32 g_value; + u32 b_value; +}; + +struct hibmc_dp_cbar_cfg { + u8 enable; + u8 self_timing; + u8 dynamic_rate; /* 0:static, 1-255(frame):dynamic */ + enum hibmc_dp_cbar_pattern pattern; +}; + struct hibmc_dp { struct hibmc_dp_dev *dp_dev; struct drm_device *drm_dev; @@ -21,10 +48,12 @@ struct hibmc_dp { struct drm_connector connector; void __iomem *mmio; struct drm_dp_aux aux; + struct hibmc_dp_cbar_cfg cfg; }; int hibmc_dp_hw_init(struct hibmc_dp *dp); int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode); void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable); +void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg); #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index 6eb76decc636e..5614b727a710e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -67,6 +67,9 @@ #define HIBMC_DP_CFG_STREAM_HTOTAL_SIZE GENMASK(31, 16) #define HIBMC_DP_CFG_STREAM_HBLANK_SIZE GENMASK(15, 0) +#define HIBMC_DP_COLOR_BAR_CTRL 0x260 +#define HIBMC_DP_COLOR_BAR_CTRL1 0x264 + #define HIBMC_DP_TIMING_GEN_CONFIG0 0x26c #define HIBMC_DP_CFG_TIMING_GEN0_HACTIVE GENMASK(31, 16) #define HIBMC_DP_CFG_TIMING_GEN0_HBLANK GENMASK(15, 0) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c new file mode 100644 index 0000000000000..f585387c3a49f --- /dev/null +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// Copyright (c) 2024 Hisilicon Limited. + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "hibmc_drm_drv.h" + +#define MAX_BUF_SIZE 12 + +static ssize_t hibmc_control_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct hibmc_drm_private *priv = file_inode(file)->i_private; + struct hibmc_dp_cbar_cfg *cfg = &priv->dp.cfg; + int ret, idx; + u8 buf[MAX_BUF_SIZE]; + + if (count >= MAX_BUF_SIZE) + return -EINVAL; + + if (copy_from_user(buf, user_buf, count)) + return -EFAULT; + + buf[count] = '\0'; + + /* Only 4 parameters is allowed, the ranger are as follow: + * [0] enable/disable colorbar feature + 0: enable colorbar, 1: disable colorbar + * [1] the timing source of colorbar displaying + 0: timing follows XDP, 1: internal self timing + * [2] the movment of colorbar displaying + 0: static colorbar image, + * 1~255: right shifting a type of color per (1~255)frames + * [3] the color type of colorbar displaying + 0~9: color bar, white, red, orange, + * yellow, green, cyan, bule, pupper, black + */ + if (sscanf(buf, "%hhu %hhu %hhu %u", &cfg->enable, &cfg->self_timing, + &cfg->dynamic_rate, &cfg->pattern) != 4) { + return -EINVAL; + } + + if (cfg->pattern > 9 || cfg->enable > 1 || cfg->self_timing > 1) + return -EINVAL; + + ret = drm_dev_enter(&priv->dev, &idx); + if (!ret) + return -ENODEV; + + hibmc_dp_set_cbar(&priv->dp, cfg); + + drm_dev_exit(idx); + + return count; +} + +static int hibmc_dp_dbgfs_show(struct seq_file *m, void *arg) +{ + struct hibmc_drm_private *priv = m->private; + struct hibmc_dp_cbar_cfg *cfg = &priv->dp.cfg; + int idx; + + if (!drm_dev_enter(&priv->dev, &idx)) + return -ENODEV; + + seq_printf(m, "hibmc dp colorbar cfg: %u %u %u %u\n", cfg->enable, cfg->self_timing, + cfg->dynamic_rate, cfg->pattern); + + drm_dev_exit(idx); + + return 0; +} + +static int hibmc_open(struct inode *inode, struct file *filp) +{ + return single_open(filp, hibmc_dp_dbgfs_show, inode->i_private); +} + +static const struct file_operations hibmc_dbg_fops = { + .owner = THIS_MODULE, + .write = hibmc_control_write, + .read = seq_read, + .open = hibmc_open, + .llseek = seq_lseek, + .release = single_release, +}; + +void hibmc_debugfs_init(struct drm_connector *connector, struct dentry *root) +{ + struct drm_device *dev = connector->dev; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + + /* create the file in drm directory, so we don't need to remove manually */ + debugfs_create_file("colorbar-cfg", 0200, + root, priv, &hibmc_dbg_fops); +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index 8e5ee816dfbd9..a86ecce4b3c14 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -55,6 +55,7 @@ static const struct drm_connector_funcs hibmc_dp_conn_funcs = { .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, .late_register = hibmc_dp_late_register, .early_unregister = hibmc_dp_early_unregister, + .debugfs_init = hibmc_debugfs_init, }; static inline int hibmc_dp_prepare(struct hibmc_dp *dp, struct drm_display_mode *mode) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index a7994001862d4..9b2419b1765c5 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -70,4 +70,6 @@ int hibmc_mm_init(struct hibmc_drm_private *hibmc); int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *connector); int hibmc_dp_init(struct hibmc_drm_private *priv); +void hibmc_debugfs_init(struct drm_connector *connector, struct dentry *root); + #endif From 167e48a52de8627ebe54274b16fa6e5ebded1759 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:25:05 +0800 Subject: [PATCH 177/227] drm/hisilicon/hibmc: Enable this hot plug detect of irq feature maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=3c7623fb5bb6c319531b941b15b7bfc12455d3d3 ---------------------------------------------------------------------- Add HPD interrupt enable functions in drm framework, and also add detect_ctx functions. Because of the debouncing when HPD pulled out, add 200 ms delay in detect. Add link reset process to reset link status when a new connector pulgged in. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-8-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- .../gpu/drm/hisilicon/hibmc/dp/dp_config.h | 1 + drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 36 ++++++++++++++++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 5 +++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 42 +++++++++++++++++++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 2 + 5 files changed, 86 insertions(+) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h index c5feef8dc27d1..08f9e1caf7fcb 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_config.h @@ -16,5 +16,6 @@ #define HIBMC_DP_SYNC_EN_MASK 0x3 #define HIBMC_DP_LINK_RATE_CAL 27 #define HIBMC_DP_SYNC_DELAY(lanes) ((lanes) == 0x2 ? 86 : 46) +#define HIBMC_DP_INT_ENABLE 0xc #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c index ce7cb07815b22..8f0daec7d1749 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c @@ -189,6 +189,36 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp) return 0; } +void hibmc_dp_enable_int(struct hibmc_dp *dp) +{ + struct hibmc_dp_dev *dp_dev = dp->dp_dev; + + writel(HIBMC_DP_INT_ENABLE, dp_dev->base + HIBMC_DP_INTR_ENABLE); +} + +void hibmc_dp_disable_int(struct hibmc_dp *dp) +{ + struct hibmc_dp_dev *dp_dev = dp->dp_dev; + + writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE); + writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS); +} + +void hibmc_dp_hpd_cfg(struct hibmc_dp *dp) +{ + struct hibmc_dp_dev *dp_dev = dp->dp_dev; + + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_SYNC_LEN_SEL, 0x0); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_TIMER_TIMEOUT, 0x1); + hibmc_dp_reg_write_field(dp->dp_dev, HIBMC_DP_AUX_REQ, HIBMC_DP_CFG_AUX_MIN_PULSE_NUM, 0x9); + writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG); + writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE); + writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS); + writel(HIBMC_DP_INT_ENABLE, dp_dev->base + HIBMC_DP_INTR_ENABLE); + writel(HIBMC_DP_DPTX_RST, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL); + writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL); +} + void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable) { struct hibmc_dp_dev *dp_dev = dp->dp_dev; @@ -227,6 +257,12 @@ int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode) return 0; } +void hibmc_dp_reset_link(struct hibmc_dp *dp) +{ + dp->dp_dev->link.status.clock_recovered = false; + dp->dp_dev->link.status.channel_equalized = false; +} + static const struct hibmc_dp_color_raw g_rgb_raw[] = { {CBAR_COLOR_BAR, 0x000, 0x000, 0x000}, {CBAR_WHITE, 0xfff, 0xfff, 0xfff}, diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h index 9c591576a6c70..c2d27c2f8beb3 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -49,11 +49,16 @@ struct hibmc_dp { void __iomem *mmio; struct drm_dp_aux aux; struct hibmc_dp_cbar_cfg cfg; + u32 irq_status; }; int hibmc_dp_hw_init(struct hibmc_dp *dp); int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode); void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable); void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg); +void hibmc_dp_reset_link(struct hibmc_dp *dp); +void hibmc_dp_hpd_cfg(struct hibmc_dp *dp); +void hibmc_dp_enable_int(struct hibmc_dp *dp); +void hibmc_dp_disable_int(struct hibmc_dp *dp); #endif diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index a86ecce4b3c14..d06832e62e966 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -13,6 +13,8 @@ #include "hibmc_drm_drv.h" #include "dp/dp_hw.h" +#define DP_MASKED_SINK_HPD_PLUG_INT BIT(2) + static int hibmc_dp_connector_get_modes(struct drm_connector *connector) { const struct drm_edid *drm_edid; @@ -29,14 +31,25 @@ static int hibmc_dp_connector_get_modes(struct drm_connector *connector) return count; } +static int hibmc_dp_detect(struct drm_connector *connector, + struct drm_modeset_acquire_ctx *ctx, bool force) +{ + mdelay(200); + + return drm_connector_helper_detect_from_ddc(connector, ctx, force); +} + static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = { .get_modes = hibmc_dp_connector_get_modes, + .detect_ctx = hibmc_dp_detect, }; static int hibmc_dp_late_register(struct drm_connector *connector) { struct hibmc_dp *dp = to_hibmc_dp(connector); + hibmc_dp_enable_int(dp); + return drm_dp_aux_register(&dp->aux); } @@ -45,6 +58,8 @@ static void hibmc_dp_early_unregister(struct drm_connector *connector) struct hibmc_dp *dp = to_hibmc_dp(connector); drm_dp_aux_unregister(&dp->aux); + + hibmc_dp_disable_int(dp); } static const struct drm_connector_funcs hibmc_dp_conn_funcs = { @@ -96,6 +111,31 @@ static const struct drm_encoder_helper_funcs hibmc_dp_encoder_helper_funcs = { .atomic_disable = hibmc_dp_encoder_disable, }; +irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + int idx; + + if (!drm_dev_enter(dev, &idx)) + return -ENODEV; + + if (priv->dp.irq_status & DP_MASKED_SINK_HPD_PLUG_INT) { + drm_dbg_dp(&priv->dev, "HPD IN isr occur!\n"); + hibmc_dp_hpd_cfg(&priv->dp); + } else { + drm_dbg_dp(&priv->dev, "HPD OUT isr occur!\n"); + hibmc_dp_reset_link(&priv->dp); + } + + if (dev->registered) + drm_connector_helper_hpd_irq_event(&priv->dp.connector); + + drm_dev_exit(idx); + + return IRQ_HANDLED; +} + int hibmc_dp_init(struct hibmc_drm_private *priv) { struct drm_device *dev = &priv->dev; @@ -136,5 +176,7 @@ int hibmc_dp_init(struct hibmc_drm_private *priv) drm_connector_attach_encoder(connector, encoder); + connector->polled = DRM_CONNECTOR_POLL_HPD; + return 0; } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index 9b2419b1765c5..4c060d270f9ae 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -72,4 +72,6 @@ int hibmc_dp_init(struct hibmc_drm_private *priv); void hibmc_debugfs_init(struct drm_connector *connector, struct dentry *root); +irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg); + #endif From 04f09885f3fe9f6fd14ac41e1600d12bebd43502 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:25:06 +0800 Subject: [PATCH 178/227] drm/hisilicon/hibmc: Add MSI irq getting and requesting for HPD maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=b11bc1ae46587f3563c47078e605184f18e7fa57 ---------------------------------------------------------------------- To realize HPD feature, request irq for HPD , add its handler function. We use pci_alloc_irq_vectors() to get our msi irq, because we have two interrupts now. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-9-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 3 + .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 74 +++++++++++++++---- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 3 + 3 files changed, 66 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index 5614b727a710e..394b1e933c3ae 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -99,6 +99,9 @@ #define HIBMC_DP_TIMING_SYNC_CTRL 0xFF0 +#define HIBMC_DP_INTSTAT 0x1e0724 +#define HIBMC_DP_INTCLR 0x1e0728 + /* dp serdes reg */ #define HIBMC_DP_HOST_OFFSET 0x10000 #define HIBMC_DP_LANE0_RATE_OFFSET 0x4 diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index ce0955b8678d7..b09b8f41fcfcd 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -30,6 +30,8 @@ DEFINE_DRM_GEM_FOPS(hibmc_fops); +static const char *g_irqs_names_map[HIBMC_MAX_VECTORS] = { "vblank", "hpd" }; + static irqreturn_t hibmc_interrupt(int irq, void *arg) { struct drm_device *dev = (struct drm_device *)arg; @@ -47,6 +49,22 @@ static irqreturn_t hibmc_interrupt(int irq, void *arg) return IRQ_HANDLED; } +static irqreturn_t hibmc_dp_interrupt(int irq, void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + u32 status; + + status = readl(priv->mmio + HIBMC_DP_INTSTAT); + if (status) { + priv->dp.irq_status = status; + writel(status, priv->mmio + HIBMC_DP_INTCLR); + return IRQ_WAKE_THREAD; + } + + return IRQ_HANDLED; +} + static int hibmc_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { @@ -249,15 +267,48 @@ static int hibmc_hw_init(struct hibmc_drm_private *priv) return 0; } -static int hibmc_unload(struct drm_device *dev) +static void hibmc_unload(struct drm_device *dev) { - struct pci_dev *pdev = to_pci_dev(dev->dev); - drm_atomic_helper_shutdown(dev); +} - free_irq(pdev->irq, dev); +static int hibmc_msi_init(struct drm_device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev->dev); + char name[32] = {0}; + int valid_irq_num; + int irq; + int ret; - pci_disable_msi(to_pci_dev(dev->dev)); + ret = pci_alloc_irq_vectors(pdev, HIBMC_MIN_VECTORS, + HIBMC_MAX_VECTORS, PCI_IRQ_MSI); + if (ret < 0) { + drm_err(dev, "enabling MSI failed: %d\n", ret); + return ret; + } + + valid_irq_num = ret; + + for (int i = 0; i < valid_irq_num; i++) { + snprintf(name, ARRAY_SIZE(name) - 1, "%s-%s-%s", + dev->driver->name, pci_name(pdev), g_irqs_names_map[i]); + + irq = pci_irq_vector(pdev, i); + + if (i) + /* PCI devices require shared interrupts. */ + ret = devm_request_threaded_irq(&pdev->dev, irq, + hibmc_dp_interrupt, + hibmc_dp_hpd_isr, + IRQF_SHARED, name, dev); + else + ret = devm_request_irq(&pdev->dev, irq, hibmc_interrupt, + IRQF_SHARED, name, dev); + if (ret) { + drm_err(dev, "install irq failed: %d\n", ret); + return ret; + } + } return 0; } @@ -289,15 +340,10 @@ static int hibmc_load(struct drm_device *dev) goto err; } - ret = pci_enable_msi(pdev); + ret = hibmc_msi_init(dev); if (ret) { - drm_warn(dev, "enabling MSI failed: %d\n", ret); - } else { - /* PCI devices require shared interrupts. */ - ret = request_irq(pdev->irq, hibmc_interrupt, IRQF_SHARED, - dev->driver->name, dev); - if (ret) - drm_warn(dev, "install irq failed: %d\n", ret); + drm_err(dev, "hibmc msi init failed, ret:%d\n", ret); + goto err; } /* reset all the states of crtc/plane/encoder/connector */ @@ -373,7 +419,7 @@ static void hibmc_pci_remove(struct pci_dev *pdev) static void hibmc_pci_shutdown(struct pci_dev *pdev) { - drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); + hibmc_pci_remove(pdev); } static const struct pci_device_id hibmc_pci_table[] = { diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index 4c060d270f9ae..1b6fb9b022522 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -23,6 +23,9 @@ #include #include "dp/dp_hw.h" +#define HIBMC_MIN_VECTORS 1 +#define HIBMC_MAX_VECTORS 2 + struct hibmc_vdac { struct drm_device *dev; struct drm_encoder encoder; From ecad0b2cf8cead718b2c56a979fbc64795aff750 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Wed, 16 Apr 2025 16:25:07 +0800 Subject: [PATCH 179/227] drm/hisilicon/hibmc: Add vga connector detect functions maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA Reference: https://cgit.freedesktop.org/drm/drm-misc/commit/?id=4c962bc929f1734d209a0862359e25fef8f56fa0 ---------------------------------------------------------------------- Because the connected VGA connector would make driver can't get the userspace call, adding detect_ctx in vga connector to make HPD active userspace. Signed-off-by: Baihan Li Signed-off-by: Yongbang Shi Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20250331074212.3370287-10-shiyongbang@huawei.com Signed-off-by: Dmitry Baryshkov Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 82f3239ca7b40..a9f92099cc26b 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -56,6 +56,7 @@ static void hibmc_connector_destroy(struct drm_connector *connector) static const struct drm_connector_helper_funcs hibmc_connector_helper_funcs = { .get_modes = hibmc_connector_get_modes, + .detect_ctx = drm_connector_helper_detect_from_ddc, }; static const struct drm_connector_funcs hibmc_connector_funcs = { @@ -123,5 +124,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) drm_connector_attach_encoder(connector, encoder); + connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; + return 0; } From 64633a8ac44aace5c58791c55803cc087d620474 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Thu, 17 Apr 2025 15:52:13 +0800 Subject: [PATCH 180/227] drm/hisilicon/hibmc: fix HPD interrupts triggering the wrong behavior driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA ---------------------------------------------------------------------- The HPD out signal will be very fast sometimes that drm detect the connector is still connectted by aux channel in dp's detect_ctx(). In this case, driver will ignore this out signal, which will be a problem. Using hibmc_dp_detect() to detect the connector by a hpd_status flag, which will change to 1/0 when get an in/out IRQ. Fixes: 3c7623fb5bb6 ("drm/hisilicon/hibmc: Enable this hot plug detect of irq feature") Signed-off-by: Baihan Li Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 1 + .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 34 +++++++++++++------ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h index c2d27c2f8beb3..f55d78d266f21 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -50,6 +50,7 @@ struct hibmc_dp { struct drm_dp_aux aux; struct hibmc_dp_cbar_cfg cfg; u32 irq_status; + int hpd_status; }; int hibmc_dp_hw_init(struct hibmc_dp *dp); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index d06832e62e966..41a5d3a9625c6 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -13,7 +13,8 @@ #include "hibmc_drm_drv.h" #include "dp/dp_hw.h" -#define DP_MASKED_SINK_HPD_PLUG_INT BIT(2) +#define HIBMC_DP_MASKED_SINK_HPD_PLUG_INT BIT(2) +#define HIBMC_DP_MASKED_SINK_HPD_UNPLUG_INT BIT(3) static int hibmc_dp_connector_get_modes(struct drm_connector *connector) { @@ -34,9 +35,12 @@ static int hibmc_dp_connector_get_modes(struct drm_connector *connector) static int hibmc_dp_detect(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, bool force) { - mdelay(200); + struct hibmc_dp *dp = to_hibmc_dp(connector); - return drm_connector_helper_detect_from_ddc(connector, ctx, force); + if (dp->hpd_status) + return connector_status_connected; + else + return connector_status_disconnected; } static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = { @@ -115,22 +119,30 @@ irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg) { struct drm_device *dev = (struct drm_device *)arg; struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + struct hibmc_dp *dp = &priv->dp; int idx; if (!drm_dev_enter(dev, &idx)) return -ENODEV; - if (priv->dp.irq_status & DP_MASKED_SINK_HPD_PLUG_INT) { - drm_dbg_dp(&priv->dev, "HPD IN isr occur!\n"); - hibmc_dp_hpd_cfg(&priv->dp); + if (dp->hpd_status) { /* only check unplug int when the last status is HPD in */ + if ((dp->irq_status & HIBMC_DP_MASKED_SINK_HPD_UNPLUG_INT)) { + drm_dbg_dp(dev, "HPD OUT isr occur!\n"); + hibmc_dp_reset_link(dp); + dp->hpd_status = 0; + if (dev->registered) + drm_connector_helper_hpd_irq_event(&dp->connector); + } } else { - drm_dbg_dp(&priv->dev, "HPD OUT isr occur!\n"); - hibmc_dp_reset_link(&priv->dp); + if (dp->irq_status & HIBMC_DP_MASKED_SINK_HPD_PLUG_INT) { + drm_dbg_dp(&priv->dev, "HPD IN isr occur!\n"); + hibmc_dp_hpd_cfg(dp); + dp->hpd_status = 1; + if (dev->registered) + drm_connector_helper_hpd_irq_event(&dp->connector); + } } - if (dev->registered) - drm_connector_helper_hpd_irq_event(&priv->dp.connector); - drm_dev_exit(idx); return IRQ_HANDLED; From 4d14b2e02cc0882f7d2303adde0b45d485ac580e Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Thu, 17 Apr 2025 15:59:55 +0800 Subject: [PATCH 181/227] drm/hisilicon/hibmc: fix irq_request()'s irq name variable is local driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA ---------------------------------------------------------------------- fix irq_request()'s irq name variable is local. Fixes: b11bc1ae4658 ("drm/hisilicon/hibmc: Add MSI irq getting and requesting for HPD") Signed-off-by: Baihan Li Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index b09b8f41fcfcd..0036516ce55ab 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -30,7 +30,7 @@ DEFINE_DRM_GEM_FOPS(hibmc_fops); -static const char *g_irqs_names_map[HIBMC_MAX_VECTORS] = { "vblank", "hpd" }; +static const char *g_irqs_names_map[HIBMC_MAX_VECTORS] = { "hibmc-vblank", "hibmc-hpd" }; static irqreturn_t hibmc_interrupt(int irq, void *arg) { @@ -275,7 +275,6 @@ static void hibmc_unload(struct drm_device *dev) static int hibmc_msi_init(struct drm_device *dev) { struct pci_dev *pdev = to_pci_dev(dev->dev); - char name[32] = {0}; int valid_irq_num; int irq; int ret; @@ -289,10 +288,8 @@ static int hibmc_msi_init(struct drm_device *dev) valid_irq_num = ret; - for (int i = 0; i < valid_irq_num; i++) { - snprintf(name, ARRAY_SIZE(name) - 1, "%s-%s-%s", - dev->driver->name, pci_name(pdev), g_irqs_names_map[i]); - + int i; + for (i = 0; i < valid_irq_num; i++) { irq = pci_irq_vector(pdev, i); if (i) @@ -300,10 +297,10 @@ static int hibmc_msi_init(struct drm_device *dev) ret = devm_request_threaded_irq(&pdev->dev, irq, hibmc_dp_interrupt, hibmc_dp_hpd_isr, - IRQF_SHARED, name, dev); + IRQF_SHARED, g_irqs_names_map[i], dev); else ret = devm_request_irq(&pdev->dev, irq, hibmc_interrupt, - IRQF_SHARED, name, dev); + IRQF_SHARED, g_irqs_names_map[i], dev); if (ret) { drm_err(dev, "install irq failed: %d\n", ret); return ret; From 7cbc8f2598901696082fb7a29c6dbde8e5245b5a Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Tue, 27 May 2025 21:39:00 +0800 Subject: [PATCH 182/227] drm/hisilicon/hibmc:fix KVM is not displaying when GRUB VGA is on driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA ---------------------------------------------------------------------- Add vdac detect_ctx(), and make vdac is always connected when DP is disconnected. Add force() to eliminate the influence of VGA's GRUB on DP when HPD occurring. Fixes: 4c962bc929f1 ("drm/hisilicon/hibmc: Add vga connector detect functions") Signed-off-by: Baihan Li Signed-off-by: chenyi --- .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index a9f92099cc26b..9cf2c52fc4a0a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -53,18 +53,44 @@ static void hibmc_connector_destroy(struct drm_connector *connector) drm_connector_cleanup(connector); } +static int hibmc_vdac_detect(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, + bool force) +{ + struct hibmc_drm_private *priv = to_hibmc_drm_private(connector->dev); + struct hibmc_dp *dp = &priv->dp; + + if (dp->hpd_status) + return connector_status_disconnected; + + return connector_status_connected; +} + static const struct drm_connector_helper_funcs hibmc_connector_helper_funcs = { .get_modes = hibmc_connector_get_modes, - .detect_ctx = drm_connector_helper_detect_from_ddc, + .detect_ctx = hibmc_vdac_detect, }; +static void hibmc_vdac_force(struct drm_connector *connector) +{ + struct hibmc_drm_private *priv = to_hibmc_drm_private(connector->dev); + struct hibmc_dp *dp = &priv->dp; + + if (dp->hpd_status) { + connector->status = connector_status_disconnected; + return; + } + + connector->status = connector_status_connected; +} + static const struct drm_connector_funcs hibmc_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, .destroy = hibmc_connector_destroy, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .force = hibmc_vdac_force, }; static void hibmc_encoder_mode_set(struct drm_encoder *encoder, From 59cc500d8cf7166ab5060f9d7d8b81770659a577 Mon Sep 17 00:00:00 2001 From: Baihan Li Date: Tue, 27 May 2025 21:39:56 +0800 Subject: [PATCH 183/227] drm/hisilicon/hibmc: hibmc-drm bugfix for DP driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICAZCR CVE: NA ---------------------------------------------------------------------- Fix DP is not displaying in some rare scenarios. Fixes: f9698f802e50 ("drm/hisilicon/hibmc: Restructuring the header dp_reg.h") Signed-off-by: Baihan Li Signed-off-by: chenyi --- drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h | 5 +- drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c | 83 +++++++++++++------ drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h | 9 ++ drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c | 67 +++++++++++---- drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h | 10 ++- .../gpu/drm/hisilicon/hibmc/dp/dp_serdes.c | 12 --- .../drm/hisilicon/hibmc/hibmc_drm_debugfs.c | 49 ++++++++++- .../gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c | 51 ++++++++++++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c | 23 +++-- .../gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h | 1 + .../gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c | 5 ++ .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 13 ++- 12 files changed, 258 insertions(+), 70 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h index a789bdb5867ee..3522c0bf87795 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_comm.h @@ -15,6 +15,7 @@ #include "dp_hw.h" +#define HIBMC_DP_LINK_RATE_CAL 27 #define HIBMC_DP_LANE_NUM_MAX 2 struct hibmc_link_status { @@ -25,6 +26,9 @@ struct hibmc_link_status { struct hibmc_link_cap { u8 link_rate; u8 lanes; + int rx_dpcd_revision; + bool is_tps3; + bool is_tps4; }; struct hibmc_dp_link { @@ -62,7 +66,6 @@ struct hibmc_dp_dev { void hibmc_dp_aux_init(struct hibmc_dp *dp); int hibmc_dp_link_training(struct hibmc_dp_dev *dp); -int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp); int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp); int hibmc_dp_serdes_set_tx_cfg(struct hibmc_dp_dev *dp, u8 train_set[HIBMC_DP_LANE_NUM_MAX]); diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c index 8f0daec7d1749..9e8f95d957f20 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.c @@ -154,7 +154,6 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp) { struct drm_device *drm_dev = dp->drm_dev; struct hibmc_dp_dev *dp_dev; - int ret; dp_dev = devm_kzalloc(drm_dev->dev, sizeof(struct hibmc_dp_dev), GFP_KERNEL); if (!dp_dev) @@ -166,23 +165,22 @@ int hibmc_dp_hw_init(struct hibmc_dp *dp) dp_dev->dev = drm_dev; dp_dev->base = dp->mmio + HIBMC_DP_OFFSET; + dp_dev->serdes_base = dp_dev->base + HIBMC_DP_HOST_OFFSET; hibmc_dp_aux_init(dp); - ret = hibmc_dp_serdes_init(dp_dev); - if (ret) - return ret; - dp_dev->link.cap.lanes = 0x2; dp_dev->link.cap.link_rate = DP_LINK_BW_8_1; - /* hdcp data */ - writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG); /* int init */ writel(0, dp_dev->base + HIBMC_DP_INTR_ENABLE); writel(HIBMC_DP_INT_RST, dp_dev->base + HIBMC_DP_INTR_ORIGINAL_STATUS); /* rst */ + writel(0, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL); + usleep_range(30, 50); writel(HIBMC_DP_DPTX_RST, dp_dev->base + HIBMC_DP_DPTX_RST_CTRL); + /* hdcp data */ + writel(HIBMC_DP_HDCP, dp_dev->base + HIBMC_DP_HDCP_CFG); /* clock enable */ writel(HIBMC_DP_CLK_EN, dp_dev->base + HIBMC_DP_DPTX_CLK_CTRL); @@ -224,14 +222,17 @@ void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable) struct hibmc_dp_dev *dp_dev = dp->dp_dev; if (enable) { - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, BIT(0), 0x1); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, + HIBMC_DP_CFG_MST_ENABLE, 0x1); writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, BIT(10), 0x1); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, + HIBMC_DP_CFG_TIMING_GEN_ENABLE, 0x1); writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); } else { - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, BIT(10), 0); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_DPTX_GCTL0, + HIBMC_DP_CFG_TIMING_GEN_ENABLE, 0); writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, BIT(0), 0); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_MST_ENABLE, 0); writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); } @@ -257,10 +258,36 @@ int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode) return 0; } +u8 hibmc_dp_get_link_rate(struct hibmc_dp *dp) +{ + if (!dp->dp_dev) + return 0; + + return dp->dp_dev->link.cap.link_rate; +} + +u8 hibmc_dp_get_lanes(struct hibmc_dp *dp) +{ + if (!dp->dp_dev) + return 0; + + return dp->dp_dev->link.cap.lanes; +} + +int hibmc_dp_get_dpcd(struct hibmc_dp *dp) +{ + if (!dp->dp_dev) + return 0; + + return dp->dp_dev->link.cap.rx_dpcd_revision; +} + void hibmc_dp_reset_link(struct hibmc_dp *dp) { - dp->dp_dev->link.status.clock_recovered = false; - dp->dp_dev->link.status.channel_equalized = false; + if (dp->dp_dev) { + dp->dp_dev->link.status.clock_recovered = false; + dp->dp_dev->link.status.channel_equalized = false; + } } static const struct hibmc_dp_color_raw g_rgb_raw[] = { @@ -282,26 +309,30 @@ void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg) struct hibmc_dp_color_raw raw_data; if (cfg->enable) { - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(9), - cfg->self_timing); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, GENMASK(8, 1), - cfg->dynamic_rate); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, + HIBMC_DP_COLOR_BAR_TIMING_SEL_M, cfg->self_timing); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, + HIBMC_DP_COLOR_BAR_CTRL_M, cfg->dynamic_rate); if (cfg->pattern == CBAR_COLOR_BAR) { - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(10), 0); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, + HIBMC_DP_COLOR_BAR_PATTERN_SEL_M, 0); } else { raw_data = g_rgb_raw[cfg->pattern]; drm_dbg_dp(dp->drm_dev, "r:%x g:%x b:%x\n", raw_data.r_value, raw_data.g_value, raw_data.b_value); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(10), 1); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, GENMASK(23, 12), - raw_data.r_value); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL1, GENMASK(23, 12), - raw_data.g_value); - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL1, GENMASK(11, 0), - raw_data.b_value); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, + HIBMC_DP_COLOR_BAR_PATTERN_SEL_M, 1); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, + HIBMC_DP_COLOR_BAR_DATA_R_M, raw_data.r_value); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL1, + HIBMC_DP_COLOR_BAR_DATA_G_M, raw_data.g_value); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL1, + HIBMC_DP_COLOR_BAR_DATA_B_M, raw_data.b_value); } } - hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, BIT(0), cfg->enable); + hibmc_dp_reg_write_field(dp_dev, HIBMC_DP_COLOR_BAR_CTRL, + HIBMC_DP_COLOR_BAR_ENABLE_M, cfg->enable); writel(HIBMC_DP_SYNC_EN_MASK, dp_dev->base + HIBMC_DP_TIMING_SYNC_CTRL); } + diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h index f55d78d266f21..4740db06e1b00 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_hw.h @@ -12,6 +12,10 @@ #include #include +// 27 * 10000000 * 80% = 216000000 +#define DP_MODE_VALI_CAL 216000000 +#define BPP_24 24 + struct hibmc_dp_dev; enum hibmc_dp_cbar_pattern { @@ -51,11 +55,16 @@ struct hibmc_dp { struct hibmc_dp_cbar_cfg cfg; u32 irq_status; int hpd_status; + bool is_connected; }; int hibmc_dp_hw_init(struct hibmc_dp *dp); int hibmc_dp_mode_set(struct hibmc_dp *dp, struct drm_display_mode *mode); void hibmc_dp_display_en(struct hibmc_dp *dp, bool enable); +struct edid *hibmc_dp_get_edid(struct hibmc_dp *dp); +int hibmc_dp_get_dpcd(struct hibmc_dp *dp); +u8 hibmc_dp_get_link_rate(struct hibmc_dp *dp); +u8 hibmc_dp_get_lanes(struct hibmc_dp *dp); void hibmc_dp_set_cbar(struct hibmc_dp *dp, const struct hibmc_dp_cbar_cfg *cfg); void hibmc_dp_reset_link(struct hibmc_dp *dp); void hibmc_dp_hpd_cfg(struct hibmc_dp *dp); diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c index 21cad69c37575..14ef709f7214e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_link.c @@ -39,6 +39,14 @@ static int hibmc_dp_link_training_configure(struct hibmc_dp_dev *dp) /* enhanced frame */ hibmc_dp_reg_write_field(dp, HIBMC_DP_VIDEO_CTRL, HIBMC_DP_CFG_STREAM_FRAME_MODE, 0x1); + ret = hibmc_dp_get_serdes_rate_cfg(dp); + if (ret < 0) + return ret; + + ret = hibmc_dp_serdes_rate_switch(ret, dp); + if (ret) + return ret; + /* set rate and lane count */ buf[0] = dp->link.cap.link_rate; buf[1] = DP_LANE_COUNT_ENHANCED_FRAME_EN | dp->link.cap.lanes; @@ -201,21 +209,23 @@ static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp) bool level_changed; u32 voltage_tries; u32 cr_tries; + u32 max_cr; int ret; /* * DP 1.4 spec define 10 for maxtries value, for pre DP 1.4 version set a limit of 80 * (4 voltage levels x 4 preemphasis levels x 5 identical voltage retries) */ + max_cr = dp->link.cap.rx_dpcd_revision >= DP_DPCD_REV_14 ? 10 : 80; voltage_tries = 1; - for (cr_tries = 0; cr_tries < 80; cr_tries++) { + for (cr_tries = 0; cr_tries < max_cr; cr_tries++) { drm_dp_link_train_clock_recovery_delay(dp->aux, dp->dpcd); ret = drm_dp_dpcd_read_link_status(dp->aux, lane_status); if (ret != DP_LINK_STATUS_SIZE) { - drm_err(dp->dev, "Get lane status failed\n"); - return ret; + drm_err(dp->dev, "Get lane status failed, ret: %d\n", ret); + return ret >= 0 ? -EIO : ret; } if (drm_dp_clock_recovery_ok(lane_status, dp->link.cap.lanes)) { @@ -246,7 +256,7 @@ static int hibmc_dp_link_training_cr(struct hibmc_dp_dev *dp) voltage_tries = level_changed ? 1 : voltage_tries + 1; } - drm_err(dp->dev, "dp link training clock recovery 80 times failed\n"); + drm_err(dp->dev, "dp link training clock recovery %u timers failed\n", max_cr); dp->link.status.clock_recovered = false; return 0; @@ -256,9 +266,17 @@ static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp) { u8 lane_status[DP_LINK_STATUS_SIZE] = {0}; u8 eq_tries; + int tps; int ret; - ret = hibmc_dp_link_set_pattern(dp, DP_TRAINING_PATTERN_2); + if (dp->link.cap.is_tps4) + tps = DP_TRAINING_PATTERN_4; + else if (dp->link.cap.is_tps3) + tps = DP_TRAINING_PATTERN_3; + else + tps = DP_TRAINING_PATTERN_2; + + ret = hibmc_dp_link_set_pattern(dp, tps); if (ret) return ret; @@ -288,7 +306,7 @@ static int hibmc_dp_link_training_channel_eq(struct hibmc_dp_dev *dp) ret = hibmc_dp_serdes_set_tx_cfg(dp, dp->link.train_set); if (ret) - return ret; + break; ret = drm_dp_dpcd_write(dp->aux, DP_TRAINING_LANE0_SET, dp->link.train_set, dp->link.cap.lanes); @@ -317,7 +335,17 @@ static int hibmc_dp_link_downgrade_training_cr(struct hibmc_dp_dev *dp) static int hibmc_dp_link_downgrade_training_eq(struct hibmc_dp_dev *dp) { - if ((dp->link.status.clock_recovered && !dp->link.status.channel_equalized)) { + u8 status[DP_LINK_STATUS_SIZE] = {0}; + int ret; + + ret = drm_dp_dpcd_read_link_status(dp->aux, status); + if (ret != DP_LINK_STATUS_SIZE) { + drm_err(dp->dev, "get lane status failed\n"); + return ret >= 0 ? -EIO : ret; + } + + if ((dp->link.status.clock_recovered && !dp->link.status.channel_equalized) || + (status[0] != 0 && !dp->link.status.clock_recovered)) { // at least one cr_done if (!hibmc_dp_link_reduce_lane(dp)) return 0; } @@ -325,6 +353,20 @@ static int hibmc_dp_link_downgrade_training_eq(struct hibmc_dp_dev *dp) return hibmc_dp_link_reduce_rate(dp); } +static void hibmc_dp_update_caps(struct hibmc_dp_dev *dp) +{ + dp->link.cap.rx_dpcd_revision = dp->dpcd[DP_DPCD_REV]; + + dp->link.cap.is_tps3 = (dp->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_13) && + (dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED); + dp->link.cap.is_tps4 = (dp->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_14) && + (dp->dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED); + dp->link.cap.link_rate = dp->dpcd[DP_MAX_LINK_RATE]; + dp->link.cap.lanes = dp->dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; + if (dp->link.cap.lanes > HIBMC_DP_LANE_NUM_MAX) + dp->link.cap.lanes = HIBMC_DP_LANE_NUM_MAX; +} + int hibmc_dp_link_training(struct hibmc_dp_dev *dp) { struct hibmc_dp_link *link = &dp->link; @@ -334,16 +376,7 @@ int hibmc_dp_link_training(struct hibmc_dp_dev *dp) if (ret) drm_err(dp->dev, "dp aux read dpcd failed, ret: %d\n", ret); - dp->link.cap.link_rate = dp->dpcd[DP_MAX_LINK_RATE]; - dp->link.cap.lanes = 0x2; - - ret = hibmc_dp_get_serdes_rate_cfg(dp); - if (ret < 0) - return ret; - - ret = hibmc_dp_serdes_rate_switch(ret, dp); - if (ret) - return ret; + hibmc_dp_update_caps(dp); while (true) { ret = hibmc_dp_link_training_cr_pre(dp); diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h index 394b1e933c3ae..156e5d63c47f0 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_reg.h @@ -68,8 +68,14 @@ #define HIBMC_DP_CFG_STREAM_HBLANK_SIZE GENMASK(15, 0) #define HIBMC_DP_COLOR_BAR_CTRL 0x260 +#define HIBMC_DP_COLOR_BAR_ENABLE_M BIT(0) +#define HIBMC_DP_COLOR_BAR_CTRL_M GENMASK(8, 1) +#define HIBMC_DP_COLOR_BAR_TIMING_SEL_M BIT(9) +#define HIBMC_DP_COLOR_BAR_PATTERN_SEL_M BIT(10) +#define HIBMC_DP_COLOR_BAR_DATA_R_M GENMASK(23, 12) #define HIBMC_DP_COLOR_BAR_CTRL1 0x264 - +#define HIBMC_DP_COLOR_BAR_DATA_B_M GENMASK(11, 0) +#define HIBMC_DP_COLOR_BAR_DATA_G_M GENMASK(23, 12) #define HIBMC_DP_TIMING_GEN_CONFIG0 0x26c #define HIBMC_DP_CFG_TIMING_GEN0_HACTIVE GENMASK(31, 16) #define HIBMC_DP_CFG_TIMING_GEN0_HBLANK GENMASK(15, 0) @@ -90,6 +96,8 @@ #define HIBMC_DP_DPTX_GCTL0 0x708 #define HIBMC_DP_CFG_PHY_LANE_NUM GENMASK(2, 1) +#define HIBMC_DP_CFG_TIMING_GEN_ENABLE BIT(10) +#define HIBMC_DP_CFG_MST_ENABLE BIT(0) #define HIBMC_DP_INTR_ENABLE 0x720 #define HIBMC_DP_INTR_ORIGINAL_STATUS 0x728 diff --git a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c index 676059d4c1e60..8191233aa965a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c +++ b/drivers/gpu/drm/hisilicon/hibmc/dp/dp_serdes.c @@ -57,15 +57,3 @@ int hibmc_dp_serdes_rate_switch(u8 rate, struct hibmc_dp_dev *dp) return 0; } - -int hibmc_dp_serdes_init(struct hibmc_dp_dev *dp) -{ - dp->serdes_base = dp->base + HIBMC_DP_HOST_OFFSET; - - writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, DP_SERDES_VOL0_PRE0), - dp->serdes_base + HIBMC_DP_PMA_LANE0_OFFSET); - writel(FIELD_PREP(HIBMC_DP_PMA_TXDEEMPH, DP_SERDES_VOL0_PRE0), - dp->serdes_base + HIBMC_DP_PMA_LANE1_OFFSET); - - return hibmc_dp_serdes_rate_switch(DP_SERDES_BW_8_1, dp); -} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c index f585387c3a49f..840ef38c8c220 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_debugfs.c @@ -15,6 +15,44 @@ #define MAX_BUF_SIZE 12 +static int hibmc_dp_show(struct seq_file *m, void *arg) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + int idx, rate; + + if (!drm_dev_enter(dev, &idx)) + return -ENODEV; + + switch (hibmc_dp_get_link_rate(&priv->dp)) { + case 0x1e: + rate = 810; // 8.1Gbps + break; + case 0x14: + rate = 540; // 5.4Gbps + break; + case 0xA: + rate = 270; // 2.7Gbps + break; + case 0x6: + rate = 162; // 1.62Gbps + break; + default: + rate = 0; + } + + seq_printf(m, "enable lanes: %u\n", hibmc_dp_get_lanes(&priv->dp)); + seq_printf(m, "link rate: %d\n", rate); + seq_printf(m, "vfresh: %d\n", drm_mode_vrefresh(&priv->crtc.mode)); + seq_printf(m, "dpcd version: 0x%x\n", hibmc_dp_get_dpcd(&priv->dp)); + seq_printf(m, "hpd status: %d\n", priv->dp.hpd_status); + + drm_dev_exit(idx); + + return 0; +} + static ssize_t hibmc_control_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { @@ -93,12 +131,19 @@ static const struct file_operations hibmc_dbg_fops = { .release = single_release, }; +static struct drm_info_list hibmc_debugfs_list[] = { + { "hibmc-dp", hibmc_dp_show }, +}; + void hibmc_debugfs_init(struct drm_connector *connector, struct dentry *root) { struct drm_device *dev = connector->dev; struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + struct drm_minor *minor = dev->primary; /* create the file in drm directory, so we don't need to remove manually */ - debugfs_create_file("colorbar-cfg", 0200, - root, priv, &hibmc_dbg_fops); + debugfs_create_file("colorbar-cfg", 0200, root, priv, &hibmc_dbg_fops); + + drm_debugfs_create_files(hibmc_debugfs_list, ARRAY_SIZE(hibmc_debugfs_list), + minor->debugfs_root, minor); } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c index 41a5d3a9625c6..4b07e53dcada8 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_dp.c @@ -11,6 +11,7 @@ #include #include "hibmc_drm_drv.h" +#include "hibmc_drm_regs.h" #include "dp/dp_hw.h" #define HIBMC_DP_MASKED_SINK_HPD_PLUG_INT BIT(2) @@ -18,6 +19,7 @@ static int hibmc_dp_connector_get_modes(struct drm_connector *connector) { + struct hibmc_dp *dp = to_hibmc_dp(connector); const struct drm_edid *drm_edid; int count; @@ -26,12 +28,39 @@ static int hibmc_dp_connector_get_modes(struct drm_connector *connector) drm_edid_connector_update(connector, drm_edid); count = drm_edid_connector_add_modes(connector); + if (count) + dp->is_connected = true; + else + dp->is_connected = false; drm_edid_free(drm_edid); return count; } +static int hibmc_dp_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode, + struct drm_modeset_acquire_ctx *ctx, + enum drm_mode_status *status) +{ + struct hibmc_dp *dp = to_hibmc_dp(connector); + u64 cur_val, max_val; + + if (!dp->is_connected) { + *status = MODE_OK; + return 0; + } + + cur_val = (u64)mode->htotal * mode->vtotal * drm_mode_vrefresh(mode) * BPP_24; + max_val = (u64)hibmc_dp_get_link_rate(dp) * DP_MODE_VALI_CAL * hibmc_dp_get_lanes(dp); + if (cur_val > max_val) + *status = MODE_CLOCK_HIGH; + else + *status = MODE_OK; + + return 0; +} + static int hibmc_dp_detect(struct drm_connector *connector, struct drm_modeset_acquire_ctx *ctx, bool force) { @@ -45,6 +74,7 @@ static int hibmc_dp_detect(struct drm_connector *connector, static const struct drm_connector_helper_funcs hibmc_dp_conn_helper_funcs = { .get_modes = hibmc_dp_connector_get_modes, + .mode_valid_ctx = hibmc_dp_mode_valid, .detect_ctx = hibmc_dp_detect, }; @@ -110,9 +140,26 @@ static void hibmc_dp_encoder_disable(struct drm_encoder *drm_encoder, hibmc_dp_display_en(dp, false); } +static void hibmc_dp_encoder_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + u32 reg; + struct drm_device *dev = encoder->dev; + struct hibmc_drm_private *priv = to_hibmc_drm_private(dev); + + reg = readl(priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); + reg |= HIBMC_DISPLAY_CONTROL_FPVDDEN(1); + reg |= HIBMC_DISPLAY_CONTROL_PANELDATE(1); + reg |= HIBMC_DISPLAY_CONTROL_FPEN(1); + reg |= HIBMC_DISPLAY_CONTROL_VBIASEN(1); + writel(reg, priv->mmio + HIBMC_DISPLAY_CONTROL_HISILE); +} + static const struct drm_encoder_helper_funcs hibmc_dp_encoder_helper_funcs = { .atomic_enable = hibmc_dp_encoder_enable, .atomic_disable = hibmc_dp_encoder_disable, + .atomic_mode_set = hibmc_dp_encoder_mode_set, }; irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg) @@ -132,6 +179,8 @@ irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg) dp->hpd_status = 0; if (dev->registered) drm_connector_helper_hpd_irq_event(&dp->connector); + } else { + drm_dbg_dp(dev, "HPD OUT occur but err!\n"); } } else { if (dp->irq_status & HIBMC_DP_MASKED_SINK_HPD_PLUG_INT) { @@ -140,6 +189,8 @@ irqreturn_t hibmc_dp_hpd_isr(int irq, void *arg) dp->hpd_status = 1; if (dev->registered) drm_connector_helper_hpd_irq_event(&dp->connector); + } else { + drm_dbg_dp(dev, "HPD IN occur but err!\n"); } } diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 0036516ce55ab..8a0c790060fa4 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -132,7 +132,7 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) ret = hibmc_de_init(priv); if (ret) { drm_err(dev, "failed to init de: %d\n", ret); - return ret; + goto err; } /* @@ -142,17 +142,24 @@ static int hibmc_kms_init(struct hibmc_drm_private *priv) ret = readl(priv->mmio + HIBMC_DP_HOST_SERDES_CTRL); if (ret) { ret = hibmc_dp_init(priv); - if (ret) + if (ret) { drm_err(dev, "failed to init dp: %d\n", ret); + goto err; + } } ret = hibmc_vdac_init(priv); if (ret) { drm_err(dev, "failed to init vdac: %d\n", ret); - return ret; + goto err; } return 0; + +err: + drm_atomic_helper_shutdown(dev); + + return ret; } /* @@ -318,18 +325,20 @@ static int hibmc_load(struct drm_device *dev) ret = hibmc_hw_init(priv); if (ret) - goto err; + return ret; ret = drmm_vram_helper_init(dev, pci_resource_start(pdev, 0), pci_resource_len(pdev, 0)); if (ret) { drm_err(dev, "Error initializing VRAM MM; %d\n", ret); - goto err; + return ret; } ret = hibmc_kms_init(priv); - if (ret) - goto err; + if (ret) { + drm_err(dev, "hibmc kms init failed, ret:%d\n", ret); + return ret; + } ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) { diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h index 1b6fb9b022522..346c09117f19a 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.h @@ -71,6 +71,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv); int hibmc_mm_init(struct hibmc_drm_private *hibmc); int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *connector); +void hibmc_ddc_del(struct hibmc_vdac *vdac); int hibmc_dp_init(struct hibmc_drm_private *priv); void hibmc_debugfs_init(struct drm_connector *connector, struct dentry *root); diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c index 99b3b77b5445f..44860011855eb 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_i2c.c @@ -95,3 +95,8 @@ int hibmc_ddc_create(struct drm_device *drm_dev, struct hibmc_vdac *vdac) return i2c_bit_add_bus(&vdac->adapter); } + +void hibmc_ddc_del(struct hibmc_vdac *vdac) +{ + i2c_del_adapter(&vdac->adapter); +} diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 9cf2c52fc4a0a..2888facf8fc0e 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -45,7 +45,7 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) return count; } -static void hibmc_connector_destroy(struct drm_connector *connector) +static void hibmc_vdac_connector_destroy(struct drm_connector *connector) { struct hibmc_vdac *vdac = to_hibmc_vdac(connector); @@ -86,7 +86,7 @@ static void hibmc_vdac_force(struct drm_connector *connector) static const struct drm_connector_funcs hibmc_connector_funcs = { .fill_modes = drm_helper_probe_single_connector_modes, - .destroy = hibmc_connector_destroy, + .destroy = hibmc_vdac_connector_destroy, .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, @@ -132,7 +132,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) ret = drmm_encoder_init(dev, encoder, NULL, DRM_MODE_ENCODER_DAC, NULL); if (ret) { drm_err(dev, "failed to init encoder: %d\n", ret); - return ret; + goto err; } drm_encoder_helper_add(encoder, &hibmc_encoder_helper_funcs); @@ -143,7 +143,7 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) &vdac->adapter); if (ret) { drm_err(dev, "failed to init connector: %d\n", ret); - return ret; + goto err; } drm_connector_helper_add(connector, &hibmc_connector_helper_funcs); @@ -153,4 +153,9 @@ int hibmc_vdac_init(struct hibmc_drm_private *priv) connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; return 0; + +err: + hibmc_ddc_del(vdac); + + return ret; } From 2f2b4c9c9b44abc78ca2ccc32d61a33d93ce53e4 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 28 Mar 2022 17:34:33 +0300 Subject: [PATCH 184/227] drm/edid: split drm_add_edid_modes() to two MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the size of the function that actually modifies the EDID. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Reviewed-by: Emil Velikov Link: https://patchwork.freedesktop.org/patch/msgid/437c3c79f68d1144444fb2dd18a678f3aa97272c.1648477901.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 42 ++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 720956893b56c..21b56a4637bbc 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5344,18 +5344,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, return num_modes; } -/** - * drm_add_edid_modes - add modes from EDID data, if available - * @connector: connector we're probing - * @edid: EDID data - * - * Add the specified modes to the connector's mode list. Also fills out the - * &drm_display_info structure and ELD in @connector with any information which - * can be derived from the edid. - * - * Return: The number of modes added or 0 if we couldn't find any. - */ -int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) +static int drm_edid_connector_update(struct drm_connector *connector, + const struct edid *edid) { int num_modes = 0; u32 quirks; @@ -5364,12 +5354,6 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) clear_eld(connector); return 0; } - if (!drm_edid_is_valid(edid)) { - clear_eld(connector); - drm_warn(connector->dev, "%s: EDID invalid.\n", - connector->name); - return 0; - } drm_edid_to_eld(connector, edid); @@ -5421,6 +5405,28 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) return num_modes; } + +/** + * drm_add_edid_modes - add modes from EDID data, if available + * @connector: connector we're probing + * @edid: EDID data + * + * Add the specified modes to the connector's mode list. Also fills out the + * &drm_display_info structure and ELD in @connector with any information which + * can be derived from the edid. + * + * Return: The number of modes added or 0 if we couldn't find any. + */ +int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) +{ + if (edid && !drm_edid_is_valid(edid)) { + drm_warn(connector->dev, "%s: EDID invalid.\n", + connector->name); + edid = NULL; + } + + return drm_edid_connector_update(connector, edid); +} EXPORT_SYMBOL(drm_add_edid_modes); /** From bb757b2e8877bb3369b810f3ed58522d58e76f6f Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 11 Apr 2022 17:00:35 +0300 Subject: [PATCH 185/227] drm/edid: add EDID block count and size helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some helpers to figure out the EDID extension block count, block count, size, pointers to blocks. Unfortunately, we'll need to cast away the const in a few places where we actually need to access the data. v3: fix (!edid_extension_block_count(edid) == 0) (kernel test robot) v2: fix s/j/i/ introduced in a rebase Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/dc7b0850293d837439fb3914c8a9d81e39018b4b.1649685475.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 58 ++++++++++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 21b56a4637bbc..3d48c999f5ffa 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1556,6 +1556,38 @@ static const struct drm_display_mode edid_4k_modes[] = { /*** DDC fetch and block validation ***/ +static int edid_extension_block_count(const struct edid *edid) +{ + return edid->extensions; +} + +static int edid_block_count(const struct edid *edid) +{ + return edid_extension_block_count(edid) + 1; +} + +static int edid_size_by_blocks(int num_blocks) +{ + return num_blocks * EDID_LENGTH; +} + +static int edid_size(const struct edid *edid) +{ + return edid_size_by_blocks(edid_block_count(edid)); +} + +static const void *edid_block_data(const struct edid *edid, int index) +{ + BUILD_BUG_ON(sizeof(*edid) != EDID_LENGTH); + + return edid + index; +} + +static const void *edid_extension_block_data(const struct edid *edid, int index) +{ + return edid_block_data(edid, index + 1); +} + static const u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; @@ -1631,8 +1663,8 @@ bool drm_edid_are_equal(const struct edid *edid1, const struct edid *edid2) return false; if (edid1) { - edid1_len = EDID_LENGTH * (1 + edid1->extensions); - edid2_len = EDID_LENGTH * (1 + edid2->extensions); + edid1_len = edid_size(edid1); + edid2_len = edid_size(edid2); if (edid1_len != edid2_len) return false; @@ -1753,14 +1785,16 @@ EXPORT_SYMBOL(drm_edid_block_valid); bool drm_edid_is_valid(struct edid *edid) { int i; - u8 *raw = (u8 *)edid; if (!edid) return false; - for (i = 0; i <= edid->extensions; i++) - if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true, NULL)) + for (i = 0; i < edid_block_count(edid); i++) { + void *block = (void *)edid_block_data(edid, i); + + if (!drm_edid_block_valid(block, i, true, NULL)) return false; + } return true; } @@ -2106,7 +2140,7 @@ EXPORT_SYMBOL(drm_get_edid_switcheroo); */ struct edid *drm_edid_duplicate(const struct edid *edid) { - return kmemdup(edid, (edid->extensions + 1) * EDID_LENGTH, GFP_KERNEL); + return kmemdup(edid, edid_size(edid), GFP_KERNEL); } EXPORT_SYMBOL(drm_edid_duplicate); @@ -2304,8 +2338,8 @@ drm_for_each_detailed_block(u8 *raw_edid, detailed_cb *cb, void *closure) for (i = 0; i < EDID_DETAILED_TIMINGS; i++) cb(&(edid->detailed_timings[i]), closure); - for (i = 1; i <= raw_edid[0x7e]; i++) { - u8 *ext = raw_edid + (i * EDID_LENGTH); + for (i = 0; i < edid_extension_block_count(edid); i++) { + const u8 *ext = edid_extension_block_data(edid, i); switch (*ext) { case CEA_EXT: @@ -3255,17 +3289,17 @@ const u8 *drm_find_edid_extension(const struct edid *edid, int i; /* No EDID or EDID extensions */ - if (edid == NULL || edid->extensions == 0) + if (!edid || !edid_extension_block_count(edid)) return NULL; /* Find CEA extension */ - for (i = *ext_index; i < edid->extensions; i++) { - edid_ext = (const u8 *)edid + EDID_LENGTH * (i + 1); + for (i = *ext_index; i < edid_extension_block_count(edid); i++) { + edid_ext = edid_extension_block_data(edid, i); if (edid_ext[0] == ext_id) break; } - if (i >= edid->extensions) + if (i >= edid_extension_block_count(edid)) return NULL; *ext_index = i + 1; From 219dea4f794bb6a281b34518f5f87547ac0a8921 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 11 Apr 2022 17:00:29 +0300 Subject: [PATCH 186/227] drm/edid: add typedef for block read function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the callback a bit easier on the eye. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/10b8721bb7ea8c7df1fd0c1d97c5d446905abbf4.1649685475.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3d48c999f5ffa..d042e7f3158ab 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1948,6 +1948,8 @@ int drm_add_override_edid_modes(struct drm_connector *connector) } EXPORT_SYMBOL(drm_add_override_edid_modes); +typedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len); + /** * drm_do_get_edid - get EDID data using a custom EDID block read function * @connector: connector we're probing @@ -1969,9 +1971,8 @@ EXPORT_SYMBOL(drm_add_override_edid_modes); * Return: Pointer to valid EDID or NULL if we couldn't find any. */ struct edid *drm_do_get_edid(struct drm_connector *connector, - int (*get_edid_block)(void *data, u8 *buf, unsigned int block, - size_t len), - void *data) + read_block_fn read_block, + void *context) { int i, j = 0, valid_extensions = 0; u8 *edid, *new; @@ -1986,7 +1987,7 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, /* base block fetch */ for (i = 0; i < 4; i++) { - if (get_edid_block(data, edid, 0, EDID_LENGTH)) + if (read_block(context, edid, 0, EDID_LENGTH)) goto out; if (drm_edid_block_valid(edid, 0, false, &connector->edid_corrupt)) @@ -2013,7 +2014,7 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, u8 *block = edid + j * EDID_LENGTH; for (i = 0; i < 4; i++) { - if (get_edid_block(data, block, j, EDID_LENGTH)) + if (read_block(context, block, j, EDID_LENGTH)) goto out; if (drm_edid_block_valid(block, j, false, NULL)) break; From ce381217aab34c6253b37952f60cce014443092e Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 14 Sep 2021 12:17:23 +0200 Subject: [PATCH 187/227] drm/probe-helper: Create a HPD IRQ event helper for a single connector The drm_helper_hpd_irq_event() function is iterating over all the connectors when an hotplug event is detected. During that iteration, it will call each connector detect function and figure out if its status changed. Finally, if any connector changed, it will notify the user-space and the clients that something changed on the DRM device. This is supposed to be used for drivers that don't have a hotplug interrupt for individual connectors. However, drivers that can use an interrupt for a single connector are left in the dust and can either reimplement the logic used during the iteration for each connector or use that helper and iterate over all connectors all the time. Since both are suboptimal, let's create a helper that will only perform the status detection on a single connector. Signed-off-by: Maxime Ripard Reviewed-by: Sam Ravnborg Link: https://patchwork.freedesktop.org/patch/msgid/20210914101724.266570-2-maxime@cerno.tech Signed-off-by: chenyi --- drivers/gpu/drm/drm_probe_helper.c | 116 +++++++++++++++++++++-------- include/drm/drm_probe_helper.h | 1 + 2 files changed, 86 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index f6b72e03688d4..0f3a4526a510c 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -796,6 +796,86 @@ void drm_kms_helper_poll_fini(struct drm_device *dev) } EXPORT_SYMBOL(drm_kms_helper_poll_fini); +static bool check_connector_changed(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + enum drm_connector_status old_status; + u64 old_epoch_counter; + + /* Only handle HPD capable connectors. */ + drm_WARN_ON(dev, !(connector->polled & DRM_CONNECTOR_POLL_HPD)); + + drm_WARN_ON(dev, !mutex_is_locked(&dev->mode_config.mutex)); + + old_status = connector->status; + old_epoch_counter = connector->epoch_counter; + connector->status = drm_helper_probe_detect(connector, NULL, false); + + if (old_epoch_counter == connector->epoch_counter) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Same epoch counter %llu\n", + connector->base.id, + connector->name, + connector->epoch_counter); + + return false; + } + + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] status updated from %s to %s\n", + connector->base.id, + connector->name, + drm_get_connector_status_name(old_status), + drm_get_connector_status_name(connector->status)); + + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Changed epoch counter %llu => %llu\n", + connector->base.id, + connector->name, + old_epoch_counter, + connector->epoch_counter); + + return true; +} + +/** + * drm_connector_helper_hpd_irq_event - hotplug processing + * @connector: drm_connector + * + * Drivers can use this helper function to run a detect cycle on a connector + * which has the DRM_CONNECTOR_POLL_HPD flag set in its &polled member. + * + * This helper function is useful for drivers which can track hotplug + * interrupts for a single connector. Drivers that want to send a + * hotplug event for all connectors or can't track hotplug interrupts + * per connector need to use drm_helper_hpd_irq_event(). + * + * This function must be called from process context with no mode + * setting locks held. + * + * Note that a connector can be both polled and probed from the hotplug + * handler, in case the hotplug interrupt is known to be unreliable. + * + * Returns: + * A boolean indicating whether the connector status changed or not + */ +bool drm_connector_helper_hpd_irq_event(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + bool changed; + + mutex_lock(&dev->mode_config.mutex); + changed = check_connector_changed(connector); + mutex_unlock(&dev->mode_config.mutex); + + if (changed) { + drm_kms_helper_hotplug_event(dev); + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Sent hotplug event\n", + connector->base.id, + connector->name); + } + + return changed; +} +EXPORT_SYMBOL(drm_connector_helper_hpd_irq_event); + /** * drm_helper_hpd_irq_event - hotplug processing * @dev: drm_device @@ -809,9 +889,10 @@ EXPORT_SYMBOL(drm_kms_helper_poll_fini); * interrupts for each connector. * * Drivers which support hotplug interrupts for each connector individually and - * which have a more fine-grained detect logic should bypass this code and - * directly call drm_kms_helper_hotplug_event() in case the connector state - * changed. + * which have a more fine-grained detect logic can use + * drm_connector_helper_hpd_irq_event(). Alternatively, they should bypass this + * code and directly call drm_kms_helper_hotplug_event() in case the connector + * state changed. * * This function must be called from process context with no mode * setting locks held. @@ -823,9 +904,7 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) { struct drm_connector *connector; struct drm_connector_list_iter conn_iter; - enum drm_connector_status old_status; bool changed = false; - u64 old_epoch_counter; if (!dev->mode_config.poll_enabled) return false; @@ -837,33 +916,8 @@ bool drm_helper_hpd_irq_event(struct drm_device *dev) if (!(connector->polled & DRM_CONNECTOR_POLL_HPD)) continue; - old_status = connector->status; - - old_epoch_counter = connector->epoch_counter; - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Old epoch counter %llu\n", connector->base.id, - connector->name, - old_epoch_counter); - - connector->status = drm_helper_probe_detect(connector, NULL, false); - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] status updated from %s to %s\n", - connector->base.id, - connector->name, - drm_get_connector_status_name(old_status), - drm_get_connector_status_name(connector->status)); - - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] New epoch counter %llu\n", - connector->base.id, - connector->name, - connector->epoch_counter); - - /* - * Check if epoch counter had changed, meaning that we need - * to send a uevent. - */ - if (old_epoch_counter != connector->epoch_counter) + if (check_connector_changed(connector)) changed = true; - } drm_connector_list_iter_end(&conn_iter); mutex_unlock(&dev->mode_config.mutex); diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h index 8d3ed2834d345..04c57564c397d 100644 --- a/include/drm/drm_probe_helper.h +++ b/include/drm/drm_probe_helper.h @@ -18,6 +18,7 @@ int drm_helper_probe_detect(struct drm_connector *connector, void drm_kms_helper_poll_init(struct drm_device *dev); void drm_kms_helper_poll_fini(struct drm_device *dev); bool drm_helper_hpd_irq_event(struct drm_device *dev); +bool drm_connector_helper_hpd_irq_event(struct drm_connector *connector); void drm_kms_helper_hotplug_event(struct drm_device *dev); void drm_kms_helper_poll_disable(struct drm_device *dev); From af8a48bc670fe76942e1da3e1b60020c1b94932a Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 9 May 2022 15:03:02 +0300 Subject: [PATCH 188/227] drm/edid: add struct drm_edid container Introduce new opaque type struct drm_edid to encapsulate the EDID data and the size allocated for it. The contents will be private to drm_edid.c. There are a number of reasons for adding a container around struct edid: * struct edid is a raw blob pointer to data that usually originates outside of the kernel. Its size is contained within the structure. * There's no way to attach meta information (such as allocated memory size) to struct edid. * Validation of the EDID blob and its size become crucial, and it's spread all over the subsystem, with varying levels of accuracy. * HDMI Forum has introduced an HF-EEODB extension that defines an override EDID size within an EDID extension. The size allocated for an EDID depends on whether the allocator understands the HF-EEODB extension. Given a struct edid *, it's impossible to know how much memory was actually allocated for it. There are also some reasons for making the container type struct drm_edid opaque and private to drm_edid.c: * Have only one place for creating and parsing the EDID, to avoid duplicating bugs. * Prepare for reading a pure DisplayID 2.0 from its own DDC address, and adding it within the same struct drm_edid container, transparently, and for all drivers. * With the idea that the drm_edid objects are immutable during their lifetimes, it will be possible to refcount them and reduce EDID copying everywhere (this is left for future work). Initially, just add the type. In follow-up, we'll start converting the guts of drm_edid.c to use it, and finally add interfaces around it. Signed-off-by: Jani Nikula Reviewed-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/f3ecabd8a219aea678ad00f7bcdecf77b27b3c78.1652097712.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index d042e7f3158ab..5dd74fd30cecc 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1556,6 +1556,15 @@ static const struct drm_display_mode edid_4k_modes[] = { /*** DDC fetch and block validation ***/ +/* + * The opaque EDID type, internal to drm_edid.c. + */ +struct drm_edid { + /* Size allocated for edid */ + size_t size; + const struct edid *edid; +}; + static int edid_extension_block_count(const struct edid *edid) { return edid->extensions; From 1d2385b1bc8056a8b006f696a79edbf6f9f5825f Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 4 Feb 2022 16:13:41 -0800 Subject: [PATCH 189/227] drm: Plumb debugfs_init through to panels We'd like panels to be able to add things to debugfs underneath the connector's directory. Let's plumb it through. A panel will be able to put things in a "panel" directory under the connector's directory. Note that debugfs is not ABI and so it's always possible that the location that the panel gets for its debugfs could change in the future. NOTE: this currently only works if you're using a modern architecture. Specifically the plumbing relies on _both_ drm_bridge_connector and drm_panel_bridge. If you're not using one or both of these things then things won't be plumbed through. As a side effect of this change, drm_bridges can also get callbacks to put stuff underneath the connector's debugfs directory. At the moment all bridges in the chain have their debugfs_init() called with the connector's root directory. Signed-off-by: Douglas Anderson Reviewed-by: Javier Martinez Canillas Reviewed-by: Laurent Pinchart Link: https://patchwork.freedesktop.org/patch/msgid/20220204161245.v2.2.Ib0bd5346135cbb0b63006b69b61d4c8af6484740@changeid Signed-off-by: chenyi --- drivers/gpu/drm/bridge/panel.c | 12 ++++++++++++ drivers/gpu/drm/drm_bridge_connector.c | 15 +++++++++++++++ drivers/gpu/drm/drm_debugfs.c | 3 +++ include/drm/drm_bridge.h | 7 +++++++ include/drm/drm_connector.h | 7 +++++++ include/drm/drm_panel.h | 8 ++++++++ 6 files changed, 52 insertions(+) diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index b32295abd9e75..5be0575751831 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -138,6 +138,17 @@ static int panel_bridge_get_modes(struct drm_bridge *bridge, return drm_panel_get_modes(panel_bridge->panel, connector); } +static void panel_bridge_debugfs_init(struct drm_bridge *bridge, + struct dentry *root) +{ + struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); + struct drm_panel *panel = panel_bridge->panel; + + root = debugfs_create_dir("panel", root); + if (panel->funcs->debugfs_init) + panel->funcs->debugfs_init(panel, root); +} + static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .attach = panel_bridge_attach, .detach = panel_bridge_detach, @@ -150,6 +161,7 @@ static const struct drm_bridge_funcs panel_bridge_bridge_funcs = { .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt, + .debugfs_init = panel_bridge_debugfs_init, }; /** diff --git a/drivers/gpu/drm/drm_bridge_connector.c b/drivers/gpu/drm/drm_bridge_connector.c index 4f20137ef21d5..6b3dad03d77d0 100644 --- a/drivers/gpu/drm/drm_bridge_connector.c +++ b/drivers/gpu/drm/drm_bridge_connector.c @@ -216,6 +216,20 @@ static void drm_bridge_connector_destroy(struct drm_connector *connector) kfree(bridge_connector); } +static void drm_bridge_connector_debugfs_init(struct drm_connector *connector, + struct dentry *root) +{ + struct drm_bridge_connector *bridge_connector = + to_drm_bridge_connector(connector); + struct drm_encoder *encoder = bridge_connector->encoder; + struct drm_bridge *bridge; + + list_for_each_entry(bridge, &encoder->bridge_chain, chain_node) { + if (bridge->funcs->debugfs_init) + bridge->funcs->debugfs_init(bridge, root); + } +} + static const struct drm_connector_funcs drm_bridge_connector_funcs = { .reset = drm_atomic_helper_connector_reset, .detect = drm_bridge_connector_detect, @@ -223,6 +237,7 @@ static const struct drm_connector_funcs drm_bridge_connector_funcs = { .destroy = drm_bridge_connector_destroy, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, + .debugfs_init = drm_bridge_connector_debugfs_init, }; /* ----------------------------------------------------------------------------- diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index b0a8264894885..7f1b82dbaebb9 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -436,6 +436,9 @@ void drm_debugfs_connector_add(struct drm_connector *connector) /* vrr range */ debugfs_create_file("vrr_range", S_IRUGO, root, connector, &vrr_range_fops); + + if (connector->funcs->debugfs_init) + connector->funcs->debugfs_init(connector, root); } void drm_debugfs_connector_remove(struct drm_connector *connector) diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h index 3188b7a1d2c7c..285039789b1d3 100644 --- a/include/drm/drm_bridge.h +++ b/include/drm/drm_bridge.h @@ -649,6 +649,13 @@ struct drm_bridge_funcs { * the DRM_BRIDGE_OP_HPD flag in their &drm_bridge->ops. */ void (*hpd_disable)(struct drm_bridge *bridge); + + /** + * @debugfs_init: + * + * Allows bridges to create bridge-specific debugfs files. + */ + void (*debugfs_init)(struct drm_bridge *bridge, struct dentry *root); }; /** diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index 1f43d7c6724aa..f7ee6a538ddb9 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1090,6 +1090,13 @@ struct drm_connector_funcs { */ void (*atomic_print_state)(struct drm_printer *p, const struct drm_connector_state *state); + + /** + * @debugfs_init: + * + * Allows connectors to create connector-specific debugfs files. + */ + void (*debugfs_init)(struct drm_connector *connector, struct dentry *root); }; /** diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h index 4602f833eb51d..1ba2d424a53ff 100644 --- a/include/drm/drm_panel.h +++ b/include/drm/drm_panel.h @@ -29,6 +29,7 @@ #include struct backlight_device; +struct dentry; struct device_node; struct drm_connector; struct drm_device; @@ -125,6 +126,13 @@ struct drm_panel_funcs { */ int (*get_timings)(struct drm_panel *panel, unsigned int num_timings, struct display_timing *timings); + + /** + * @debugfs_init: + * + * Allows panels to create panels-specific debugfs files. + */ + void (*debugfs_init)(struct drm_panel *panel, struct dentry *root); }; /** From 81a036da3f6adfe3bf44ef78f05f02f271e850cd Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 8 Jun 2022 10:50:35 +0300 Subject: [PATCH 190/227] drm/edid: add new interfaces around struct drm_edid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new functions drm_edid_read(), drm_edid_read_ddc(), and drm_edid_read_custom() to replace drm_get_edid() and drm_do_get_edid() for reading the EDID. The transition is expected to happen over a fairly long time. Note that the new drm_edid_read*() functions do not do any of the connector updates anymore. The reading and parsing will be completely separated from each other. Add new functions drm_edid_alloc(), drm_edid_dup(), and drm_edid_free() for allocating and freeing drm_edid containers. Cc: David Airlie Cc: Daniel Vetter Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/5a6532a94cad6a79424f6d1918dbe7b7d607ac03.1654674560.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 242 +++++++++++++++++++++++++++++++++---- include/drm/drm_edid.h | 9 ++ 2 files changed, 227 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 5dd74fd30cecc..51bbb2f7dbddc 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1959,29 +1959,9 @@ EXPORT_SYMBOL(drm_add_override_edid_modes); typedef int read_block_fn(void *context, u8 *buf, unsigned int block, size_t len); -/** - * drm_do_get_edid - get EDID data using a custom EDID block read function - * @connector: connector we're probing - * @get_edid_block: EDID block read function - * @data: private data passed to the block read function - * - * When the I2C adapter connected to the DDC bus is hidden behind a device that - * exposes a different interface to read EDID blocks this function can be used - * to get EDID data using a custom block read function. - * - * As in the general case the DDC bus is accessible by the kernel at the I2C - * level, drivers must make all reasonable efforts to expose it as an I2C - * adapter and use drm_get_edid() instead of abusing this function. - * - * The EDID may be overridden using debugfs override_edid or firmware EDID - * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority - * order. Having either of them bypasses actual EDID reads. - * - * Return: Pointer to valid EDID or NULL if we couldn't find any. - */ -struct edid *drm_do_get_edid(struct drm_connector *connector, - read_block_fn read_block, - void *context) +static struct edid *_drm_do_get_edid(struct drm_connector *connector, + read_block_fn read_block, void *context, + size_t *size) { int i, j = 0, valid_extensions = 0; u8 *edid, *new; @@ -2069,8 +2049,116 @@ struct edid *drm_do_get_edid(struct drm_connector *connector, kfree(edid); return NULL; } + +/** + * drm_do_get_edid - get EDID data using a custom EDID block read function + * @connector: connector we're probing + * @read_block: EDID block read function + * @context: private data passed to the block read function + * + * When the I2C adapter connected to the DDC bus is hidden behind a device that + * exposes a different interface to read EDID blocks this function can be used + * to get EDID data using a custom block read function. + * + * As in the general case the DDC bus is accessible by the kernel at the I2C + * level, drivers must make all reasonable efforts to expose it as an I2C + * adapter and use drm_get_edid() instead of abusing this function. + * + * The EDID may be overridden using debugfs override_edid or firmware EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * + * Return: Pointer to valid EDID or NULL if we couldn't find any. + */ +struct edid *drm_do_get_edid(struct drm_connector *connector, + read_block_fn read_block, + void *context) +{ + return _drm_do_get_edid(connector, read_block, context, NULL); +} EXPORT_SYMBOL_GPL(drm_do_get_edid); +/* Allocate struct drm_edid container *without* duplicating the edid data */ +static const struct drm_edid *_drm_edid_alloc(const void *edid, size_t size) +{ + struct drm_edid *drm_edid; + + if (!edid || !size || size < EDID_LENGTH) + return NULL; + + drm_edid = kzalloc(sizeof(*drm_edid), GFP_KERNEL); + if (drm_edid) { + drm_edid->edid = edid; + drm_edid->size = size; + } + + return drm_edid; +} + +/** + * drm_edid_alloc - Allocate a new drm_edid container + * @edid: Pointer to raw EDID data + * @size: Size of memory allocated for EDID + * + * Allocate a new drm_edid container. Do not calculate edid size from edid, pass + * the actual size that has been allocated for the data. There is no validation + * of the raw EDID data against the size, but at least the EDID base block must + * fit in the buffer. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: drm_edid container, or NULL on errors + */ +const struct drm_edid *drm_edid_alloc(const void *edid, size_t size) +{ + const struct drm_edid *drm_edid; + + if (!edid || !size || size < EDID_LENGTH) + return NULL; + + edid = kmemdup(edid, size, GFP_KERNEL); + if (!edid) + return NULL; + + drm_edid = _drm_edid_alloc(edid, size); + if (!drm_edid) + kfree(edid); + + return drm_edid; +} +EXPORT_SYMBOL(drm_edid_alloc); + +/** + * drm_edid_dup - Duplicate a drm_edid container + * @drm_edid: EDID to duplicate + * + * The returned pointer must be freed using drm_edid_free(). + * + * Returns: drm_edid container copy, or NULL on errors + */ +const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid) +{ + if (!drm_edid) + return NULL; + + return drm_edid_alloc(drm_edid->edid, drm_edid->size); +} +EXPORT_SYMBOL(drm_edid_dup); + +/** + * drm_edid_free - Free the drm_edid container + * @drm_edid: EDID to free + */ +void drm_edid_free(const struct drm_edid *drm_edid) +{ + if (!drm_edid) + return; + + kfree(drm_edid->edid); + kfree(drm_edid); +} +EXPORT_SYMBOL(drm_edid_free); + /** * drm_probe_ddc() - probe DDC presence * @adapter: I2C adapter to probe @@ -2107,12 +2195,118 @@ struct edid *drm_get_edid(struct drm_connector *connector, if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) return NULL; - edid = drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter); + edid = _drm_do_get_edid(connector, drm_do_probe_ddc_edid, adapter, NULL); drm_connector_update_edid_property(connector, edid); return edid; } EXPORT_SYMBOL(drm_get_edid); +/** + * drm_edid_read_custom - Read EDID data using given EDID block read function + * @connector: Connector to use + * @read_block: EDID block read function + * @context: Private data passed to the block read function + * + * When the I2C adapter connected to the DDC bus is hidden behind a device that + * exposes a different interface to read EDID blocks this function can be used + * to get EDID data using a custom block read function. + * + * As in the general case the DDC bus is accessible by the kernel at the I2C + * level, drivers must make all reasonable efforts to expose it as an I2C + * adapter and use drm_edid_read() or drm_edid_read_ddc() instead of abusing + * this function. + * + * The EDID may be overridden using debugfs override_edid or firmware EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: Pointer to EDID, or NULL if probe/read failed. + */ +const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector, + read_block_fn read_block, + void *context) +{ + const struct drm_edid *drm_edid; + struct edid *edid; + size_t size = 0; + + edid = _drm_do_get_edid(connector, read_block, context, &size); + if (!edid) + return NULL; + + /* Sanity check for now */ + drm_WARN_ON(connector->dev, !size); + + drm_edid = _drm_edid_alloc(edid, size); + if (!drm_edid) + kfree(edid); + + return drm_edid; +} +EXPORT_SYMBOL(drm_edid_read_custom); + +/** + * drm_edid_read_ddc - Read EDID data using given I2C adapter + * @connector: Connector to use + * @adapter: I2C adapter to use for DDC + * + * Read EDID using the given I2C adapter. + * + * The EDID may be overridden using debugfs override_edid or firmware EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * + * Prefer initializing connector->ddc with drm_connector_init_with_ddc() and + * using drm_edid_read() instead of this function. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: Pointer to EDID, or NULL if probe/read failed. + */ +const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, + struct i2c_adapter *adapter) +{ + const struct drm_edid *drm_edid; + + if (connector->force == DRM_FORCE_OFF) + return NULL; + + if (connector->force == DRM_FORCE_UNSPECIFIED && !drm_probe_ddc(adapter)) + return NULL; + + drm_edid = drm_edid_read_custom(connector, drm_do_probe_ddc_edid, adapter); + + /* Note: Do *not* call connector updates here. */ + + return drm_edid; +} +EXPORT_SYMBOL(drm_edid_read_ddc); + +/** + * drm_edid_read - Read EDID data using connector's I2C adapter + * @connector: Connector to use + * + * Read EDID using the connector's I2C adapter. + * + * The EDID may be overridden using debugfs override_edid or firmware EDID + * (drm_load_edid_firmware() and drm.edid_firmware parameter), in this priority + * order. Having either of them bypasses actual EDID reads. + * + * The returned pointer must be freed using drm_edid_free(). + * + * Return: Pointer to EDID, or NULL if probe/read failed. + */ +const struct drm_edid *drm_edid_read(struct drm_connector *connector) +{ + if (drm_WARN_ON(connector->dev, !connector->ddc)) + return NULL; + + return drm_edid_read_ddc(connector, connector->ddc); +} +EXPORT_SYMBOL(drm_edid_read); + /** * drm_get_edid_switcheroo - get EDID data for a vga_switcheroo output * @connector: connector we're probing diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index c24559f5329dd..b5c4bb96f71e1 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -543,6 +543,15 @@ struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, struct drm_display_mode * drm_display_mode_from_cea_vic(struct drm_device *dev, u8 video_code); +const struct drm_edid *drm_edid_alloc(const void *edid, size_t size); +const struct drm_edid *drm_edid_dup(const struct drm_edid *drm_edid); +void drm_edid_free(const struct drm_edid *drm_edid); +const struct drm_edid *drm_edid_read(struct drm_connector *connector); +const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, + struct i2c_adapter *adapter); +const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector, + int (*read_block)(void *context, u8 *buf, unsigned int block, size_t len), + void *context); const u8 *drm_find_edid_extension(const struct edid *edid, int ext_id, int *ext_index); From d549611af2c0b991c55858c370720a33195ec718 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 9 May 2022 15:03:03 +0300 Subject: [PATCH 191/227] drm/edid: start propagating drm_edid to lower levels We'll need to propagate drm_edid everywhere. This is a clunky start, but a start nonetheless. We'll eventually convert all of the EDID parsing to struct drm_edid. Initially, we'll just create the struct drm_edid in stack. This will be the compat layer for legacy struct edid code. In the future, we'll have EDID read return drm_edid objects. v2: Add legacy init helper. Signed-off-by: Jani Nikula Reviewed-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/000452fddedbaf7f473ac25d4dde2502e60b8e39.1652097712.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 51bbb2f7dbddc..80c2cdeb9651d 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1597,6 +1597,24 @@ static const void *edid_extension_block_data(const struct edid *edid, int index) return edid_block_data(edid, index + 1); } +/* + * Initializer helper for legacy interfaces, where we have no choice but to + * trust edid size. Not for general purpose use. + */ +static const struct drm_edid *drm_edid_legacy_init(struct drm_edid *drm_edid, + const struct edid *edid) +{ + if (!edid) + return NULL; + + memset(drm_edid, 0, sizeof(*drm_edid)); + + drm_edid->edid = edid; + drm_edid->size = edid_size(edid); + + return drm_edid; +} + static const u8 edid_header[] = { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; @@ -5583,17 +5601,19 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, } static int drm_edid_connector_update(struct drm_connector *connector, - const struct edid *edid) + const struct drm_edid *drm_edid) { + const struct edid *edid; int num_modes = 0; u32 quirks; - if (edid == NULL) { + if (!drm_edid) { clear_eld(connector); return 0; } drm_edid_to_eld(connector, edid); + edid = drm_edid->edid; /* * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks. @@ -5657,13 +5677,16 @@ static int drm_edid_connector_update(struct drm_connector *connector, */ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) { + struct drm_edid drm_edid; + if (edid && !drm_edid_is_valid(edid)) { drm_warn(connector->dev, "%s: EDID invalid.\n", connector->name); edid = NULL; } - return drm_edid_connector_update(connector, edid); + return drm_edid_connector_update(connector, + drm_edid_legacy_init(&drm_edid, edid)); } EXPORT_SYMBOL(drm_add_edid_modes); From dd585f251060f0a6ce3799019b18933dbc0c7cda Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 11 May 2022 12:21:04 +0300 Subject: [PATCH 192/227] drm/edid: keep propagating drm_edid to display info We'll need to propagate drm_edid everywhere. v3: Update comment to refer to update_display_info() (Ankit) v2: Use drm_edid_legacy_init() Signed-off-by: Jani Nikula Reviewed-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20220511092104.4179637-1-jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 80c2cdeb9651d..b2548dda3a537 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -2389,14 +2389,15 @@ static bool edid_vendor(const struct edid *edid, const char *vendor) /** * edid_get_quirks - return quirk flags for a given EDID - * @edid: EDID to process + * @drm_edid: EDID to process * * This tells subsequent routines what fixes they need to apply. */ -static u32 edid_get_quirks(const struct edid *edid) +static u32 edid_get_quirks(const struct drm_edid *drm_edid) { const struct edid_quirk *quirk; int i; + const struct edid *edid = drm_edid->edid; for (i = 0; i < ARRAY_SIZE(edid_quirk_list); i++) { quirk = &edid_quirk_list[i]; @@ -5391,11 +5392,11 @@ void get_monitor_range(struct detailed_timing *timing, monitor_range->max_vfreq = range->max_vfreq; } -static -void drm_get_monitor_range(struct drm_connector *connector, - const struct edid *edid) +static void drm_get_monitor_range(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_display_info *info = &connector->display_info; + const struct edid *edid = drm_edid->edid; if (!version_greater(edid, 1, 1)) return; @@ -5433,11 +5434,13 @@ drm_reset_display_info(struct drm_connector *connector) memset(&info->monitor_range, 0, sizeof(info->monitor_range)); } -u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid) +static u32 update_display_info(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_display_info *info = &connector->display_info; + const struct edid *edid = drm_edid->edid; - u32 quirks = edid_get_quirks(edid); + u32 quirks = edid_get_quirks(drm_edid); drm_reset_display_info(connector); @@ -5446,7 +5449,7 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi info->non_desktop = !!(quirks & EDID_QUIRK_NON_DESKTOP); - drm_get_monitor_range(connector, edid); + drm_get_monitor_range(connector, drm_edid); DRM_DEBUG_KMS("non_desktop set to %d\n", info->non_desktop); @@ -5512,6 +5515,14 @@ u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edi return quirks; } +u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid) +{ + struct drm_edid drm_edid; + + return update_display_info(connector, + drm_edid_legacy_init(&drm_edid, edid)); +} + static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, struct displayid_detailed_timings_1 *timings) { @@ -5620,7 +5631,7 @@ static int drm_edid_connector_update(struct drm_connector *connector, * To avoid multiple parsing of same block, lets parse that map * from sink info, before parsing CEA modes. */ - quirks = drm_add_display_info(connector, edid); + quirks = update_display_info(connector, drm_edid); /* * EDID spec says modes should be preferred in this order: From 501df47dcb1656cd8a82b78e182480fb2f2c6bca Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Mon, 9 May 2022 15:03:22 +0300 Subject: [PATCH 193/227] drm/edid: add drm_edid helper for drm_update_tile_info() We'll need to propagate drm_edid everywhere. v2: Handle NULL EDID pointer (Ville, CI) Signed-off-by: Jani Nikula Reviewed-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/5b36683b656446a4d9a172d4dca1cf9aca08a48b.1652097712.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b2548dda3a537..4824093fd5933 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -6254,15 +6254,15 @@ static void drm_parse_tiled_block(struct drm_connector *connector, } } -void drm_update_tile_info(struct drm_connector *connector, - const struct edid *edid) +static void _drm_update_tile_info(struct drm_connector *connector, + const struct drm_edid *drm_edid) { const struct displayid_block *block; struct displayid_iter iter; connector->has_tile = false; - displayid_iter_edid_begin(edid, &iter); + displayid_iter_edid_begin(drm_edid ? drm_edid->edid : NULL, &iter); displayid_iter_for_each(block, &iter) { if (block->tag == DATA_BLOCK_TILED_DISPLAY) drm_parse_tiled_block(connector, block); @@ -6274,3 +6274,11 @@ void drm_update_tile_info(struct drm_connector *connector, connector->tile_group = NULL; } } + +void drm_update_tile_info(struct drm_connector *connector, + const struct edid *edid) +{ + struct drm_edid drm_edid; + + _drm_update_tile_info(connector, drm_edid_legacy_init(&drm_edid, edid)); +} From 00b8f9d32c066d56e6e16613e14fd9fc9a399418 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 11 May 2022 12:26:50 +0300 Subject: [PATCH 194/227] drm/edid: convert drm_edid_connector_update() to drm_edid fully We'll need to propagate drm_edid everywhere. v3: s/edid/drm_edid/ in comment too (Ankit) Signed-off-by: Jani Nikula Reviewed-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20220511092650.4193330-1-jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 94 ++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 49 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 4824093fd5933..b0575ccb1c909 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3195,17 +3195,16 @@ do_inferred_modes(struct detailed_timing *timing, void *c) } } -static int -add_inferred_modes(struct drm_connector *connector, struct edid *edid) +static int add_inferred_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .edid = drm_edid->edid, }; - if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, do_inferred_modes, - &closure); + if (version_greater(drm_edid->edid, 1, 0)) + drm_for_each_detailed_block((u8 *)drm_edid->edid, do_inferred_modes, &closure); return closure.modes; } @@ -3258,10 +3257,11 @@ do_established_modes(struct detailed_timing *timing, void *c) * Each EDID block contains a bitmap of the supported "established modes" list * (defined above). Tease them out and add them to the global modes list. */ -static int -add_established_modes(struct drm_connector *connector, struct edid *edid) +static int add_established_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_device *dev = connector->dev; + const struct edid *edid = drm_edid->edid; unsigned long est_bits = edid->established_timings.t1 | (edid->established_timings.t2 << 8) | ((edid->established_timings.mfg_rsvd & 0x80) << 9); @@ -3284,8 +3284,8 @@ add_established_modes(struct drm_connector *connector, struct edid *edid) } if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, - do_established_modes, &closure); + drm_for_each_detailed_block((u8 *)drm_edid->edid, do_established_modes, + &closure); return modes + closure.modes; } @@ -3322,28 +3322,28 @@ do_standard_modes(struct detailed_timing *timing, void *c) * Standard modes can be calculated using the appropriate standard (DMT, * GTF or CVT. Grab them from @edid and add them to the list. */ -static int -add_standard_modes(struct drm_connector *connector, struct edid *edid) +static int add_standard_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { int i, modes = 0; struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .edid = drm_edid->edid, }; for (i = 0; i < EDID_STD_TIMINGS; i++) { struct drm_display_mode *newmode; - newmode = drm_mode_std(connector, edid, - &edid->standard_timings[i]); + newmode = drm_mode_std(connector, drm_edid->edid, + &drm_edid->edid->standard_timings[i]); if (newmode) { drm_mode_probed_add(connector, newmode); modes++; } } - if (version_greater(edid, 1, 0)) - drm_for_each_detailed_block((u8 *)edid, do_standard_modes, + if (version_greater(drm_edid->edid, 1, 0)) + drm_for_each_detailed_block((u8 *)drm_edid->edid, do_standard_modes, &closure); /* XXX should also look for standard codes in VTB blocks */ @@ -3415,15 +3415,15 @@ do_cvt_mode(struct detailed_timing *timing, void *c) } static int -add_cvt_modes(struct drm_connector *connector, struct edid *edid) +add_cvt_modes(struct drm_connector *connector, const struct drm_edid *drm_edid) { struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .edid = drm_edid->edid, }; - if (version_greater(edid, 1, 2)) - drm_for_each_detailed_block((u8 *)edid, do_cvt_mode, &closure); + if (version_greater(drm_edid->edid, 1, 2)) + drm_for_each_detailed_block((u8 *)drm_edid->edid, do_cvt_mode, &closure); /* XXX should also look for CVT codes in VTB blocks */ @@ -3465,25 +3465,24 @@ do_detailed_mode(struct detailed_timing *timing, void *c) /* * add_detailed_modes - Add modes from detailed timings * @connector: attached connector - * @edid: EDID block to scan + * @drm_edid: EDID block to scan * @quirks: quirks to apply */ -static int -add_detailed_modes(struct drm_connector *connector, struct edid *edid, - u32 quirks) +static int add_detailed_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid, u32 quirks) { struct detailed_mode_closure closure = { .connector = connector, - .edid = edid, + .edid = drm_edid->edid, .preferred = true, .quirks = quirks, }; - if (closure.preferred && !version_greater(edid, 1, 3)) + if (closure.preferred && !version_greater(drm_edid->edid, 1, 3)) closure.preferred = - (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); + (drm_edid->edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING); - drm_for_each_detailed_block((u8 *)edid, do_detailed_mode, &closure); + drm_for_each_detailed_block((u8 *)drm_edid->edid, do_detailed_mode, &closure); return closure.modes; } @@ -3814,8 +3813,8 @@ static bool drm_valid_hdmi_vic(u8 vic) return vic > 0 && vic < ARRAY_SIZE(edid_4k_modes); } -static int -add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) +static int add_alternate_cea_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_device *dev = connector->dev; struct drm_display_mode *mode, *tmp; @@ -3823,7 +3822,7 @@ add_alternate_cea_modes(struct drm_connector *connector, struct edid *edid) int modes = 0; /* Don't add CEA modes if the CEA extension block is missing */ - if (!drm_find_cea_extension(edid)) + if (!drm_find_cea_extension(drm_edid->edid)) return 0; /* @@ -4492,9 +4491,10 @@ static void drm_parse_y420cmdb_bitmap(struct drm_connector *connector, hdmi->y420_cmdb_map = map; } -static int -add_cea_modes(struct drm_connector *connector, struct edid *edid) +static int add_cea_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { + struct edid *edid = drm_edid->edid; const u8 *cea = drm_find_cea_extension(edid); const u8 *db, *hdmi = NULL, *video = NULL; u8 dbl, hdmi_len, video_len = 0; @@ -5595,13 +5595,13 @@ static int add_displayid_detailed_1_modes(struct drm_connector *connector, } static int add_displayid_detailed_modes(struct drm_connector *connector, - struct edid *edid) + const struct drm_edid *drm_edid) { const struct displayid_block *block; struct displayid_iter iter; int num_modes = 0; - displayid_iter_edid_begin(edid, &iter); + displayid_iter_edid_begin(drm_edid->edid, &iter); displayid_iter_for_each(block, &iter) { if (block->tag == DATA_BLOCK_TYPE_1_DETAILED_TIMING) num_modes += add_displayid_detailed_1_modes(connector, block); @@ -5614,7 +5614,6 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, static int drm_edid_connector_update(struct drm_connector *connector, const struct drm_edid *drm_edid) { - const struct edid *edid; int num_modes = 0; u32 quirks; @@ -5623,9 +5622,6 @@ static int drm_edid_connector_update(struct drm_connector *connector, return 0; } - drm_edid_to_eld(connector, edid); - edid = drm_edid->edid; - /* * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks. * To avoid multiple parsing of same block, lets parse that map @@ -5647,15 +5643,15 @@ static int drm_edid_connector_update(struct drm_connector *connector, * * XXX order for additional mode types in extension blocks? */ - num_modes += add_detailed_modes(connector, edid, quirks); - num_modes += add_cvt_modes(connector, edid); - num_modes += add_standard_modes(connector, edid); - num_modes += add_established_modes(connector, edid); - num_modes += add_cea_modes(connector, edid); - num_modes += add_alternate_cea_modes(connector, edid); - num_modes += add_displayid_detailed_modes(connector, edid); - if (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) - num_modes += add_inferred_modes(connector, edid); + num_modes += add_detailed_modes(connector, drm_edid, quirks); + num_modes += add_cvt_modes(connector, drm_edid); + num_modes += add_standard_modes(connector, drm_edid); + num_modes += add_established_modes(connector, drm_edid); + num_modes += add_cea_modes(connector, drm_edid); + num_modes += add_alternate_cea_modes(connector, drm_edid); + num_modes += add_displayid_detailed_modes(connector, drm_edid); + if (drm_edid->edid->features & DRM_EDID_FEATURE_DEFAULT_GTF) + num_modes += add_inferred_modes(connector, drm_edid); if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75)) edid_fixup_preferred(connector, quirks); From 8f29461e466bd53c48c0c75880e634bcc8031971 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 29 Jun 2022 12:27:46 +0300 Subject: [PATCH 195/227] drm/edid: move drm_connector_update_edid_property() to drm_edid.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function needs access to drm_edid.c internals more than drm_connector.c. We can make drm_reset_display_info(), drm_add_display_info() and drm_update_tile_info() static. There will be more benefits with follow-up struct drm_edid refactoring. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/cbabce6ffb41fdc903533f21d946e8cae0667be5.1656494768.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_connector.c | 74 ------------------------- drivers/gpu/drm/drm_crtc_internal.h | 3 - drivers/gpu/drm/drm_edid.c | 86 +++++++++++++++++++++++++++-- 3 files changed, 81 insertions(+), 82 deletions(-) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index cfe163103cfd7..7ac81ebfdf12e 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -2017,80 +2017,6 @@ int drm_connector_set_tile_property(struct drm_connector *connector) } EXPORT_SYMBOL(drm_connector_set_tile_property); -/** - * drm_connector_update_edid_property - update the edid property of a connector - * @connector: drm connector - * @edid: new value of the edid property - * - * This function creates a new blob modeset object and assigns its id to the - * connector's edid property. - * Since we also parse tile information from EDID's displayID block, we also - * set the connector's tile property here. See drm_connector_set_tile_property() - * for more details. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_connector_update_edid_property(struct drm_connector *connector, - const struct edid *edid) -{ - struct drm_device *dev = connector->dev; - size_t size = 0; - int ret; - const struct edid *old_edid; - - /* ignore requests to set edid when overridden */ - if (connector->override_edid) - return 0; - - if (edid) - size = EDID_LENGTH * (1 + edid->extensions); - - /* Set the display info, using edid if available, otherwise - * resetting the values to defaults. This duplicates the work - * done in drm_add_edid_modes, but that function is not - * consistently called before this one in all drivers and the - * computation is cheap enough that it seems better to - * duplicate it rather than attempt to ensure some arbitrary - * ordering of calls. - */ - if (edid) - drm_add_display_info(connector, edid); - else - drm_reset_display_info(connector); - - drm_update_tile_info(connector, edid); - - if (connector->edid_blob_ptr) { - old_edid = (const struct edid *)connector->edid_blob_ptr->data; - if (old_edid) { - if (!drm_edid_are_equal(edid, old_edid)) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Edid was changed.\n", - connector->base.id, connector->name); - - connector->epoch_counter += 1; - DRM_DEBUG_KMS("Updating change counter to %llu\n", - connector->epoch_counter); - } - } - } - - drm_object_property_set_value(&connector->base, - dev->mode_config.non_desktop_property, - connector->display_info.non_desktop); - - ret = drm_property_replace_global_blob(dev, - &connector->edid_blob_ptr, - size, - edid, - &connector->base, - dev->mode_config.edid_property); - if (ret) - return ret; - return drm_connector_set_tile_property(connector); -} -EXPORT_SYMBOL(drm_connector_update_edid_property); - /** * drm_connector_set_link_status_property - Set link status property of a connector * @connector: drm connector diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index edb772947cb4a..dcaca1a4ce9c9 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -284,6 +284,3 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, /* drm_edid.c */ void drm_mode_fixup_1366x768(struct drm_display_mode *mode); -void drm_reset_display_info(struct drm_connector *connector); -u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid); -void drm_update_tile_info(struct drm_connector *connector, const struct edid *edid); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b0575ccb1c909..3f647b8388de9 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5412,8 +5412,7 @@ static void drm_get_monitor_range(struct drm_connector *connector, /* A connector has no EDID information, so we've got no EDID to compute quirks from. Reset * all of the values which would have been set from EDID */ -void -drm_reset_display_info(struct drm_connector *connector) +static void drm_reset_display_info(struct drm_connector *connector) { struct drm_display_info *info = &connector->display_info; @@ -5515,7 +5514,7 @@ static u32 update_display_info(struct drm_connector *connector, return quirks; } -u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid) +static u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid) { struct drm_edid drm_edid; @@ -5671,6 +5670,83 @@ static int drm_edid_connector_update(struct drm_connector *connector, return num_modes; } +static void drm_update_tile_info(struct drm_connector *connector, + const struct edid *edid); + +/** + * drm_connector_update_edid_property - update the edid property of a connector + * @connector: drm connector + * @edid: new value of the edid property + * + * This function creates a new blob modeset object and assigns its id to the + * connector's edid property. + * Since we also parse tile information from EDID's displayID block, we also + * set the connector's tile property here. See drm_connector_set_tile_property() + * for more details. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_update_edid_property(struct drm_connector *connector, + const struct edid *edid) +{ + struct drm_device *dev = connector->dev; + size_t size = 0; + int ret; + const struct edid *old_edid; + + /* ignore requests to set edid when overridden */ + if (connector->override_edid) + return 0; + + if (edid) + size = EDID_LENGTH * (1 + edid->extensions); + + /* + * Set the display info, using edid if available, otherwise resetting + * the values to defaults. This duplicates the work done in + * drm_add_edid_modes, but that function is not consistently called + * before this one in all drivers and the computation is cheap enough + * that it seems better to duplicate it rather than attempt to ensure + * some arbitrary ordering of calls. + */ + if (edid) + drm_add_display_info(connector, edid); + else + drm_reset_display_info(connector); + + drm_update_tile_info(connector, edid); + + if (connector->edid_blob_ptr) { + old_edid = (const struct edid *)connector->edid_blob_ptr->data; + if (old_edid) { + if (!drm_edid_are_equal(edid, old_edid)) { + DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Edid was changed.\n", + connector->base.id, connector->name); + + connector->epoch_counter += 1; + DRM_DEBUG_KMS("Updating change counter to %llu\n", + connector->epoch_counter); + } + } + } + + drm_object_property_set_value(&connector->base, + dev->mode_config.non_desktop_property, + connector->display_info.non_desktop); + + ret = drm_property_replace_global_blob(dev, + &connector->edid_blob_ptr, + size, + edid, + &connector->base, + dev->mode_config.edid_property); + if (ret) + return ret; + return drm_connector_set_tile_property(connector); +} +EXPORT_SYMBOL(drm_connector_update_edid_property); + /** * drm_add_edid_modes - add modes from EDID data, if available * @connector: connector we're probing @@ -6271,8 +6347,8 @@ static void _drm_update_tile_info(struct drm_connector *connector, } } -void drm_update_tile_info(struct drm_connector *connector, - const struct edid *edid) +static void drm_update_tile_info(struct drm_connector *connector, + const struct edid *edid) { struct drm_edid drm_edid; From e25e82d5a4c804fb22febd98df4ecf099f221198 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 29 Jun 2022 12:27:47 +0300 Subject: [PATCH 196/227] drm/edid: convert drm_connector_update_edid_property() to struct drm_edid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make drm_connector_update_edid_property() a thin wrapper around a struct drm_edid based version of the same. This lets us remove the legacy drm_update_tile_info() and drm_add_display_info() functions altogether. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/4bfe87ae392554ffb41d725353c4265ae56700bb.1656494768.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 81 ++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3f647b8388de9..69aba603509c0 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5514,14 +5514,6 @@ static u32 update_display_info(struct drm_connector *connector, return quirks; } -static u32 drm_add_display_info(struct drm_connector *connector, const struct edid *edid) -{ - struct drm_edid drm_edid; - - return update_display_info(connector, - drm_edid_legacy_init(&drm_edid, edid)); -} - static struct drm_display_mode *drm_mode_displayid_detailed(struct drm_device *dev, struct displayid_detailed_timings_1 *timings) { @@ -5670,38 +5662,19 @@ static int drm_edid_connector_update(struct drm_connector *connector, return num_modes; } -static void drm_update_tile_info(struct drm_connector *connector, - const struct edid *edid); +static void _drm_update_tile_info(struct drm_connector *connector, + const struct drm_edid *drm_edid); -/** - * drm_connector_update_edid_property - update the edid property of a connector - * @connector: drm connector - * @edid: new value of the edid property - * - * This function creates a new blob modeset object and assigns its id to the - * connector's edid property. - * Since we also parse tile information from EDID's displayID block, we also - * set the connector's tile property here. See drm_connector_set_tile_property() - * for more details. - * - * Returns: - * Zero on success, negative errno on failure. - */ -int drm_connector_update_edid_property(struct drm_connector *connector, - const struct edid *edid) +static int _drm_connector_update_edid_property(struct drm_connector *connector, + const struct drm_edid *drm_edid) { struct drm_device *dev = connector->dev; - size_t size = 0; int ret; - const struct edid *old_edid; /* ignore requests to set edid when overridden */ if (connector->override_edid) return 0; - if (edid) - size = EDID_LENGTH * (1 + edid->extensions); - /* * Set the display info, using edid if available, otherwise resetting * the values to defaults. This duplicates the work done in @@ -5710,17 +5683,18 @@ int drm_connector_update_edid_property(struct drm_connector *connector, * that it seems better to duplicate it rather than attempt to ensure * some arbitrary ordering of calls. */ - if (edid) - drm_add_display_info(connector, edid); + if (drm_edid) + update_display_info(connector, drm_edid); else drm_reset_display_info(connector); - drm_update_tile_info(connector, edid); + _drm_update_tile_info(connector, drm_edid); if (connector->edid_blob_ptr) { - old_edid = (const struct edid *)connector->edid_blob_ptr->data; + const struct edid *old_edid = connector->edid_blob_ptr->data; + if (old_edid) { - if (!drm_edid_are_equal(edid, old_edid)) { + if (!drm_edid_are_equal(drm_edid ? drm_edid->edid : NULL, old_edid)) { DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Edid was changed.\n", connector->base.id, connector->name); @@ -5737,14 +5711,37 @@ int drm_connector_update_edid_property(struct drm_connector *connector, ret = drm_property_replace_global_blob(dev, &connector->edid_blob_ptr, - size, - edid, + drm_edid ? drm_edid->size : 0, + drm_edid ? drm_edid->edid : NULL, &connector->base, dev->mode_config.edid_property); if (ret) return ret; return drm_connector_set_tile_property(connector); } + +/** + * drm_connector_update_edid_property - update the edid property of a connector + * @connector: drm connector + * @edid: new value of the edid property + * + * This function creates a new blob modeset object and assigns its id to the + * connector's edid property. + * Since we also parse tile information from EDID's displayID block, we also + * set the connector's tile property here. See drm_connector_set_tile_property() + * for more details. + * + * Returns: + * Zero on success, negative errno on failure. + */ +int drm_connector_update_edid_property(struct drm_connector *connector, + const struct edid *edid) +{ + struct drm_edid drm_edid; + + return _drm_connector_update_edid_property(connector, + drm_edid_legacy_init(&drm_edid, edid)); +} EXPORT_SYMBOL(drm_connector_update_edid_property); /** @@ -6346,11 +6343,3 @@ static void _drm_update_tile_info(struct drm_connector *connector, connector->tile_group = NULL; } } - -static void drm_update_tile_info(struct drm_connector *connector, - const struct edid *edid) -{ - struct drm_edid drm_edid; - - _drm_update_tile_info(connector, drm_edid_legacy_init(&drm_edid, edid)); -} From 1ace56cede0893970fbde7fa269ac80e0966879e Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 29 Jun 2022 12:27:48 +0300 Subject: [PATCH 197/227] drm/edid: clean up connector update error handling and debug logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bail out on all errors, debug log all errors, and convert to drm device based debug logging. Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/de4270b3d2e61fe42b9248490376594d472d19aa.1656494768.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 41 ++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 69aba603509c0..242e2485ac622 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5695,29 +5695,44 @@ static int _drm_connector_update_edid_property(struct drm_connector *connector, if (old_edid) { if (!drm_edid_are_equal(drm_edid ? drm_edid->edid : NULL, old_edid)) { - DRM_DEBUG_KMS("[CONNECTOR:%d:%s] Edid was changed.\n", - connector->base.id, connector->name); - - connector->epoch_counter += 1; - DRM_DEBUG_KMS("Updating change counter to %llu\n", - connector->epoch_counter); + connector->epoch_counter++; + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID changed, epoch counter %llu\n", + connector->base.id, connector->name, + connector->epoch_counter); } } } - drm_object_property_set_value(&connector->base, - dev->mode_config.non_desktop_property, - connector->display_info.non_desktop); - ret = drm_property_replace_global_blob(dev, &connector->edid_blob_ptr, drm_edid ? drm_edid->size : 0, drm_edid ? drm_edid->edid : NULL, &connector->base, dev->mode_config.edid_property); - if (ret) - return ret; - return drm_connector_set_tile_property(connector); + if (ret) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] EDID property update failed (%d)\n", + connector->base.id, connector->name, ret); + goto out; + } + + ret = drm_object_property_set_value(&connector->base, + dev->mode_config.non_desktop_property, + connector->display_info.non_desktop); + if (ret) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Non-desktop property update failed (%d)\n", + connector->base.id, connector->name, ret); + goto out; + } + + ret = drm_connector_set_tile_property(connector); + if (ret) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] Tile property update failed (%d)\n", + connector->base.id, connector->name, ret); + goto out; + } + +out: + return ret; } /** From c348b1be12e0e3436266de916a9b8dec57e2708d Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 29 Jun 2022 12:27:49 +0300 Subject: [PATCH 198/227] drm/edid: abstract debugfs override EDID set/reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add functions drm_edid_override_set() and drm_edid_override_reset() to support "edid_override" connector debugfs, and to hide the details about it in drm_edid.c. No functional changes at this time. Also note in the connector.override_edid flag kernel-doc that this is only supposed to be modified by the code doing debugfs EDID override handling. Currently, it is still being modified by amdgpu in create_eml_sink() and handle_edid_mgmt() for reasons unknown. This was added in commit 4562236b3bc0 ("drm/amd/dc: Add dc display driver (v2)") and later moved to amdgpu_dm.c in commit e7b07ceef2a6 ("drm/amd/display: Merge amdgpu_dm_types and amdgpu_dm"). Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/8f6b4001630cafac5f44aa5913429ac9979743d2.1656494768.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_crtc_internal.h | 2 ++ drivers/gpu/drm/drm_debugfs.c | 21 +++++---------------- drivers/gpu/drm/drm_edid.c | 26 ++++++++++++++++++++++++++ include/drm/drm_connector.h | 6 +++++- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index dcaca1a4ce9c9..b56ef34338d3a 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -284,3 +284,5 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, /* drm_edid.c */ void drm_mode_fixup_1366x768(struct drm_display_mode *mode); +int drm_edid_override_set(struct drm_connector *connector, const void *edid, size_t size); +int drm_edid_override_reset(struct drm_connector *connector); diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 7f1b82dbaebb9..6be503564c557 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -350,31 +350,20 @@ static ssize_t edid_write(struct file *file, const char __user *ubuf, struct seq_file *m = file->private_data; struct drm_connector *connector = m->private; char *buf; - struct edid *edid; int ret; buf = memdup_user(ubuf, len); if (IS_ERR(buf)) return PTR_ERR(buf); - edid = (struct edid *) buf; - - if (len == 5 && !strncmp(buf, "reset", 5)) { - connector->override_edid = false; - ret = drm_connector_update_edid_property(connector, NULL); - } else if (len < EDID_LENGTH || - EDID_LENGTH * (1 + edid->extensions) > len) - ret = -EINVAL; - else { - connector->override_edid = false; - ret = drm_connector_update_edid_property(connector, edid); - if (!ret) - connector->override_edid = true; - } + if (len == 5 && !strncmp(buf, "reset", 5)) + ret = drm_edid_override_reset(connector); + else + ret = drm_edid_override_set(connector, buf, len); kfree(buf); - return (ret) ? ret : len; + return ret ? ret : len; } /* diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 242e2485ac622..3ff6886173603 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1945,6 +1945,32 @@ static struct edid *drm_get_override_edid(struct drm_connector *connector) return IS_ERR(override) ? NULL : override; } +/* For debugfs edid_override implementation */ +int drm_edid_override_set(struct drm_connector *connector, const void *edid, + size_t size) +{ + int ret; + + if (size < EDID_LENGTH || edid_size(edid) > size) + return -EINVAL; + + connector->override_edid = false; + + ret = drm_connector_update_edid_property(connector, edid); + if (!ret) + connector->override_edid = true; + + return ret; +} + +/* For debugfs edid_override implementation */ +int drm_edid_override_reset(struct drm_connector *connector) +{ + connector->override_edid = false; + + return drm_connector_update_edid_property(connector, NULL); +} + /** * drm_add_override_edid_modes - add modes from override/firmware EDID * @connector: connector we're probing diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index f7ee6a538ddb9..57a37355abfa9 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h @@ -1440,7 +1440,11 @@ struct drm_connector { struct drm_cmdline_mode cmdline_mode; /** @force: a DRM_FORCE_ state for forced mode sets */ enum drm_connector_force force; - /** @override_edid: has the EDID been overwritten through debugfs for testing? */ + /** + * @override_edid: has the EDID been overwritten through debugfs for + * testing? Do not modify outside of drm_edid_override_set() and + * drm_edid_override_reset(). + */ bool override_edid; /** @epoch_counter: used to detect any other changes in connector, besides status */ u64 epoch_counter; From 5681d34285189aac291397bad0cf5961f2d95932 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Wed, 29 Jun 2022 12:27:50 +0300 Subject: [PATCH 199/227] drm/edid: add drm_edid_connector_update() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new function drm_edid_connector_update() to replace the combination of calls drm_connector_update_edid_property() and drm_add_edid_modes(). Usually they are called in the drivers in this order, however the former needs information from the latter. Since the new drm_edid_read*() functions no longer call the connector updates directly, and the read and update are separated, we'll need this new function for the connector update. This is all in drm_edid.c simply to keep struct drm_edid opaque. v2: - Share code with drm_connector_update_edid_property() (Ville) - Add comment about override EDID handling Signed-off-by: Jani Nikula Reviewed-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/75aa3dbc8c9aa26ebbcdeacd98a466ef8d8827f4.1656494768.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 103 ++++++++++++++++++++++++++++--------- include/drm/drm_edid.h | 2 + 2 files changed, 81 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 3ff6886173603..b22735120d74a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5628,8 +5628,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, return num_modes; } -static int drm_edid_connector_update(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static int _drm_edid_connector_update(struct drm_connector *connector, + const struct drm_edid *drm_edid) { int num_modes = 0; u32 quirks; @@ -5691,31 +5691,12 @@ static int drm_edid_connector_update(struct drm_connector *connector, static void _drm_update_tile_info(struct drm_connector *connector, const struct drm_edid *drm_edid); -static int _drm_connector_update_edid_property(struct drm_connector *connector, +static int _drm_edid_connector_property_update(struct drm_connector *connector, const struct drm_edid *drm_edid) { struct drm_device *dev = connector->dev; int ret; - /* ignore requests to set edid when overridden */ - if (connector->override_edid) - return 0; - - /* - * Set the display info, using edid if available, otherwise resetting - * the values to defaults. This duplicates the work done in - * drm_add_edid_modes, but that function is not consistently called - * before this one in all drivers and the computation is cheap enough - * that it seems better to duplicate it rather than attempt to ensure - * some arbitrary ordering of calls. - */ - if (drm_edid) - update_display_info(connector, drm_edid); - else - drm_reset_display_info(connector); - - _drm_update_tile_info(connector, drm_edid); - if (connector->edid_blob_ptr) { const struct edid *old_edid = connector->edid_blob_ptr->data; @@ -5761,6 +5742,76 @@ static int _drm_connector_update_edid_property(struct drm_connector *connector, return ret; } +/** + * drm_edid_connector_update - Update connector information from EDID + * @connector: Connector + * @drm_edid: EDID + * + * Update the connector mode list, display info, ELD, HDR metadata, relevant + * properties, etc. from the passed in EDID. + * + * If EDID is NULL, reset the information. + * + * Return: The number of modes added or 0 if we couldn't find any. + */ +int drm_edid_connector_update(struct drm_connector *connector, + const struct drm_edid *drm_edid) +{ + int count; + + /* + * FIXME: Reconcile the differences in override_edid handling between + * this and drm_connector_update_edid_property(). + * + * If override_edid is set, and the EDID passed in here originates from + * drm_edid_read() and friends, it will be the override EDID, and there + * are no issues. drm_connector_update_edid_property() ignoring requests + * to set the EDID dates back to a time when override EDID was not + * handled at the low level EDID read. + * + * The only way the EDID passed in here can be different from the + * override EDID is when a driver passes in an EDID that does *not* + * originate from drm_edid_read() and friends, or passes in a stale + * cached version. This, in turn, is a question of when an override EDID + * set via debugfs should take effect. + */ + + count = _drm_edid_connector_update(connector, drm_edid); + + _drm_update_tile_info(connector, drm_edid); + + /* Note: Ignore errors for now. */ + _drm_edid_connector_property_update(connector, drm_edid); + + return count; +} +EXPORT_SYMBOL(drm_edid_connector_update); + +static int _drm_connector_update_edid_property(struct drm_connector *connector, + const struct drm_edid *drm_edid) +{ + /* ignore requests to set edid when overridden */ + if (connector->override_edid) + return 0; + + /* + * Set the display info, using edid if available, otherwise resetting + * the values to defaults. This duplicates the work done in + * drm_add_edid_modes, but that function is not consistently called + * before this one in all drivers and the computation is cheap enough + * that it seems better to duplicate it rather than attempt to ensure + * some arbitrary ordering of calls. + */ + if (drm_edid) + update_display_info(connector, drm_edid); + else + drm_reset_display_info(connector); + + _drm_update_tile_info(connector, drm_edid); + + return _drm_edid_connector_property_update(connector, drm_edid); +} + /** * drm_connector_update_edid_property - update the edid property of a connector * @connector: drm connector @@ -5772,6 +5823,8 @@ static int _drm_connector_update_edid_property(struct drm_connector *connector, * set the connector's tile property here. See drm_connector_set_tile_property() * for more details. * + * This function is deprecated. Use drm_edid_connector_update() instead. + * * Returns: * Zero on success, negative errno on failure. */ @@ -5794,6 +5847,8 @@ EXPORT_SYMBOL(drm_connector_update_edid_property); * &drm_display_info structure and ELD in @connector with any information which * can be derived from the edid. * + * This function is deprecated. Use drm_edid_connector_update() instead. + * * Return: The number of modes added or 0 if we couldn't find any. */ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) @@ -5806,8 +5861,8 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) edid = NULL; } - return drm_edid_connector_update(connector, - drm_edid_legacy_init(&drm_edid, edid)); + return _drm_edid_connector_update(connector, + drm_edid_legacy_init(&drm_edid, edid)); } EXPORT_SYMBOL(drm_add_edid_modes); diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index b5c4bb96f71e1..8c89117fe3f40 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -552,6 +552,8 @@ const struct drm_edid *drm_edid_read_ddc(struct drm_connector *connector, const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector, int (*read_block)(void *context, u8 *buf, unsigned int block, size_t len), void *context); +int drm_edid_connector_update(struct drm_connector *connector, + const struct drm_edid *edid); const u8 *drm_find_edid_extension(const struct edid *edid, int ext_id, int *ext_index); From 2204e55a1f527154289050519974d64484f2cee4 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 19 Jan 2023 18:18:55 +0200 Subject: [PATCH 200/227] drm/edid: refactor _drm_edid_connector_update() and rename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By moving update_display_info() out of _drm_edid_connector_update() we make the function purely about adding modes. Rename accordingly. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/e9880bbb2b5724d9aac88a90a31ba3ba9af9da3f.1674144945.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index b22735120d74a..e946ccc77fbc4 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5628,8 +5628,8 @@ static int add_displayid_detailed_modes(struct drm_connector *connector, return num_modes; } -static int _drm_edid_connector_update(struct drm_connector *connector, - const struct drm_edid *drm_edid) +static int _drm_edid_connector_add_modes(struct drm_connector *connector, + const struct drm_edid *drm_edid) { int num_modes = 0; u32 quirks; @@ -5639,13 +5639,6 @@ static int _drm_edid_connector_update(struct drm_connector *connector, return 0; } - /* - * CEA-861-F adds ycbcr capability map block, for HDMI 2.0 sinks. - * To avoid multiple parsing of same block, lets parse that map - * from sink info, before parsing CEA modes. - */ - quirks = update_display_info(connector, drm_edid); - /* * EDID spec says modes should be preferred in this order: * - preferred detailed mode @@ -5776,7 +5769,9 @@ int drm_edid_connector_update(struct drm_connector *connector, * set via debugfs should take effect. */ - count = _drm_edid_connector_update(connector, drm_edid); + update_display_info(connector, drm_edid); + + count = _drm_edid_connector_add_modes(connector, drm_edid); _drm_update_tile_info(connector, drm_edid); @@ -5853,7 +5848,8 @@ EXPORT_SYMBOL(drm_connector_update_edid_property); */ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) { - struct drm_edid drm_edid; + struct drm_edid _drm_edid; + const struct drm_edid *drm_edid; if (edid && !drm_edid_is_valid(edid)) { drm_warn(connector->dev, "%s: EDID invalid.\n", @@ -5861,8 +5857,11 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid) edid = NULL; } - return _drm_edid_connector_update(connector, - drm_edid_legacy_init(&drm_edid, edid)); + drm_edid = drm_edid_legacy_init(&_drm_edid, edid); + + update_display_info(connector, drm_edid); + + return _drm_edid_connector_add_modes(connector, drm_edid); } EXPORT_SYMBOL(drm_add_edid_modes); From f81a0cb405a6af09bff71bab769c53a1f9cbc7b3 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Thu, 19 Jan 2023 18:18:56 +0200 Subject: [PATCH 201/227] drm/edid: add separate drm_edid_connector_add_modes() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original goal with drm_edid_connector_update() was to have a single call for updating the connector and adding probed modes, in this order, but that turned out to be problematic. Drivers that need to update the connector in the .detect() callback would end up updating the probed modes as well. Turns out the callback may be called so many times that the probed mode list fills up without bounds, and this is amplified by add_alternate_cea_modes() duplicating the CEA modes on every call, actually running out of memory on some machines. Kudos to Imre Deak for explaining this to me. Go back to having separate drm_edid_connector_update() and drm_edid_connector_add_modes() calls. The former may be called from .detect(), .force(), or .get_modes(), but the latter only from .get_modes(). Unlike drm_add_edid_modes(), have drm_edid_connector_add_modes() update the probed modes from the EDID property instead of the passed in EDID. This is mainly to enforce two things: 1) drm_edid_connector_update() must be called before drm_edid_connector_add_modes(). Display info and quirks are needed for parsing the modes, and we don't want to call update_display_info() again to ensure the info is available, like drm_add_edid_modes() does. 2) The same EDID is used for both updating the connector and adding the probed modes. Fortunately, the change is easy, because no driver has actually adopted drm_edid_connector_update(). Not even i915, and that's mainly because of the problem described above. Cc: Imre Deak Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/e86fff1579f14ebf6334692526c8f6831cd02cac.1674144945.git.jani.nikula@intel.com Signed-off-by: chenyi --- drivers/gpu/drm/drm_edid.c | 61 +++++++++++++++++++++----------------- include/drm/drm_edid.h | 2 ++ 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index e946ccc77fbc4..c94d48027c2fb 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -5740,47 +5740,54 @@ static int _drm_edid_connector_property_update(struct drm_connector *connector, * @connector: Connector * @drm_edid: EDID * - * Update the connector mode list, display info, ELD, HDR metadata, relevant - * properties, etc. from the passed in EDID. + * Update the connector display info, ELD, HDR metadata, relevant properties, + * etc. from the passed in EDID. * * If EDID is NULL, reset the information. * - * Return: The number of modes added or 0 if we couldn't find any. + * Must be called before calling drm_edid_connector_add_modes(). + * + * Return: 0 on success, negative error on errors. */ int drm_edid_connector_update(struct drm_connector *connector, const struct drm_edid *drm_edid) { - int count; + update_display_info(connector, drm_edid); - /* - * FIXME: Reconcile the differences in override_edid handling between - * this and drm_connector_update_edid_property(). - * - * If override_edid is set, and the EDID passed in here originates from - * drm_edid_read() and friends, it will be the override EDID, and there - * are no issues. drm_connector_update_edid_property() ignoring requests - * to set the EDID dates back to a time when override EDID was not - * handled at the low level EDID read. - * - * The only way the EDID passed in here can be different from the - * override EDID is when a driver passes in an EDID that does *not* - * originate from drm_edid_read() and friends, or passes in a stale - * cached version. This, in turn, is a question of when an override EDID - * set via debugfs should take effect. - */ + _drm_update_tile_info(connector, drm_edid); - update_display_info(connector, drm_edid); + return _drm_edid_connector_property_update(connector, drm_edid); +} +EXPORT_SYMBOL(drm_edid_connector_update); - count = _drm_edid_connector_add_modes(connector, drm_edid); +/** + * drm_edid_connector_add_modes - Update probed modes from the EDID property + * @connector: Connector + * + * Add the modes from the previously updated EDID property to the connector + * probed modes list. + * + * drm_edid_connector_update() must have been called before this to update the + * EDID property. + * + * Return: The number of modes added, or 0 if we couldn't find any. + */ +int drm_edid_connector_add_modes(struct drm_connector *connector) +{ + const struct drm_edid *drm_edid = NULL; + int count; - _drm_update_tile_info(connector, drm_edid); + if (connector->edid_blob_ptr) + drm_edid = drm_edid_alloc(connector->edid_blob_ptr->data, + connector->edid_blob_ptr->length); + + count = _drm_edid_connector_add_modes(connector, drm_edid); - /* Note: Ignore errors for now. */ - _drm_edid_connector_property_update(connector, drm_edid); + drm_edid_free(drm_edid); return count; } -EXPORT_SYMBOL(drm_edid_connector_update); +EXPORT_SYMBOL(drm_edid_connector_add_modes); static int _drm_connector_update_edid_property(struct drm_connector *connector, const struct drm_edid *drm_edid) @@ -5842,7 +5849,7 @@ EXPORT_SYMBOL(drm_connector_update_edid_property); * &drm_display_info structure and ELD in @connector with any information which * can be derived from the edid. * - * This function is deprecated. Use drm_edid_connector_update() instead. + * This function is deprecated. Use drm_edid_connector_add_modes() instead. * * Return: The number of modes added or 0 if we couldn't find any. */ diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 8c89117fe3f40..7cdbbb0a5c860 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -554,6 +554,8 @@ const struct drm_edid *drm_edid_read_custom(struct drm_connector *connector, void *context); int drm_edid_connector_update(struct drm_connector *connector, const struct drm_edid *edid); +int drm_edid_connector_add_modes(struct drm_connector *connector); + const u8 *drm_find_edid_extension(const struct edid *edid, int ext_id, int *ext_index); From a3091f37f99855cf58364f7dba473fdb2ec35053 Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 14 Mar 2025 15:34:22 +0800 Subject: [PATCH 202/227] drm/hisilicon/hibmc: convert to struct drm_edid maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IBSNIA CVE: NA Reference: https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ff2a391349afcf62e99897de9bfd854ecf9d21b8 ---------------------------------------------------------------------- Prefer the struct drm_edid based functions for reading the EDID and updating the connector. Reviewed-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/386e3a64efbdd61c3eaed3f49ea9c3ebd4fcd41d.1715691257.git.jani.nikula@intel.com Signed-off-by: Jani Nikula Signed-off-by: Baihan Li Signed-off-by: chenyi --- .../gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c index 2888facf8fc0e..b1be55a56b2e8 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_vdac.c @@ -23,14 +23,16 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) { - int count; - void *edid; struct hibmc_vdac *vdac = to_hibmc_vdac(connector); + const struct drm_edid *drm_edid; + int count; + + drm_edid = drm_edid_read_ddc(connector, &vdac->adapter); - edid = drm_get_edid(connector, &vdac->adapter); - if (edid) { - drm_connector_update_edid_property(connector, edid); - count = drm_add_edid_modes(connector, edid); + drm_edid_connector_update(connector, drm_edid); + + if (drm_edid) { + count = drm_edid_connector_add_modes(connector); if (count) goto out; } @@ -41,7 +43,8 @@ static int hibmc_connector_get_modes(struct drm_connector *connector) drm_set_preferred_mode(connector, 1024, 768); out: - kfree(edid); + drm_edid_free(drm_edid); + return count; } From d851c67264ad9c11dfb80e23a40de6d398da8135 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 29 Aug 2024 17:03:32 +0800 Subject: [PATCH 203/227] drivers/perf: hisi_pcie: Export supported Root Ports [bdf_min, bdf_max] maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IAQG4N CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/commit/?id=d1c93d5c67ebc7cf5b70ecff7172a0c399975d55 ---------------------------------------------------------------------- Currently users can get the Root Ports supported by the PCIe PMU by "bus" sysfs attributes which indicates the PCIe bus number where Root Ports are located. This maybe insufficient since Root Ports supported by different PCIe PMUs may be located on the same PCIe bus. So export the BDF range the Root Ports additionally. Signed-off-by: Yicong Yang Acked-by: Jonathan Cameron Link: https://lore.kernel.org/r/20240829090332.28756-4-yangyicong@huawei.com Signed-off-by: Will Deacon Signed-off-by: zhangyuyang Signed-off-by: Slim6882 <15605196882@163.com> --- .../admin-guide/perf/hisi-pcie-pmu.rst | 4 +++- drivers/perf/hisilicon/hisi_pcie_pmu.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/perf/hisi-pcie-pmu.rst b/Documentation/admin-guide/perf/hisi-pcie-pmu.rst index 5541ff40e06a8..083ca50de896b 100644 --- a/Documentation/admin-guide/perf/hisi-pcie-pmu.rst +++ b/Documentation/admin-guide/perf/hisi-pcie-pmu.rst @@ -28,7 +28,9 @@ The "identifier" sysfs file allows users to identify the version of the PMU hardware device. The "bus" sysfs file allows users to get the bus number of Root Ports -monitored by PMU. +monitored by PMU. Furthermore users can get the Root Ports range in +[bdf_min, bdf_max] from "bdf_min" and "bdf_max" sysfs attributes +respectively. Example usage of perf:: diff --git a/drivers/perf/hisilicon/hisi_pcie_pmu.c b/drivers/perf/hisilicon/hisi_pcie_pmu.c index 86ac08f3d9a65..2511d732a6ac6 100644 --- a/drivers/perf/hisilicon/hisi_pcie_pmu.c +++ b/drivers/perf/hisilicon/hisi_pcie_pmu.c @@ -152,6 +152,22 @@ static ssize_t bus_show(struct device *dev, struct device_attribute *attr, char } static DEVICE_ATTR_RO(bus); +static ssize_t bdf_min_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(dev_get_drvdata(dev)); + + return sysfs_emit(buf, "%#04x\n", pcie_pmu->bdf_min); +} +static DEVICE_ATTR_RO(bdf_min); + +static ssize_t bdf_max_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hisi_pcie_pmu *pcie_pmu = to_pcie_pmu(dev_get_drvdata(dev)); + + return sysfs_emit(buf, "%#04x\n", pcie_pmu->bdf_max); +} +static DEVICE_ATTR_RO(bdf_max); + static struct hisi_pcie_reg_pair hisi_pcie_parse_reg_value(struct hisi_pcie_pmu *pcie_pmu, u32 reg_off) { @@ -775,6 +791,8 @@ static const struct attribute_group hisi_pcie_pmu_format_group = { static struct attribute *hisi_pcie_pmu_bus_attrs[] = { &dev_attr_bus.attr, + &dev_attr_bdf_max.attr, + &dev_attr_bdf_min.attr, NULL }; From 161717e3e77c0ef5cfa70d775f357ab92c2852a7 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Thu, 10 Apr 2025 16:25:50 +0800 Subject: [PATCH 204/227] perf stat: Enable iostat mode for HiSilicon PCIe PMU driver inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/IC02AC ---------------------------------------------------------------------- Some HiSilicon platforms provide PCIe PMU devices for monitoring the bandwidth and latency of PCIe traffic. With the support of PCIe PMU we can enable the perf iostat mode. The HiSilicon PCIe PMU can support measuring the bandwidth of certain TLP types and of certian root port. Totally 6 metrics are provided in the unit of MB: - Inbound MWR: The memory write TLPs from the devices downstream the root port - Inbound MRD: The memory read TLPs from the devices downstream the root port - Inbound CPL: The completion TLPs from the devices downstream the root port - Outbound MWR: The memory write TLPs from the CPU to the downstream devices - Outbound MRD: The memory read TLPs from the CPU to the downstream devices - Outbound CPL: The completions TLPs from the CPU to the downstream devices Since the PMU measure the throughput with unit of DWords. So we need the calculate the actually bandwidth like: Count * 4B / 1024 / 1024 / Time_elapsed (MB) Some of the display of the `perf iostat` will be like: [root@localhost tmp]# ./perf iostat list hisi_pcie8_core2<0000:aa:00.0> hisi_pcie0_core2<0000:40:00.0> hisi_pcie10_core2<0000:d5:00.0> hisi_pcie8_core1<0000:95:04.0> hisi_pcie8_core1<0000:95:00.0> hisi_pcie2_core2<0000:5f:00.0> hisi_pcie0_core1<0000:16:00.0> hisi_pcie0_core1<0000:16:04.0> [root@localhost tmp]# ./perf iostat --timeout 10000 Performance counter stats for 'system wide': port Inbound MWR(MB) Inbound MRD(MB) Inbound CPL(MB) Outbound MWR(MB) Outbound MRD(MB) Outbound CPL(MB) 0000:aa:00.0 0 0 0 0 0 0 0000:40:00.0 0 0 0 0 0 0 0000:d5:00.0 0 0 0 0 0 0 0000:95:04.0 9 81 0 1 0 2497 0000:95:00.0 0 0 0 0 0 0 0000:5f:00.0 0 0 0 0 0 0 0000:16:00.0 0 0 0 0 0 0 0000:16:04.0 5658 2 0 2 0 17 10.006795510 seconds time elapsed [root@localhost tmp]# ./perf iostat 0000:95:04.0 -- iperf -c 192.100.100.1 -t 30 ------------------------------------------------------------ Client connecting to 192.100.100.1, TCP port 5001 TCP window size: 1.79 MByte (default) ------------------------------------------------------------ [ 3] local 192.100.100.2 port 57130 connected with 192.100.100.1 port 5001 [ ID] Interval Transfer Bandwidth [ 3] 0.0-30.0 sec 32.9 GBytes 9.41 Gbits/sec Performance counter stats for 'system wide': port Inbound MWR(MB) Inbound MRD(MB) Inbound CPL(MB) Outbound MWR(MB) Outbound MRD(MB) Outbound CPL(MB) 0000:95:04.0 4 41 0 1 0 1259 30.060558310 seconds time elapsed 0.060853000 seconds user 3.758800000 seconds sys More information of the HiSilicon PCIe PMU can be found at Documentation/admin-guide/perf/hisi-pcie-pmu.rst. Signed-off-by: Yicong Yang Signed-off-by: Yushan Wang Signed-off-by: Qizhi Zhang Signed-off-by: Slim6882 <15605196882@163.com> --- tools/perf/arch/arm64/util/Build | 1 + tools/perf/arch/arm64/util/hisi-iostat.c | 431 +++++++++++++++++++++++ 2 files changed, 432 insertions(+) create mode 100644 tools/perf/arch/arm64/util/hisi-iostat.c diff --git a/tools/perf/arch/arm64/util/Build b/tools/perf/arch/arm64/util/Build index f19839b3509b8..1464d098c43e0 100644 --- a/tools/perf/arch/arm64/util/Build +++ b/tools/perf/arch/arm64/util/Build @@ -3,6 +3,7 @@ perf-y += perf_regs.o perf-y += tsc.o perf-y += pmu.o perf-y += kvm-stat.o +perf-y += hisi-iostat.o perf-$(CONFIG_DWARF) += dwarf-regs.o perf-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind.o perf-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o diff --git a/tools/perf/arch/arm64/util/hisi-iostat.c b/tools/perf/arch/arm64/util/hisi-iostat.c new file mode 100644 index 0000000000000..96684cc004648 --- /dev/null +++ b/tools/perf/arch/arm64/util/hisi-iostat.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * perf iostat support for HiSilicon PCIe PMU. + * Partly derived from tools/perf/arch/x86/util/iostat.c. + * + * Copyright (c) 2024 HiSilicon Technologies Co., Ltd. + * Author: Yicong Yang + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/counts.h" +#include "util/cpumap.h" +#include "util/debug.h" +#include "util/iostat.h" +#include "util/pmu.h" + +#define PCI_DEFAULT_DOMAIN 0 +#define PCI_DEVICE_NAME_PATTERN "%04x:%02hhx:%02hhx.%hhu" +#define PCI_ROOT_BUS_DEVICES_PATH "bus/pci/devices" + +static const char *hisi_iostat_metrics[] = { + "Inbound MWR(MB)", + "Inbound MRD(MB)", + "Inbound CPL(MB)", + "Outbound MWR(MB)", + "Outbound MRD(MB)", + "Outbound CPL(MB)", +}; + +static const char *hisi_iostat_cmd_template[] = { + /* Inbound Memory Write */ + "hisi_pcie%hu_core%hu/event=0x0104,port=0x%hx/", + /* Inbound Memory Read */ + "hisi_pcie%hu_core%hu/event=0x0804,port=0x%hx/", + /* Inbound Memory Completion */ + "hisi_pcie%hu_core%hu/event=0x2004,port=0x%hx/", + /* Outbound Memory Write */ + "hisi_pcie%hu_core%hu/event=0x0105,port=0x%hx/", + /* Outbound Memory Read */ + "hisi_pcie%hu_core%hu/event=0x0405,port=0x%hx/", + /* Outbound Memory Completion */ + "hisi_pcie%hu_core%hu/event=0x1005,port=0x%hx/", +}; + +struct hisi_pcie_root_port { + struct list_head list; + /* Is this Root Port selected for monitoring */ + bool selected; + /* IDs to locate the PMU */ + u16 sicl_id; + u16 core_id; + /* Filter mask for this Root Port */ + u16 mask; + /* PCIe Root Port's ::. */ + u32 domain; + u8 bus; + u8 dev; + u8 fn; +}; + +LIST_HEAD(hisi_pcie_root_ports_list); +static int hisi_pcie_root_ports_num; + +static void hisi_pcie_init_root_port_mask(struct hisi_pcie_root_port *rp) +{ + rp->mask = BIT(rp->dev << 1); +} + +/* + * Select specific Root Port to monitor. Return 0 if successfully find the + * Root Port, Otherwise -EINVAL. + */ +static int hisi_pcie_root_ports_select_one(u32 domain, u8 bus, u8 dev, u8 fn) +{ + struct hisi_pcie_root_port *rp; + + list_for_each_entry(rp, &hisi_pcie_root_ports_list, list) + if (domain == rp->domain && bus == rp->bus && + dev == rp->dev && fn == rp->fn) { + rp->selected = true; + return 0; + } + + return -EINVAL; +} + +static void hisi_pcie_root_ports_select_all(void) +{ + struct hisi_pcie_root_port *rp; + + list_for_each_entry(rp, &hisi_pcie_root_ports_list, list) + rp->selected = true; +} + +static void hisi_pcie_root_ports_add(u16 sicl_id, u16 core_id, u8 target_bus) +{ + const char *sysfs = sysfs__mountpoint(); + struct hisi_pcie_root_port *rp; + struct dirent *dent; + char path[PATH_MAX]; + u8 bus, dev, fn; + u32 domain; + DIR *dir; + int ret; + + snprintf(path, PATH_MAX, "%s/%s", sysfs, PCI_ROOT_BUS_DEVICES_PATH); + dir = opendir(path); + if (!dir) + return; + + /* Scan the PCI root bus to find the match root port on @target_bus */ + while ((dent = readdir(dir))) { + ret = sscanf(dent->d_name, PCI_DEVICE_NAME_PATTERN, + &domain, &bus, &dev, &fn); + if (ret != 4 || bus != target_bus) + continue; + + rp = zalloc(sizeof(*rp)); + if (!rp) + continue; + + rp->selected = false; + rp->sicl_id = sicl_id; + rp->core_id = core_id; + rp->domain = domain; + rp->bus = bus; + rp->dev = dev; + rp->fn = fn; + + hisi_pcie_init_root_port_mask(rp); + + list_add(&rp->list, &hisi_pcie_root_ports_list); + hisi_pcie_root_ports_num++; + + pr_debug3("Found root port %s\n", dent->d_name); + } + + closedir(dir); +} + +/* Scan the PMUs and build the mapping of the Root Ports to the PMU */ +static int hisi_pcie_root_ports_init(void) +{ + char event_source[PATH_MAX], bus_path[PATH_MAX]; + unsigned long long bus; + u16 sicl_id, core_id; + struct dirent *dent; + DIR *dir; + + perf_pmu__event_source_devices_scnprintf(event_source, sizeof(event_source)); + dir = opendir(event_source); + if (!dir) + return -ENOENT; + + while ((dent = readdir(dir))) { + /* + * This HiSilicon PCIe PMU will be named as: + * hisi_pcie_core + */ + if ((sscanf(dent->d_name, "hisi_pcie%hu_core%hu", &sicl_id, &core_id)) != 2) + continue; + + /* + * Driver will export the root port it can monitor through + * the "bus" sysfs attribute. + */ + scnprintf(bus_path, sizeof(bus_path), "%s/hisi_pcie%hu_core%hu/bus", + event_source, sicl_id, core_id); + + /* + * Per PCIe spec the bus should be 8bit, use unsigned long long + * for the convience of the library function. + */ + if (filename__read_ull(bus_path, &bus)) + continue; + + pr_debug3("Found pmu %s bus 0x%llx\n", dent->d_name, bus); + + hisi_pcie_root_ports_add(sicl_id, core_id, (u8)bus); + } + + closedir(dir); + return hisi_pcie_root_ports_num > 0 ? 0 : -ENOENT; +} + +static void hisi_pcie_root_ports_free(void) +{ + struct hisi_pcie_root_port *rp, *tmp; + + if (hisi_pcie_root_ports_num == 0) + return; + + list_for_each_entry_safe(rp, tmp, &hisi_pcie_root_ports_list, list) { + list_del(&rp->list); + zfree(&rp); + hisi_pcie_root_ports_num--; + } +} + +static int hisi_iostat_add_events(struct evlist *evl) +{ + struct hisi_pcie_root_port *rp; + struct evsel *evsel; + unsigned int i, j; + char *iostat_cmd; + int pos = 0; + int ret; + + if (!hisi_pcie_root_ports_num) + return -ENOENT; + + iostat_cmd = zalloc(PATH_MAX); + if (!iostat_cmd) + return -ENOMEM; + + list_for_each_entry(rp, &hisi_pcie_root_ports_list, list) { + if (!rp->selected) + continue; + + iostat_cmd[pos++] = '{'; + for (j = 0; j < ARRAY_SIZE(hisi_iostat_cmd_template); j++) { + pos += snprintf(iostat_cmd + pos, ARG_MAX - pos - 1, + hisi_iostat_cmd_template[j], + rp->sicl_id, rp->core_id, rp->mask); + + if (j == ARRAY_SIZE(hisi_iostat_cmd_template) - 1) + iostat_cmd[pos++] = '}'; + else + iostat_cmd[pos++] = ','; + } + + ret = parse_event(evl, iostat_cmd); + if (ret) + break; + + i = 0; + evlist__for_each_entry_reverse(evl, evsel) { + if (i == ARRAY_SIZE(hisi_iostat_cmd_template)) + break; + + evsel->priv = rp; + i++; + } + + memset(iostat_cmd, 0, PATH_MAX); + pos = 0; + } + + zfree(&iostat_cmd); + return ret; +} + +int iostat_prepare(struct evlist *evlist, + struct perf_stat_config *config) +{ + if (evlist->core.nr_entries > 0) { + pr_warning("The -e and -M options are not supported." + "All chosen events/metrics will be dropped\n"); + evlist__delete(evlist); + evlist = evlist__new(); + if (!evlist) + return -ENOMEM; + } + + config->metric_only = true; + config->aggr_mode = AGGR_GLOBAL; + + return hisi_iostat_add_events(evlist); +} + +static int hisi_pcie_root_ports_list_filter(const char *str) +{ + char *tok, *tmp, *copy = NULL; + u8 bus, dev, fn; + u32 domain; + int ret; + + copy = strdup(str); + if (!copy) + return -ENOMEM; + + for (tok = strtok_r(copy, ",", &tmp); tok; tok = strtok_r(NULL, ",", &tmp)) { + ret = sscanf(tok, PCI_DEVICE_NAME_PATTERN, &domain, &bus, &dev, &fn); + if (ret != 4) { + ret = -EINVAL; + break; + } + + ret = hisi_pcie_root_ports_select_one(domain, bus, dev, fn); + if (ret) + break; + } + + zfree(©); + return ret; +} + +int iostat_parse(const struct option *opt, const char *str, int unset __maybe_unused) +{ + struct perf_stat_config *config = (struct perf_stat_config *)opt->data; + int ret; + + ret = hisi_pcie_root_ports_init(); + if (!ret) { + config->iostat_run = true; + + if (!str) { + iostat_mode = IOSTAT_RUN; + hisi_pcie_root_ports_select_all(); + } else if (!strcmp(str, "list")) { + iostat_mode = IOSTAT_LIST; + hisi_pcie_root_ports_select_all(); + } else { + iostat_mode = IOSTAT_RUN; + ret = hisi_pcie_root_ports_list_filter(str); + } + } + + return ret; +} + +static void hisi_pcie_root_port_show(FILE *output, + const struct hisi_pcie_root_port * const rp) +{ + if (output && rp) + fprintf(output, "hisi_pcie%hu_core%hu<" PCI_DEVICE_NAME_PATTERN ">\n", + rp->sicl_id, rp->core_id, rp->domain, rp->bus, rp->dev, rp->fn); +} + +void iostat_list(struct evlist *evlist __maybe_unused, struct perf_stat_config *config) +{ + struct hisi_pcie_root_port *rp = NULL; + struct evsel *evsel; + + evlist__for_each_entry(evlist, evsel) { + if (rp != evsel->priv) { + hisi_pcie_root_port_show(config->output, evsel->priv); + rp = evsel->priv; + } + } +} + +void iostat_release(struct evlist *evlist) +{ + struct evsel *evsel; + + evlist__for_each_entry(evlist, evsel) + evsel->priv = NULL; + + hisi_pcie_root_ports_free(); +} + +void iostat_print_header_prefix(struct perf_stat_config *config) +{ + if (config->csv_output) + fputs("port,", config->output); + else if (config->interval) + fprintf(config->output, "# time port "); + else + fprintf(config->output, " port "); +} + +void iostat_print_metric(struct perf_stat_config *config, struct evsel *evsel, + struct perf_stat_output_ctx *out) +{ + const char *iostat_metric = hisi_iostat_metrics[evsel->core.idx % ARRAY_SIZE(hisi_iostat_metrics)]; + struct perf_counts_values *count; + double iostat_value; + + /* We're using AGGR_GLOBAL so there's only one aggr counts aggr[0]. */ + count = &evsel->stats->aggr[0].counts; + + /* The counts has been scaled, we can use it directly. */ + iostat_value = (double)count->val; + + /* + * Display two digits after decimal point for better accuracy if the + * value is non-zero. + */ + out->print_metric(config, out->ctx, NULL, + iostat_value > 0 ? "%8.2f" : "%8.0f", + iostat_metric, iostat_value / (256 * 1024)); +} + +void iostat_prefix(struct evlist *evlist, struct perf_stat_config *config, + char *prefix, struct timespec *ts) +{ + struct hisi_pcie_root_port *rp = evlist->selected->priv; + + if (rp) { + if (ts) + sprintf(prefix, "%6lu.%09lu%s" PCI_DEVICE_NAME_PATTERN "%s", + ts->tv_sec, ts->tv_nsec, config->csv_sep, + rp->domain, rp->bus, rp->dev, rp->fn, + config->csv_sep); + else + sprintf(prefix, PCI_DEVICE_NAME_PATTERN "%s", + rp->domain, rp->bus, rp->dev, rp->fn, + config->csv_sep); + } +} + +void iostat_print_counters(struct evlist *evlist, struct perf_stat_config *config, + struct timespec *ts, char *prefix, + iostat_print_counter_t print_cnt_cb, void *arg) +{ + struct evsel *counter = evlist__first(evlist); + void *perf_device; + + evlist__set_selected(evlist, counter); + iostat_prefix(evlist, config, prefix, ts); + fprintf(config->output, "%s", prefix); + evlist__for_each_entry(evlist, counter) { + perf_device = evlist->selected->priv; + if (perf_device && perf_device != counter->priv) { + evlist__set_selected(evlist, counter); + iostat_prefix(evlist, config, prefix, ts); + fprintf(config->output, "\n%s", prefix); + } + print_cnt_cb(config, counter, arg); + } + fputc('\n', config->output); +} From 9b8933b8700f15ce642137894969b40194897537 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 2 Aug 2024 14:58:00 +0800 Subject: [PATCH 205/227] perf stat: Display iostat headers correctly stable inclusion from stable-v6.6.54 commit fa0720b32afa89496fc0cf5c96705a908311cf40 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAZ3K2 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=fa0720b32afa89496fc0cf5c96705a908311cf40 -------------------------------- [ Upstream commit 2615639352420e6e3115952c5b8f46846e1c6d0e ] Currently we'll only print metric headers for metric leader in aggregration mode. This will make `perf iostat` header not shown since it'll aggregrated globally but don't have metric events: root@ubuntu204:/home/yang/linux/tools/perf# ./perf stat --iostat --timeout 1000 Performance counter stats for 'system wide': port 0000:00 0 0 0 0 0000:80 0 0 0 0 [...] Fix this by excluding the iostat in the check of printing metric headers. Then we can see the headers: root@ubuntu204:/home/yang/linux/tools/perf# ./perf stat --iostat --timeout 1000 Performance counter stats for 'system wide': port Inbound Read(MB) Inbound Write(MB) Outbound Read(MB) Outbound Write(MB) 0000:00 0 0 0 0 0000:80 0 0 0 0 [...] Fixes: 193a9e30207f5477 ("perf stat: Don't display metric header for non-leader uncore events") Signed-off-by: Yicong Yang Acked-by: Namhyung Kim Cc: Ian Rogers Cc: Ingo Molnar Cc: Jonathan Cameron Cc: Junhao He Cc: linuxarm@huawei.com Cc: Mark Rutland Cc: Peter Zijlstra Cc: Shameerali Kolothum Thodi Cc: Zeng Tao Link: https://lore.kernel.org/r/20240802065800.48774-1-yangyicong@huawei.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Sasha Levin (cherry picked from commit fa0720b32afa89496fc0cf5c96705a908311cf40) Signed-off-by: Wentao Guan Signed-off-by: Slim6882 <15605196882@163.com> --- tools/perf/util/stat-display.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index 24e50fabb6c33..05a8dc606f1a1 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -1000,6 +1000,10 @@ static void print_metric_headers(struct perf_stat_config *config, /* Print metrics headers only */ evlist__for_each_entry(evlist, counter) { + if (!config->iostat_run && + config->aggr_mode != AGGR_NONE && counter->metric_leader != counter) + continue; + os.evsel = counter; out.ctx = &os; out.print_metric = print_metric_header; From a2c6fe8732109f58395c7ac71a2f356e11d0243f Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 16 Jun 2025 18:26:44 +0800 Subject: [PATCH 206/227] perf iostat: hisi: Fix port range retrival driver inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/ICFG5B ---------------------------------------------------------------------- Root ports monitored by different the PCIe PMUs may locates on the same root bus. It's not correct to match all the Root Ports on the same bus for one PCIe PMU. Fix this. Fixes: 26b19f9990c0 ("perf stat: Enable iostat mode for HiSilicon PCIe PMU") Signed-off-by: Yicong Yang Signed-off-by: Yushan Wang Signed-off-by: Qizhi Zhang Signed-off-by: Slim6882 <15605196882@163.com> --- tools/perf/arch/arm64/util/hisi-iostat.c | 32 +++++++++++++++++++----- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/tools/perf/arch/arm64/util/hisi-iostat.c b/tools/perf/arch/arm64/util/hisi-iostat.c index 96684cc004648..fc794ee7d8d46 100644 --- a/tools/perf/arch/arm64/util/hisi-iostat.c +++ b/tools/perf/arch/arm64/util/hisi-iostat.c @@ -22,6 +22,10 @@ #include "util/iostat.h" #include "util/pmu.h" +/* From include/uapi/linux/pci.h */ +#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) +#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07)) + #define PCI_DEFAULT_DOMAIN 0 #define PCI_DEVICE_NAME_PATTERN "%04x:%02hhx:%02hhx.%hhu" #define PCI_ROOT_BUS_DEVICES_PATH "bus/pci/devices" @@ -69,9 +73,9 @@ struct hisi_pcie_root_port { LIST_HEAD(hisi_pcie_root_ports_list); static int hisi_pcie_root_ports_num; -static void hisi_pcie_init_root_port_mask(struct hisi_pcie_root_port *rp) +static void hisi_pcie_init_root_port_mask(struct hisi_pcie_root_port *rp, u16 devbase) { - rp->mask = BIT(rp->dev << 1); + rp->mask = BIT((rp->dev - devbase) << 1); } /* @@ -100,7 +104,8 @@ static void hisi_pcie_root_ports_select_all(void) rp->selected = true; } -static void hisi_pcie_root_ports_add(u16 sicl_id, u16 core_id, u8 target_bus) +static void hisi_pcie_root_ports_add(u16 sicl_id, u16 core_id, u8 target_bus, + u16 bdf_min, u16 bdf_max) { const char *sysfs = sysfs__mountpoint(); struct hisi_pcie_root_port *rp; @@ -109,6 +114,7 @@ static void hisi_pcie_root_ports_add(u16 sicl_id, u16 core_id, u8 target_bus) u8 bus, dev, fn; u32 domain; DIR *dir; + u16 bdf; int ret; snprintf(path, PATH_MAX, "%s/%s", sysfs, PCI_ROOT_BUS_DEVICES_PATH); @@ -123,6 +129,10 @@ static void hisi_pcie_root_ports_add(u16 sicl_id, u16 core_id, u8 target_bus) if (ret != 4 || bus != target_bus) continue; + bdf = (bus << 8) | PCI_DEVFN(dev, fn); + if (bdf < bdf_min || bdf > bdf_max) + continue; + rp = zalloc(sizeof(*rp)); if (!rp) continue; @@ -135,7 +145,7 @@ static void hisi_pcie_root_ports_add(u16 sicl_id, u16 core_id, u8 target_bus) rp->dev = dev; rp->fn = fn; - hisi_pcie_init_root_port_mask(rp); + hisi_pcie_init_root_port_mask(rp, PCI_SLOT(bdf_min)); list_add(&rp->list, &hisi_pcie_root_ports_list); hisi_pcie_root_ports_num++; @@ -150,7 +160,7 @@ static void hisi_pcie_root_ports_add(u16 sicl_id, u16 core_id, u8 target_bus) static int hisi_pcie_root_ports_init(void) { char event_source[PATH_MAX], bus_path[PATH_MAX]; - unsigned long long bus; + unsigned long long bus, bdf_max, bdf_min; u16 sicl_id, core_id; struct dirent *dent; DIR *dir; @@ -182,9 +192,19 @@ static int hisi_pcie_root_ports_init(void) if (filename__read_ull(bus_path, &bus)) continue; + scnprintf(bus_path, sizeof(bus_path), "%s/hisi_pcie%hu_core%hu/bdf_max", + event_source, sicl_id, core_id); + if (filename__read_xll(bus_path, &bdf_max)) + bdf_max = -1; + + scnprintf(bus_path, sizeof(bus_path), "%s/hisi_pcie%hu_core%hu/bdf_min", + event_source, sicl_id, core_id); + if (filename__read_xll(bus_path, &bdf_min)) + bdf_min = 0; + pr_debug3("Found pmu %s bus 0x%llx\n", dent->d_name, bus); - hisi_pcie_root_ports_add(sicl_id, core_id, (u8)bus); + hisi_pcie_root_ports_add(sicl_id, core_id, (u8)bus, (u16)bdf_min, (u16)bdf_max); } closedir(dir); From f16335945fc8a6377ccb8a1d674c38011fcbc2a6 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 6 Jun 2024 23:53:43 -0700 Subject: [PATCH 207/227] perf arm: Workaround ARM PMUs cpu maps having offline cpus mainline inclusion from mainline-v6.11-rc1 commit 5518063fcb2e022411c1112b1b9b069e68380bbb category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IAQG1H CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=5518063fcb2e022411c1112b1b9b069e68380bbb ---------------------------------------------------------------------- When PMUs have a cpu map in the 'cpus' or 'cpumask' file, perf will try to open events on those CPUs. ARM doesn't remove offline CPUs meaning taking a CPU offline will cause perf commands to fail unless a CPU map is passed on the command line. More context in: https://lore.kernel.org/lkml/20240603092812.46616-1-yangyicong@huawei.com/ Reported-by: Yicong Yang Closes: https://lore.kernel.org/lkml/20240603092812.46616-2-yangyicong@huawei.com/ Signed-off-by: Ian Rogers Tested-by: Yicong Yang Tested-by: Leo Yan Cc: James Clark Cc: Suzuki K Poulose Cc: Will Deacon Cc: Mike Leach Cc: Leo Yan Cc: linux-arm-kernel@lists.infradead.org Cc: coresight@lists.linaro.org Cc: John Garry Signed-off-by: Namhyung Kim Signed-off-by: Qizhi Zhang Link: https://lore.kernel.org/r/20240607065343.695369-1-irogers@google.com Signed-off-by: Slim6882 <15605196882@163.com> --- tools/perf/arch/arm/util/pmu.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c index 887c8addc4916..e10ac550e4e4e 100644 --- a/tools/perf/arch/arm/util/pmu.c +++ b/tools/perf/arch/arm/util/pmu.c @@ -11,11 +11,14 @@ #include "arm-spe.h" #include "hisi-ptt.h" +#include "../../../util/cpumap.h" #include "../../../util/pmu.h" struct perf_event_attr -*perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) +*perf_pmu__get_default_config(struct perf_pmu *pmu) { + struct perf_cpu_map *intersect; + #ifdef HAVE_AUXTRACE_SUPPORT if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) { /* add ETM default config here */ @@ -29,5 +32,10 @@ struct perf_event_attr } #endif + /* Workaround some ARM PMU's failing to correctly set CPU maps for online processors. */ + intersect = perf_cpu_map__intersect(cpu_map__online(), pmu->cpus); + perf_cpu_map__put(pmu->cpus); + pmu->cpus = intersect; + return NULL; } From 9f90196b39ba1fc0fb7c8a0dbec0472377dabcc1 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 19 Sep 2024 14:25:06 +0800 Subject: [PATCH 208/227] drivers: perf: arm_pmuv3: Read PMMIR_EL1 unconditionally mainline inclusion from mainline-v6.7-rc1 commit 58f8fc57b1d314b5402c374a8c454ac7c870574c category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I9Q7QP CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=58f8fc57b1d314b5402c374a8c454ac7c870574c -------------------------------- Currently the PMUv3 driver only reads PMMIR_EL1 if the PMU implements FEAT_PMUv3p4 and the STALL_SLOT event, but the check for STALL_SLOT event isn't necessary and can be removed. The check for STALL_SLOT event was introduced with the read of PMMIR_EL1 in commit f5be3a61fdb5dd11 ("arm64: perf: Add support caps under sysfs") When this logic was written, the ARM ARM said: | If STALL_SLOT is not implemented, it is IMPLEMENTATION DEFINED whether | the PMMIR System registers are implemented. ... and thus the driver had to check for STALL_SLOT event to verify that PMMIR_EL1 was implemented and accesses to PMMIR_EL1 would not be UNDEFINED. Subsequently, the architecture was retrospectively tightened to require that any FEAT_PMUv3p4 implementation implements PMMIR_EL1. Since the G.b release of the ARM ARM, the wording regarding STALL_SLOT event has been removed, and the description of PMMIR_EL1 says: | This register is present only when FEAT_PMUv3p4 is implemented. Drop the unnecessary check for STALL_SLOT event when reading PMMIR_EL1. Cc: Will Deacon Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Reviewed-by: James Clark Signed-off-by: Anshuman Khandual Link: https://lore.kernel.org/r/20231013024354.1289070-1-anshuman.khandual@arm.com Signed-off-by: Will Deacon Signed-off-by: Junhao He Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/arm_pmuv3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 504c218725fb3..e3cdb5431a888 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -1122,7 +1122,7 @@ static void __armv8pmu_probe_pmu(void *info) pmceid, ARMV8_PMUV3_MAX_COMMON_EVENTS); /* store PMMIR register for sysfs */ - if (is_pmuv3p4(pmuver) && (pmceid_raw[1] & BIT(31))) + if (is_pmuv3p4(pmuver)) cpu_pmu->reg_pmmir = read_pmmir(); else cpu_pmu->reg_pmmir = 0; From 5a4665e7ca3be2e2d07329a58ad78e398f29b6ee Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Tue, 14 Dec 2021 14:16:14 +0000 Subject: [PATCH 209/227] arm64: perf: Simplify registration boilerplate With the trend for per-core events moving to userspace JSON, registering names for PMUv3 implementations is increasingly a pure boilerplate exercise. Let's wrap things a step further so we can generate the basic PMUv3 init function with a macro invocation, and reduce further new addition to just 2 lines each. Suggested-by: Mark Rutland Signed-off-by: Robin Murphy Link: https://lore.kernel.org/r/b79477ea3b97f685d00511d4ecd2f686184dca34.1639490264.git.robin.murphy@arm.com Signed-off-by: Will Deacon Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/arm_pmuv3.c | 97 ++++++++++++---------------------------- 1 file changed, 29 insertions(+), 68 deletions(-) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index e3cdb5431a888..0a99ee636b5c3 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -1225,17 +1225,26 @@ static int armv8_pmu_init_nogroups(struct arm_pmu *cpu_pmu, char *name, return armv8_pmu_init(cpu_pmu, name, map_event, NULL, NULL, NULL); } -static int armv8_pmuv3_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_pmuv3", - armv8_pmuv3_map_event); +#define PMUV3_INIT_SIMPLE(name) \ +static int name##_pmu_init(struct arm_pmu *cpu_pmu) \ +{ \ + return armv8_pmu_init_nogroups(cpu_pmu, #name, armv8_pmuv3_map_event);\ } -static int armv8_a34_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a34", - armv8_pmuv3_map_event); -} +PMUV3_INIT_SIMPLE(armv8_pmuv3) + +PMUV3_INIT_SIMPLE(armv8_cortex_a34) +PMUV3_INIT_SIMPLE(armv8_cortex_a55) +PMUV3_INIT_SIMPLE(armv8_cortex_a65) +PMUV3_INIT_SIMPLE(armv8_cortex_a75) +PMUV3_INIT_SIMPLE(armv8_cortex_a76) +PMUV3_INIT_SIMPLE(armv8_cortex_a77) +PMUV3_INIT_SIMPLE(armv8_cortex_a78) +PMUV3_INIT_SIMPLE(armv8_neoverse_e1) +PMUV3_INIT_SIMPLE(armv8_neoverse_n1) + +PMUV3_INIT_SIMPLE(armv8_nvidia_carmel) +PMUV3_INIT_SIMPLE(armv8_nvidia_denver) static int armv8_a35_pmu_init(struct arm_pmu *cpu_pmu) { @@ -1249,24 +1258,12 @@ static int armv8_a53_pmu_init(struct arm_pmu *cpu_pmu) armv8_a53_map_event); } -static int armv8_a55_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a55", - armv8_pmuv3_map_event); -} - static int armv8_a57_pmu_init(struct arm_pmu *cpu_pmu) { return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a57", armv8_a57_map_event); } -static int armv8_a65_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a65", - armv8_pmuv3_map_event); -} - static int armv8_a72_pmu_init(struct arm_pmu *cpu_pmu) { return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a72", @@ -1279,42 +1276,6 @@ static int armv8_a73_pmu_init(struct arm_pmu *cpu_pmu) armv8_a73_map_event); } -static int armv8_a75_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a75", - armv8_pmuv3_map_event); -} - -static int armv8_a76_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a76", - armv8_pmuv3_map_event); -} - -static int armv8_a77_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a77", - armv8_pmuv3_map_event); -} - -static int armv8_a78_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a78", - armv8_pmuv3_map_event); -} - -static int armv8_e1_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_neoverse_e1", - armv8_pmuv3_map_event); -} - -static int armv8_n1_pmu_init(struct arm_pmu *cpu_pmu) -{ - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_neoverse_n1", - armv8_pmuv3_map_event); -} - static int armv8_thunder_pmu_init(struct arm_pmu *cpu_pmu) { return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cavium_thunder", @@ -1328,21 +1289,21 @@ static int armv8_vulcan_pmu_init(struct arm_pmu *cpu_pmu) } static const struct of_device_id armv8_pmu_of_device_ids[] = { - {.compatible = "arm,armv8-pmuv3", .data = armv8_pmuv3_init}, - {.compatible = "arm,cortex-a34-pmu", .data = armv8_a34_pmu_init}, + {.compatible = "arm,armv8-pmuv3", .data = armv8_pmuv3_pmu_init}, + {.compatible = "arm,cortex-a34-pmu", .data = armv8_cortex_a34_pmu_init}, {.compatible = "arm,cortex-a35-pmu", .data = armv8_a35_pmu_init}, {.compatible = "arm,cortex-a53-pmu", .data = armv8_a53_pmu_init}, - {.compatible = "arm,cortex-a55-pmu", .data = armv8_a55_pmu_init}, + {.compatible = "arm,cortex-a55-pmu", .data = armv8_cortex_a55_pmu_init}, {.compatible = "arm,cortex-a57-pmu", .data = armv8_a57_pmu_init}, - {.compatible = "arm,cortex-a65-pmu", .data = armv8_a65_pmu_init}, + {.compatible = "arm,cortex-a65-pmu", .data = armv8_cortex_a65_pmu_init}, {.compatible = "arm,cortex-a72-pmu", .data = armv8_a72_pmu_init}, {.compatible = "arm,cortex-a73-pmu", .data = armv8_a73_pmu_init}, - {.compatible = "arm,cortex-a75-pmu", .data = armv8_a75_pmu_init}, - {.compatible = "arm,cortex-a76-pmu", .data = armv8_a76_pmu_init}, - {.compatible = "arm,cortex-a77-pmu", .data = armv8_a77_pmu_init}, - {.compatible = "arm,cortex-a78-pmu", .data = armv8_a78_pmu_init}, - {.compatible = "arm,neoverse-e1-pmu", .data = armv8_e1_pmu_init}, - {.compatible = "arm,neoverse-n1-pmu", .data = armv8_n1_pmu_init}, + {.compatible = "arm,cortex-a75-pmu", .data = armv8_cortex_a75_pmu_init}, + {.compatible = "arm,cortex-a76-pmu", .data = armv8_cortex_a76_pmu_init}, + {.compatible = "arm,cortex-a77-pmu", .data = armv8_cortex_a77_pmu_init}, + {.compatible = "arm,cortex-a78-pmu", .data = armv8_cortex_a78_pmu_init}, + {.compatible = "arm,neoverse-e1-pmu", .data = armv8_neoverse_e1_pmu_init}, + {.compatible = "arm,neoverse-n1-pmu", .data = armv8_neoverse_n1_pmu_init}, {.compatible = "cavium,thunder-pmu", .data = armv8_thunder_pmu_init}, {.compatible = "brcm,vulcan-pmu", .data = armv8_vulcan_pmu_init}, {}, @@ -1367,7 +1328,7 @@ static int __init armv8_pmu_driver_init(void) if (acpi_disabled) return platform_driver_register(&armv8_pmu_driver); else - return arm_pmu_acpi_probe(armv8_pmuv3_init); + return arm_pmu_acpi_probe(armv8_pmuv3_pmu_init); } device_initcall(armv8_pmu_driver_init) From 44e940fc7b35927d7dae4da4fd759f8a254cdafa Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Thu, 19 Sep 2024 14:25:07 +0800 Subject: [PATCH 210/227] drivers: perf: arm_pmuv3: Drop some unused arguments from armv8_pmu_init() mainline inclusion from mainline-v6.7-rc1 commit 3b9a22d345ff89232227b2449a311bf3f910f5f2 category: cleanup bugzilla: https://gitee.com/openeuler/kernel/issues/I9Q7QP CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=3b9a22d345ff89232227b2449a311bf3f910f5f2 -------------------------------- All the PMU init functions want the default sysfs attribute groups, and so these all call armv8_pmu_init_nogroups() helper, with none of them calling armv8_pmu_init() directly. When we introduced armv8_pmu_init_nogroups() in the commit e424b1798526 ("arm64: perf: Refactor PMU init callbacks") ... we thought that we might need custom attribute groups in future, but as we evidently haven't, we can remove the option. This patch folds armv8_pmu_init_nogroups() into armv8_pmu_init(), removing the ability to use custom attribute groups and simplifying the code. CC: James Clark Cc: Robin Murphy Cc: Will Deacon Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Mark Rutland Signed-off-by: Anshuman Khandual Link: https://lore.kernel.org/r/20231016025436.1368945-1-anshuman.khandual@arm.com Signed-off-by: Will Deacon Signed-off-by: Junhao He Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/arm_pmuv3.c | 44 +++++++++++----------------------------- 1 file changed, 12 insertions(+), 32 deletions(-) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 0a99ee636b5c3..ece31a78610e9 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -1183,10 +1183,7 @@ static void armv8_pmu_register_sysctl_table(void) } static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name, - int (*map_event)(struct perf_event *event), - const struct attribute_group *events, - const struct attribute_group *format, - const struct attribute_group *caps) + int (*map_event)(struct perf_event *event)) { int ret = armv8pmu_probe_pmu(cpu_pmu); if (ret) @@ -1208,27 +1205,17 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name, cpu_pmu->name = name; cpu_pmu->map_event = map_event; - cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_EVENTS] = events ? - events : &armv8_pmuv3_events_attr_group; - cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = format ? - format : &armv8_pmuv3_format_attr_group; - cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_CAPS] = caps ? - caps : &armv8_pmuv3_caps_attr_group; - + cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_EVENTS] = &armv8_pmuv3_events_attr_group; + cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_FORMATS] = &armv8_pmuv3_format_attr_group; + cpu_pmu->attr_groups[ARMPMU_ATTR_GROUP_CAPS] = &armv8_pmuv3_caps_attr_group; armv8_pmu_register_sysctl_table(); return 0; } -static int armv8_pmu_init_nogroups(struct arm_pmu *cpu_pmu, char *name, - int (*map_event)(struct perf_event *event)) -{ - return armv8_pmu_init(cpu_pmu, name, map_event, NULL, NULL, NULL); -} - #define PMUV3_INIT_SIMPLE(name) \ static int name##_pmu_init(struct arm_pmu *cpu_pmu) \ { \ - return armv8_pmu_init_nogroups(cpu_pmu, #name, armv8_pmuv3_map_event);\ + return armv8_pmu_init(cpu_pmu, #name, armv8_pmuv3_map_event); \ } PMUV3_INIT_SIMPLE(armv8_pmuv3) @@ -1248,44 +1235,37 @@ PMUV3_INIT_SIMPLE(armv8_nvidia_denver) static int armv8_a35_pmu_init(struct arm_pmu *cpu_pmu) { - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a35", - armv8_a53_map_event); + return armv8_pmu_init(cpu_pmu, "armv8_cortex_a35", armv8_a53_map_event); } static int armv8_a53_pmu_init(struct arm_pmu *cpu_pmu) { - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a53", - armv8_a53_map_event); + return armv8_pmu_init(cpu_pmu, "armv8_cortex_a53", armv8_a53_map_event); } static int armv8_a57_pmu_init(struct arm_pmu *cpu_pmu) { - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a57", - armv8_a57_map_event); + return armv8_pmu_init(cpu_pmu, "armv8_cortex_a57", armv8_a57_map_event); } static int armv8_a72_pmu_init(struct arm_pmu *cpu_pmu) { - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a72", - armv8_a57_map_event); + return armv8_pmu_init(cpu_pmu, "armv8_cortex_a72", armv8_a57_map_event); } static int armv8_a73_pmu_init(struct arm_pmu *cpu_pmu) { - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cortex_a73", - armv8_a73_map_event); + return armv8_pmu_init(cpu_pmu, "armv8_cortex_a73", armv8_a73_map_event); } static int armv8_thunder_pmu_init(struct arm_pmu *cpu_pmu) { - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_cavium_thunder", - armv8_thunder_map_event); + return armv8_pmu_init(cpu_pmu, "armv8_cavium_thunder", armv8_thunder_map_event); } static int armv8_vulcan_pmu_init(struct arm_pmu *cpu_pmu) { - return armv8_pmu_init_nogroups(cpu_pmu, "armv8_brcm_vulcan", - armv8_vulcan_map_event); + return armv8_pmu_init(cpu_pmu, "armv8_brcm_vulcan", armv8_vulcan_map_event); } static const struct of_device_id armv8_pmu_of_device_ids[] = { From 84d9a5523b79327f803225bcc58f9840bfe74c18 Mon Sep 17 00:00:00 2001 From: James Clark Date: Thu, 19 Sep 2024 14:25:12 +0800 Subject: [PATCH 211/227] arm: perf: Remove inlines from arm_pmuv3.c mainline inclusion from mainline-v6.8-rc1 commit 9343c790e6de7edd2bab17572832f4f21c748dc2 category: cleanup bugzilla: https://gitee.com/openeuler/kernel/issues/I9Q7QP CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=9343c790e6de7edd2bab17572832f4f21c748dc2 -------------------------------- These are all static and in one compilation unit so the inline has no effect on the binary. Except if FTRACE is enabled, then 3 functions which were already not inlined now get the nops added which allows them to be traced. Signed-off-by: James Clark Link: https://lore.kernel.org/r/20231211161331.1277825-2-james.clark@arm.com Signed-off-by: Will Deacon Signed-off-by: Junhao He Signed-off-by: Slim6882 <15605196882@163.com> --- drivers/perf/arm_pmuv3.c | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index ece31a78610e9..317282b063c64 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -295,12 +295,12 @@ PMU_FORMAT_ATTR(rdpmc, "config1:1"); static int sysctl_perf_user_access __read_mostly; -static inline bool armv8pmu_event_is_64bit(struct perf_event *event) +static bool armv8pmu_event_is_64bit(struct perf_event *event) { return event->attr.config1 & 0x1; } -static inline bool armv8pmu_event_want_user_access(struct perf_event *event) +static bool armv8pmu_event_want_user_access(struct perf_event *event) { return event->attr.config1 & 0x2; } @@ -392,7 +392,7 @@ static bool armv8pmu_has_long_event(struct arm_pmu *cpu_pmu) return (IS_ENABLED(CONFIG_ARM64) && is_pmuv3p5(cpu_pmu->pmuver)); } -static inline bool armv8pmu_event_has_user_read(struct perf_event *event) +static bool armv8pmu_event_has_user_read(struct perf_event *event) { return event->hw.flags & PERF_EVENT_FLAG_USER_READ_CNT; } @@ -402,7 +402,7 @@ static inline bool armv8pmu_event_has_user_read(struct perf_event *event) * except when we have allocated the 64bit cycle counter (for CPU * cycles event) or when user space counter access is enabled. */ -static inline bool armv8pmu_event_is_chained(struct perf_event *event) +static bool armv8pmu_event_is_chained(struct perf_event *event) { int idx = event->hw.idx; struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); @@ -423,36 +423,36 @@ static inline bool armv8pmu_event_is_chained(struct perf_event *event) #define ARMV8_IDX_TO_COUNTER(x) \ (((x) - ARMV8_IDX_COUNTER0) & ARMV8_PMU_COUNTER_MASK) -static inline u64 armv8pmu_pmcr_read(void) +static u64 armv8pmu_pmcr_read(void) { return read_pmcr(); } -static inline void armv8pmu_pmcr_write(u64 val) +static void armv8pmu_pmcr_write(u64 val) { val &= ARMV8_PMU_PMCR_MASK; isb(); write_pmcr(val); } -static inline int armv8pmu_has_overflowed(u32 pmovsr) +static int armv8pmu_has_overflowed(u32 pmovsr) { return pmovsr & ARMV8_PMU_OVERFLOWED_MASK; } -static inline int armv8pmu_counter_has_overflowed(u32 pmnc, int idx) +static int armv8pmu_counter_has_overflowed(u32 pmnc, int idx) { return pmnc & BIT(ARMV8_IDX_TO_COUNTER(idx)); } -static inline u64 armv8pmu_read_evcntr(int idx) +static u64 armv8pmu_read_evcntr(int idx) { u32 counter = ARMV8_IDX_TO_COUNTER(idx); return read_pmevcntrn(counter); } -static inline u64 armv8pmu_read_hw_counter(struct perf_event *event) +static u64 armv8pmu_read_hw_counter(struct perf_event *event) { int idx = event->hw.idx; u64 val = armv8pmu_read_evcntr(idx); @@ -514,14 +514,14 @@ static u64 armv8pmu_read_counter(struct perf_event *event) return armv8pmu_unbias_long_counter(event, value); } -static inline void armv8pmu_write_evcntr(int idx, u64 value) +static void armv8pmu_write_evcntr(int idx, u64 value) { u32 counter = ARMV8_IDX_TO_COUNTER(idx); write_pmevcntrn(counter, value); } -static inline void armv8pmu_write_hw_counter(struct perf_event *event, +static void armv8pmu_write_hw_counter(struct perf_event *event, u64 value) { int idx = event->hw.idx; @@ -547,7 +547,7 @@ static void armv8pmu_write_counter(struct perf_event *event, u64 value) armv8pmu_write_hw_counter(event, value); } -static inline void armv8pmu_write_evtype(int idx, u32 val) +static void armv8pmu_write_evtype(int idx, u32 val) { u32 counter = ARMV8_IDX_TO_COUNTER(idx); @@ -555,7 +555,7 @@ static inline void armv8pmu_write_evtype(int idx, u32 val) write_pmevtypern(counter, val); } -static inline void armv8pmu_write_event_type(struct perf_event *event) +static void armv8pmu_write_event_type(struct perf_event *event) { struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; @@ -589,7 +589,7 @@ static u32 armv8pmu_event_cnten_mask(struct perf_event *event) return mask; } -static inline void armv8pmu_enable_counter(u32 mask) +static void armv8pmu_enable_counter(u32 mask) { /* * Make sure event configuration register writes are visible before we @@ -599,7 +599,7 @@ static inline void armv8pmu_enable_counter(u32 mask) write_pmcntenset(mask); } -static inline void armv8pmu_enable_event_counter(struct perf_event *event) +static void armv8pmu_enable_event_counter(struct perf_event *event) { struct perf_event_attr *attr = &event->attr; u32 mask = armv8pmu_event_cnten_mask(event); @@ -611,7 +611,7 @@ static inline void armv8pmu_enable_event_counter(struct perf_event *event) armv8pmu_enable_counter(mask); } -static inline void armv8pmu_disable_counter(u32 mask) +static void armv8pmu_disable_counter(u32 mask) { write_pmcntenclr(mask); /* @@ -621,7 +621,7 @@ static inline void armv8pmu_disable_counter(u32 mask) isb(); } -static inline void armv8pmu_disable_event_counter(struct perf_event *event) +static void armv8pmu_disable_event_counter(struct perf_event *event) { struct perf_event_attr *attr = &event->attr; u32 mask = armv8pmu_event_cnten_mask(event); @@ -633,18 +633,18 @@ static inline void armv8pmu_disable_event_counter(struct perf_event *event) armv8pmu_disable_counter(mask); } -static inline void armv8pmu_enable_intens(u32 mask) +static void armv8pmu_enable_intens(u32 mask) { write_pmintenset(mask); } -static inline void armv8pmu_enable_event_irq(struct perf_event *event) +static void armv8pmu_enable_event_irq(struct perf_event *event) { u32 counter = ARMV8_IDX_TO_COUNTER(event->hw.idx); armv8pmu_enable_intens(BIT(counter)); } -static inline void armv8pmu_disable_intens(u32 mask) +static void armv8pmu_disable_intens(u32 mask) { write_pmintenclr(mask); isb(); @@ -653,13 +653,13 @@ static inline void armv8pmu_disable_intens(u32 mask) isb(); } -static inline void armv8pmu_disable_event_irq(struct perf_event *event) +static void armv8pmu_disable_event_irq(struct perf_event *event) { u32 counter = ARMV8_IDX_TO_COUNTER(event->hw.idx); armv8pmu_disable_intens(BIT(counter)); } -static inline u32 armv8pmu_getreset_flags(void) +static u32 armv8pmu_getreset_flags(void) { u32 value; From bd6be5067fb489fe15ab75a1b8cc541d5ace0951 Mon Sep 17 00:00:00 2001 From: Alexandru Elisei Date: Mon, 25 Apr 2022 15:55:30 +0100 Subject: [PATCH 212/227] KVM/arm64: Don't emulate a PMU for 32-bit guests if feature not set kvm->arch.arm_pmu is set when userspace attempts to set the first PMU attribute. As certain attributes are mandatory, arm_pmu ends up always being set to a valid arm_pmu, otherwise KVM will refuse to run the VCPU. However, this only happens if the VCPU has the PMU feature. If the VCPU doesn't have the feature bit set, kvm->arch.arm_pmu will be left uninitialized and equal to NULL. KVM doesn't do ID register emulation for 32-bit guests and accesses to the PMU registers aren't gated by the pmu_visibility() function. This is done to prevent injecting unexpected undefined exceptions in guests which have detected the presence of a hardware PMU. But even though the VCPU feature is missing, KVM still attempts to emulate certain aspects of the PMU when PMU registers are accessed. This leads to a NULL pointer dereference like this one, which happens on an odroid-c4 board when running the kvm-unit-tests pmu-cycle-counter test with kvmtool and without the PMU feature being set: [ 454.402699] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000150 [ 454.405865] Mem abort info: [ 454.408596] ESR = 0x96000004 [ 454.411638] EC = 0x25: DABT (current EL), IL = 32 bits [ 454.416901] SET = 0, FnV = 0 [ 454.419909] EA = 0, S1PTW = 0 [ 454.423010] FSC = 0x04: level 0 translation fault [ 454.427841] Data abort info: [ 454.430687] ISV = 0, ISS = 0x00000004 [ 454.434484] CM = 0, WnR = 0 [ 454.437404] user pgtable: 4k pages, 48-bit VAs, pgdp=000000000c924000 [ 454.443800] [0000000000000150] pgd=0000000000000000, p4d=0000000000000000 [ 454.450528] Internal error: Oops: 96000004 [#1] PREEMPT SMP [ 454.456036] Modules linked in: [ 454.459053] CPU: 1 PID: 267 Comm: kvm-vcpu-0 Not tainted 5.18.0-rc4 #113 [ 454.465697] Hardware name: Hardkernel ODROID-C4 (DT) [ 454.470612] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 454.477512] pc : kvm_pmu_event_mask.isra.0+0x14/0x74 [ 454.482427] lr : kvm_pmu_set_counter_event_type+0x2c/0x80 [ 454.487775] sp : ffff80000a9839c0 [ 454.491050] x29: ffff80000a9839c0 x28: ffff000000a83a00 x27: 0000000000000000 [ 454.498127] x26: 0000000000000000 x25: 0000000000000000 x24: ffff00000a510000 [ 454.505198] x23: ffff000000a83a00 x22: ffff000003b01000 x21: 0000000000000000 [ 454.512271] x20: 000000000000001f x19: 00000000000003ff x18: 0000000000000000 [ 454.519343] x17: 000000008003fe98 x16: 0000000000000000 x15: 0000000000000000 [ 454.526416] x14: 0000000000000000 x13: 0000000000000000 x12: 0000000000000000 [ 454.533489] x11: 000000008003fdbc x10: 0000000000009d20 x9 : 000000000000001b [ 454.540561] x8 : 0000000000000000 x7 : 0000000000000d00 x6 : 0000000000009d00 [ 454.547633] x5 : 0000000000000037 x4 : 0000000000009d00 x3 : 0d09000000000000 [ 454.554705] x2 : 000000000000001f x1 : 0000000000000000 x0 : 0000000000000000 [ 454.561779] Call trace: [ 454.564191] kvm_pmu_event_mask.isra.0+0x14/0x74 [ 454.568764] kvm_pmu_set_counter_event_type+0x2c/0x80 [ 454.573766] access_pmu_evtyper+0x128/0x170 [ 454.577905] perform_access+0x34/0x80 [ 454.581527] kvm_handle_cp_32+0x13c/0x160 [ 454.585495] kvm_handle_cp15_32+0x1c/0x30 [ 454.589462] handle_exit+0x70/0x180 [ 454.592912] kvm_arch_vcpu_ioctl_run+0x1c4/0x5e0 [ 454.597485] kvm_vcpu_ioctl+0x23c/0x940 [ 454.601280] __arm64_sys_ioctl+0xa8/0xf0 [ 454.605160] invoke_syscall+0x48/0x114 [ 454.608869] el0_svc_common.constprop.0+0xd4/0xfc [ 454.613527] do_el0_svc+0x28/0x90 [ 454.616803] el0_svc+0x34/0xb0 [ 454.619822] el0t_64_sync_handler+0xa4/0x130 [ 454.624049] el0t_64_sync+0x18c/0x190 [ 454.627675] Code: a9be7bfd 910003fd f9000bf3 52807ff3 (b9415001) [ 454.633714] ---[ end trace 0000000000000000 ]--- In this particular case, Linux hasn't detected the presence of a hardware PMU because the PMU node is missing from the DTB, so userspace would have been unable to set the VCPU PMU feature even if it attempted it. What happens is that the 32-bit guest reads ID_DFR0, which advertises the presence of the PMU, and when it tries to program a counter, it triggers the NULL pointer dereference because kvm->arch.arm_pmu is NULL. kvm-arch.arm_pmu was introduced by commit 46b187821472 ("KVM: arm64: Keep a per-VM pointer to the default PMU"). Until that commit, this error would be triggered instead: [ 73.388140] ------------[ cut here ]------------ [ 73.388189] Unknown PMU version 0 [ 73.390420] WARNING: CPU: 1 PID: 264 at arch/arm64/kvm/pmu-emul.c:36 kvm_pmu_event_mask.isra.0+0x6c/0x74 [ 73.399821] Modules linked in: [ 73.402835] CPU: 1 PID: 264 Comm: kvm-vcpu-0 Not tainted 5.17.0 #114 [ 73.409132] Hardware name: Hardkernel ODROID-C4 (DT) [ 73.414048] pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) [ 73.420948] pc : kvm_pmu_event_mask.isra.0+0x6c/0x74 [ 73.425863] lr : kvm_pmu_event_mask.isra.0+0x6c/0x74 [ 73.430779] sp : ffff80000a8db9b0 [ 73.434055] x29: ffff80000a8db9b0 x28: ffff000000dbaac0 x27: 0000000000000000 [ 73.441131] x26: ffff000000dbaac0 x25: 00000000c600000d x24: 0000000000180720 [ 73.448203] x23: ffff800009ffbe10 x22: ffff00000b612000 x21: 0000000000000000 [ 73.455276] x20: 000000000000001f x19: 0000000000000000 x18: ffffffffffffffff [ 73.462348] x17: 000000008003fe98 x16: 0000000000000000 x15: 0720072007200720 [ 73.469420] x14: 0720072007200720 x13: ffff800009d32488 x12: 00000000000004e6 [ 73.476493] x11: 00000000000001a2 x10: ffff800009d32488 x9 : ffff800009d32488 [ 73.483565] x8 : 00000000ffffefff x7 : ffff800009d8a488 x6 : ffff800009d8a488 [ 73.490638] x5 : ffff0000f461a9d8 x4 : 0000000000000000 x3 : 0000000000000001 [ 73.497710] x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff000000dbaac0 [ 73.504784] Call trace: [ 73.507195] kvm_pmu_event_mask.isra.0+0x6c/0x74 [ 73.511768] kvm_pmu_set_counter_event_type+0x2c/0x80 [ 73.516770] access_pmu_evtyper+0x128/0x16c [ 73.520910] perform_access+0x34/0x80 [ 73.524532] kvm_handle_cp_32+0x13c/0x160 [ 73.528500] kvm_handle_cp15_32+0x1c/0x30 [ 73.532467] handle_exit+0x70/0x180 [ 73.535917] kvm_arch_vcpu_ioctl_run+0x20c/0x6e0 [ 73.540489] kvm_vcpu_ioctl+0x2b8/0x9e0 [ 73.544283] __arm64_sys_ioctl+0xa8/0xf0 [ 73.548165] invoke_syscall+0x48/0x114 [ 73.551874] el0_svc_common.constprop.0+0xd4/0xfc [ 73.556531] do_el0_svc+0x28/0x90 [ 73.559808] el0_svc+0x28/0x80 [ 73.562826] el0t_64_sync_handler+0xa4/0x130 [ 73.567054] el0t_64_sync+0x1a0/0x1a4 [ 73.570676] ---[ end trace 0000000000000000 ]--- [ 73.575382] kvm: pmu event creation failed -2 The root cause remains the same: kvm->arch.pmuver was never set to something sensible because the VCPU feature itself was never set. The odroid-c4 is somewhat of a special case, because Linux doesn't probe the PMU. But the above errors can easily be reproduced on any hardware, with or without a PMU driver, as long as userspace doesn't set the PMU feature. Work around the fact that KVM advertises a PMU even when the VCPU feature is not set by gating all PMU emulation on the feature. The guest can still access the registers without KVM injecting an undefined exception. Signed-off-by: Alexandru Elisei Signed-off-by: Marc Zyngier Link: https://lore.kernel.org/r/20220425145530.723858-1-alexandru.elisei@arm.com Signed-off-by: Slim6882 <15605196882@163.com> --- arch/arm64/kvm/pmu-emul.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/arch/arm64/kvm/pmu-emul.c b/arch/arm64/kvm/pmu-emul.c index 886048c083638..76fa86e4e7786 100644 --- a/arch/arm64/kvm/pmu-emul.c +++ b/arch/arm64/kvm/pmu-emul.c @@ -166,6 +166,9 @@ u64 kvm_pmu_get_counter_value(struct kvm_vcpu *vcpu, u64 select_idx) struct kvm_pmu *pmu = &vcpu->arch.pmu; struct kvm_pmc *pmc = &pmu->pmc[select_idx]; + if (!kvm_vcpu_has_pmu(vcpu)) + return 0; + counter = kvm_pmu_get_pair_counter_value(vcpu, pmc); if (kvm_pmu_pmc_is_chained(pmc) && @@ -187,6 +190,9 @@ void kvm_pmu_set_counter_value(struct kvm_vcpu *vcpu, u64 select_idx, u64 val) { u64 reg; + if (!kvm_vcpu_has_pmu(vcpu)) + return; + reg = (select_idx == ARMV8_PMU_CYCLE_IDX) ? PMCCNTR_EL0 : PMEVCNTR0_EL0 + select_idx; __vcpu_sys_reg(vcpu, reg) += (s64)val - kvm_pmu_get_counter_value(vcpu, select_idx); @@ -311,6 +317,9 @@ void kvm_pmu_enable_counter_mask(struct kvm_vcpu *vcpu, u64 val) struct kvm_pmu *pmu = &vcpu->arch.pmu; struct kvm_pmc *pmc; + if (!kvm_vcpu_has_pmu(vcpu)) + return; + if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E) || !val) return; @@ -346,7 +355,7 @@ void kvm_pmu_disable_counter_mask(struct kvm_vcpu *vcpu, u64 val) struct kvm_pmu *pmu = &vcpu->arch.pmu; struct kvm_pmc *pmc; - if (!val) + if (!kvm_vcpu_has_pmu(vcpu) || !val) return; for (i = 0; i < ARMV8_PMU_MAX_COUNTERS; i++) { @@ -516,6 +525,9 @@ void kvm_pmu_software_increment(struct kvm_vcpu *vcpu, u64 val) struct kvm_pmu *pmu = &vcpu->arch.pmu; int i; + if (!kvm_vcpu_has_pmu(vcpu)) + return; + if (!(__vcpu_sys_reg(vcpu, PMCR_EL0) & ARMV8_PMU_PMCR_E)) return; @@ -566,6 +578,9 @@ void kvm_pmu_handle_pmcr(struct kvm_vcpu *vcpu, u64 val) { int i; + if (!kvm_vcpu_has_pmu(vcpu)) + return; + if (val & ARMV8_PMU_PMCR_E) { kvm_pmu_enable_counter_mask(vcpu, __vcpu_sys_reg(vcpu, PMCNTENSET_EL0)); @@ -728,6 +743,9 @@ void kvm_pmu_set_counter_event_type(struct kvm_vcpu *vcpu, u64 data, { u64 reg, mask; + if (!kvm_vcpu_has_pmu(vcpu)) + return; + mask = ARMV8_PMU_EVTYPE_MASK; mask &= ~ARMV8_PMU_EVTYPE_EVENT; mask |= kvm_pmu_event_mask(vcpu->kvm); @@ -799,6 +817,9 @@ u64 kvm_pmu_get_pmceid(struct kvm_vcpu *vcpu, bool pmceid1) u64 val, mask = 0; int base, i, nr_events; + if (!kvm_vcpu_has_pmu(vcpu)) + return 0; + if (!pmceid1) { val = read_sysreg(pmceid0_el0); base = 0; From 7101f20b3c0b8bbe241cf63a3973d54f62ae843f Mon Sep 17 00:00:00 2001 From: hbmm Date: Sat, 5 Jul 2025 16:44:44 +0800 Subject: [PATCH 213/227] Revert "tpm,tpm_tis: Avoid warning splat at shutdown" This reverts commit f5d1be445b7769715604d751878d2ed114452526. --- drivers/char/tpm/tpm_tis_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_tis_core.c b/drivers/char/tpm/tpm_tis_core.c index 6dde52c4f1d67..b3452259d6e0b 100644 --- a/drivers/char/tpm/tpm_tis_core.c +++ b/drivers/char/tpm/tpm_tis_core.c @@ -779,6 +779,7 @@ static int tpm_tis_probe_irq_single(struct tpm_chip *chip, u32 intmask, int rc; u32 int_status; + rc = devm_request_threaded_irq(chip->dev.parent, irq, NULL, tis_int_handler, IRQF_ONESHOT | flags, dev_name(&chip->dev), chip); @@ -988,7 +989,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, priv->phy_ops = phy_ops; priv->locality_count = 0; mutex_init(&priv->locality_count_mutex); - INIT_WORK(&priv->free_irq_work, tpm_tis_free_irq_func); dev_set_drvdata(&chip->dev, priv); From b316cc84def94ee52030867a3e46ec37773d8f32 Mon Sep 17 00:00:00 2001 From: hbmm Date: Sat, 5 Jul 2025 16:59:11 +0800 Subject: [PATCH 214/227] bugfix: fix acpi_fetch_acpi_dev build failed -------------------------------- fix kernel build error Signed-off-by: Yanze Liu --- drivers/acpi/scan.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index ae74720888dbf..8672f1c6b8f8d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -586,6 +586,24 @@ static struct acpi_device *handle_to_device(acpi_handle handle, return adev; } +/** + * acpi_fetch_acpi_dev - Retrieve ACPI device object. + * @handle: ACPI handle associated with the requested ACPI device object. + * + * Return a pointer to the ACPI device object associated with @handle, if + * present, or NULL otherwise. + */ +struct acpi_device *acpi_fetch_acpi_dev(acpi_handle handle) +{ + return handle_to_device(handle, NULL); +} +EXPORT_SYMBOL_GPL(acpi_fetch_acpi_dev); + +static void get_acpi_device(void *dev) +{ + acpi_dev_get(dev); +} + int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) { if (!device) @@ -599,11 +617,6 @@ int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device) } EXPORT_SYMBOL(acpi_bus_get_device); -static void get_acpi_device(void *dev) -{ - acpi_dev_get(dev); -} - struct acpi_device *acpi_bus_get_acpi_device(acpi_handle handle) { return handle_to_device(handle, get_acpi_device); From 3c3e40fc1c2b9814a18c5ba64ae3da0cd7a8285b Mon Sep 17 00:00:00 2001 From: chenyi Date: Sat, 5 Jul 2025 18:53:30 +0800 Subject: [PATCH 215/227] Revert "drm: Move nomodeset kernel parameter to the DRM subsystem" This reverts commit 75cb9e8fd0208c54d4fe5b64850e555cd62f078a. --- drivers/gpu/drm/Makefile | 2 -- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 ++- drivers/gpu/drm/ast/ast_drv.c | 3 ++- drivers/gpu/drm/i915/i915_module.c | 4 ++-- drivers/gpu/drm/mgag200/mgag200_drv.c | 3 ++- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 ++-- drivers/gpu/drm/qxl/qxl_drv.c | 3 ++- drivers/gpu/drm/radeon/radeon_drv.c | 3 ++- drivers/gpu/drm/tiny/bochs.c | 3 ++- drivers/gpu/drm/tiny/cirrus.c | 4 ++-- drivers/gpu/drm/vboxvideo/vbox_drv.c | 3 ++- drivers/gpu/drm/virtio/virtgpu_drv.c | 3 ++- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 3 ++- drivers/video/console/vgacon.c | 21 ++++++++++++++++++++ gpu/drm/drm_nomodeset.c | 26 ------------------------- include/drm/drm_drv.h | 5 ----- include/linux/console.h | 6 ++++++ 17 files changed, 51 insertions(+), 48 deletions(-) delete mode 100644 gpu/drm/drm_nomodeset.c diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 316888251eff6..d89c2c6aab5cf 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -36,8 +36,6 @@ drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o obj-$(CONFIG_DRM_DP_AUX_BUS) += drm_dp_aux_bus.o -obj-$(CONFIG_VGA_CONSOLE) += drm_nomodeset.o - drm_vram_helper-y := drm_gem_vram_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 2dd6eae1dc189..da7e87de3b2b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -30,6 +30,7 @@ #include "amdgpu_drv.h" #include +#include #include #include #include @@ -2646,7 +2647,7 @@ static int __init amdgpu_init(void) { int r; - if (drm_firmware_drivers_only()) + if (vgacon_text_force()) { DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 6d8613f6fe1cd..86d5cd7b6318c 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -26,6 +26,7 @@ * Authors: Dave Airlie */ +#include #include #include @@ -232,7 +233,7 @@ static struct pci_driver ast_pci_driver = { static int __init ast_init(void) { - if (drm_firmware_drivers_only() && ast_modeset == -1) + if (vgacon_text_force() && ast_modeset == -1) return -EINVAL; if (ast_modeset == 0) diff --git a/drivers/gpu/drm/i915/i915_module.c b/drivers/gpu/drm/i915/i915_module.c index 6c49ac3d4458a..d8b4482c69d0e 100644 --- a/drivers/gpu/drm/i915/i915_module.c +++ b/drivers/gpu/drm/i915/i915_module.c @@ -4,7 +4,7 @@ * Copyright © 2021 Intel Corporation */ -#include +#include #include "gem/i915_gem_context.h" #include "gem/i915_gem_object.h" @@ -31,7 +31,7 @@ static int i915_check_nomodeset(void) if (i915_modparams.modeset == 0) use_kms = false; - if (drm_firmware_drivers_only() && i915_modparams.modeset == -1) + if (vgacon_text_force() && i915_modparams.modeset == -1) use_kms = false; if (!use_kms) { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 740108a006ba5..6b9243713b3c6 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -6,6 +6,7 @@ * Dave Airlie */ +#include #include #include #include @@ -377,7 +378,7 @@ static struct pci_driver mgag200_pci_driver = { static int __init mgag200_init(void) { - if (drm_firmware_drivers_only() && mgag200_modeset == -1) + if (vgacon_text_force() && mgag200_modeset == -1) return -EINVAL; if (mgag200_modeset == 0) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 1674b606965da..3a7e9983c408c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -22,6 +22,7 @@ * Authors: Ben Skeggs */ +#include #include #include #include @@ -31,7 +32,6 @@ #include #include -#include #include #include #include @@ -1363,7 +1363,7 @@ nouveau_drm_init(void) nouveau_display_options(); if (nouveau_modeset == -1) { - if (drm_firmware_drivers_only()) + if (vgacon_text_force()) nouveau_modeset = 0; } diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index e4b16421500b4..fc47b0deb021d 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -29,6 +29,7 @@ #include "qxl_drv.h" +#include #include #include #include @@ -294,7 +295,7 @@ static struct drm_driver qxl_driver = { static int __init qxl_init(void) { - if (drm_firmware_drivers_only() && qxl_modeset == -1) + if (vgacon_text_force() && qxl_modeset == -1) return -EINVAL; if (qxl_modeset == 0) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index e6db74e1c7192..68c3338a5a8d4 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -31,6 +31,7 @@ #include +#include #include #include #include @@ -635,7 +636,7 @@ static struct pci_driver radeon_kms_pci_driver = { static int __init radeon_module_init(void) { - if (drm_firmware_drivers_only() && radeon_modeset == -1) { + if (vgacon_text_force() && radeon_modeset == -1) { DRM_INFO("VGACON disable radeon kernel modesetting.\n"); radeon_modeset = 0; } diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index 2da8019b8e3ef..9d342cd02dd90 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include @@ -711,7 +712,7 @@ static struct pci_driver bochs_pci_driver = { static int __init bochs_init(void) { - if (drm_firmware_drivers_only() && bochs_modeset == -1) + if (vgacon_text_force() && bochs_modeset == -1) return -EINVAL; if (bochs_modeset == 0) diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index 8dfb3ada4fc06..2a81311b22172 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -16,6 +16,7 @@ * Copyright 1999-2001 Jeff Garzik */ +#include #include #include #include @@ -635,9 +636,8 @@ static struct pci_driver cirrus_pci_driver = { static int __init cirrus_init(void) { - if (drm_firmware_drivers_only()) + if (vgacon_text_force()) return -EINVAL; - return pci_register_driver(&cirrus_pci_driver); } diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 1f270398de992..2b81cb259d234 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -7,6 +7,7 @@ * Michael Thayer */ +#include #include #include #include @@ -196,7 +197,7 @@ static const struct drm_driver driver = { static int __init vbox_init(void) { #ifdef CONFIG_VGA_CONSOLE - if (drm_firmware_drivers_only() && vbox_modeset == -1) + if (vgacon_text_force() && vbox_modeset == -1) return -EINVAL; #endif diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index e457c71c1a81a..cd92cdc145626 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -27,6 +27,7 @@ */ #include +#include #include #include @@ -101,7 +102,7 @@ static int virtio_gpu_probe(struct virtio_device *vdev) struct drm_device *dev; int ret; - if (drm_firmware_drivers_only() && virtio_gpu_modeset == -1) + if (vgacon_text_force() && virtio_gpu_modeset == -1) return -EINVAL; if (virtio_gpu_modeset == 0) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 03937e710ab3d..8449d09c06f7a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -25,6 +25,7 @@ * **************************************************************************/ +#include #include #include #include @@ -1658,7 +1659,7 @@ static int __init vmwgfx_init(void) { int ret; - if (drm_firmware_drivers_only()) + if (vgacon_text_force()) return -EINVAL; ret = pci_register_driver(&vmw_pci_driver); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 576612f18d596..9a49ea6b5112f 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -97,9 +97,30 @@ static int vga_video_font_height; static int vga_scan_lines __read_mostly; static unsigned int vga_rolled_over; /* last vc_origin offset before wrap */ +static bool vgacon_text_mode_force; static bool vga_hardscroll_enabled; static bool vga_hardscroll_user_enable = true; +bool vgacon_text_force(void) +{ + return vgacon_text_mode_force; +} +EXPORT_SYMBOL(vgacon_text_force); + +static int __init text_mode(char *str) +{ + vgacon_text_mode_force = true; + + pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); + pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); + pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); + + return 1; +} + +/* force text mode - used by kernel modesetting */ +__setup("nomodeset", text_mode); + static int __init no_scroll(char *str) { /* diff --git a/gpu/drm/drm_nomodeset.c b/gpu/drm/drm_nomodeset.c deleted file mode 100644 index 287edfb18b5d7..0000000000000 --- a/gpu/drm/drm_nomodeset.c +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include - -static bool drm_nomodeset; - -bool drm_firmware_drivers_only(void) -{ - return drm_nomodeset; -} -EXPORT_SYMBOL(drm_firmware_drivers_only); - -static int __init disable_modeset(char *str) -{ - drm_nomodeset = true; - - pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); - pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); - pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); - - return 1; -} - -/* Disable kernel modesetting */ -__setup("nomodeset", disable_modeset); diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index eec308da88f40..84d642e9faa35 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -595,10 +595,5 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) int drm_dev_set_unique(struct drm_device *dev, const char *name); -#ifdef CONFIG_VGA_CONSOLE -extern bool drm_firmware_drivers_only(void); -#else -static inline bool drm_firmware_drivers_only(void) { return false; } -#endif #endif diff --git a/include/linux/console.h b/include/linux/console.h index 7cd758a4f44ed..a97f277cfdfa3 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -219,6 +219,12 @@ extern atomic_t ignore_console_lock_warning; #define VESA_HSYNC_SUSPEND 2 #define VESA_POWERDOWN 3 +#ifdef CONFIG_VGA_CONSOLE +extern bool vgacon_text_force(void); +#else +static inline bool vgacon_text_force(void) { return false; } +#endif + extern void console_init(void); /* For deferred console takeover */ From 2436e4096f0732695b7afc09f86f947739335372 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Fri, 12 Nov 2021 14:32:27 +0100 Subject: [PATCH 216/227] drm: Move nomodeset kernel parameter to the DRM subsystem The "nomodeset" kernel cmdline parameter is handled by the vgacon driver but the exported vgacon_text_force() symbol is only used by DRM drivers. It makes much more sense for the parameter logic to be in the subsystem of the drivers that are making use of it. Let's move the vgacon_text_force() function and related logic to the DRM subsystem. While doing that, rename it to drm_firmware_drivers_only() and make it return true if "nomodeset" was used and false otherwise. This is a better description of the condition that the drivers are testing for. Suggested-by: Daniel Vetter Signed-off-by: Javier Martinez Canillas Acked-by: Thomas Zimmermann Acked-by: Jani Nikula Acked-by: Pekka Paalanen Acked-by: Greg Kroah-Hartman Link: https://patchwork.freedesktop.org/patch/msgid/20211112133230.1595307-4-javierm@redhat.com Signed-off-by: chenyi --- drivers/gpu/drm/Makefile | 2 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 3 +-- drivers/gpu/drm/ast/ast_drv.c | 3 +-- drivers/gpu/drm/i915/i915_module.c | 4 ++-- drivers/gpu/drm/mgag200/mgag200_drv.c | 3 +-- drivers/gpu/drm/nouveau/nouveau_drm.c | 4 ++-- drivers/gpu/drm/qxl/qxl_drv.c | 3 +-- drivers/gpu/drm/radeon/radeon_drv.c | 3 +-- drivers/gpu/drm/tiny/bochs.c | 3 +-- drivers/gpu/drm/tiny/cirrus.c | 4 ++-- drivers/gpu/drm/vboxvideo/vbox_drv.c | 4 +--- drivers/gpu/drm/virtio/virtgpu_drv.c | 3 +-- drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 3 +-- drivers/video/console/vgacon.c | 21 -------------------- gpu/drm/drm_nomodeset.c | 26 +++++++++++++++++++++++++ include/drm/drm_drv.h | 5 +++++ include/linux/console.h | 6 ------ 17 files changed, 48 insertions(+), 52 deletions(-) create mode 100644 gpu/drm/drm_nomodeset.c diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index d89c2c6aab5cf..316888251eff6 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -36,6 +36,8 @@ drm-$(CONFIG_DRM_ACCEL) += ../../accel/drm_accel.o obj-$(CONFIG_DRM_DP_AUX_BUS) += drm_dp_aux_bus.o +obj-$(CONFIG_VGA_CONSOLE) += drm_nomodeset.o + drm_vram_helper-y := drm_gem_vram_helper.o obj-$(CONFIG_DRM_VRAM_HELPER) += drm_vram_helper.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index da7e87de3b2b2..62af9c4d80ba4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -30,7 +30,6 @@ #include "amdgpu_drv.h" #include -#include #include #include #include @@ -2647,7 +2646,7 @@ static int __init amdgpu_init(void) { int r; - if (vgacon_text_force()) { + if (drm_firmware_drivers_only()) { DRM_ERROR("VGACON disables amdgpu kernel modesetting.\n"); return -EINVAL; } diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index 86d5cd7b6318c..6d8613f6fe1cd 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -26,7 +26,6 @@ * Authors: Dave Airlie */ -#include #include #include @@ -233,7 +232,7 @@ static struct pci_driver ast_pci_driver = { static int __init ast_init(void) { - if (vgacon_text_force() && ast_modeset == -1) + if (drm_firmware_drivers_only() && ast_modeset == -1) return -EINVAL; if (ast_modeset == 0) diff --git a/drivers/gpu/drm/i915/i915_module.c b/drivers/gpu/drm/i915/i915_module.c index d8b4482c69d0e..6c49ac3d4458a 100644 --- a/drivers/gpu/drm/i915/i915_module.c +++ b/drivers/gpu/drm/i915/i915_module.c @@ -4,7 +4,7 @@ * Copyright © 2021 Intel Corporation */ -#include +#include #include "gem/i915_gem_context.h" #include "gem/i915_gem_object.h" @@ -31,7 +31,7 @@ static int i915_check_nomodeset(void) if (i915_modparams.modeset == 0) use_kms = false; - if (vgacon_text_force() && i915_modparams.modeset == -1) + if (drm_firmware_drivers_only() && i915_modparams.modeset == -1) use_kms = false; if (!use_kms) { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index 6b9243713b3c6..740108a006ba5 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -6,7 +6,6 @@ * Dave Airlie */ -#include #include #include #include @@ -378,7 +377,7 @@ static struct pci_driver mgag200_pci_driver = { static int __init mgag200_init(void) { - if (vgacon_text_force() && mgag200_modeset == -1) + if (drm_firmware_drivers_only() && mgag200_modeset == -1) return -EINVAL; if (mgag200_modeset == 0) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 3a7e9983c408c..1674b606965da 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -22,7 +22,6 @@ * Authors: Ben Skeggs */ -#include #include #include #include @@ -32,6 +31,7 @@ #include #include +#include #include #include #include @@ -1363,7 +1363,7 @@ nouveau_drm_init(void) nouveau_display_options(); if (nouveau_modeset == -1) { - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) nouveau_modeset = 0; } diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index fc47b0deb021d..e4b16421500b4 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -29,7 +29,6 @@ #include "qxl_drv.h" -#include #include #include #include @@ -295,7 +294,7 @@ static struct drm_driver qxl_driver = { static int __init qxl_init(void) { - if (vgacon_text_force() && qxl_modeset == -1) + if (drm_firmware_drivers_only() && qxl_modeset == -1) return -EINVAL; if (qxl_modeset == 0) diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 68c3338a5a8d4..e6db74e1c7192 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -31,7 +31,6 @@ #include -#include #include #include #include @@ -636,7 +635,7 @@ static struct pci_driver radeon_kms_pci_driver = { static int __init radeon_module_init(void) { - if (vgacon_text_force() && radeon_modeset == -1) { + if (drm_firmware_drivers_only() && radeon_modeset == -1) { DRM_INFO("VGACON disable radeon kernel modesetting.\n"); radeon_modeset = 0; } diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index 9d342cd02dd90..2da8019b8e3ef 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -1,6 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#include #include #include @@ -712,7 +711,7 @@ static struct pci_driver bochs_pci_driver = { static int __init bochs_init(void) { - if (vgacon_text_force() && bochs_modeset == -1) + if (drm_firmware_drivers_only() && bochs_modeset == -1) return -EINVAL; if (bochs_modeset == 0) diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index 2a81311b22172..8dfb3ada4fc06 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -16,7 +16,6 @@ * Copyright 1999-2001 Jeff Garzik */ -#include #include #include #include @@ -636,8 +635,9 @@ static struct pci_driver cirrus_pci_driver = { static int __init cirrus_init(void) { - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) return -EINVAL; + return pci_register_driver(&cirrus_pci_driver); } diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 2b81cb259d234..fcf761ad627ea 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -7,7 +7,6 @@ * Michael Thayer */ -#include #include #include #include @@ -196,8 +195,7 @@ static const struct drm_driver driver = { static int __init vbox_init(void) { -#ifdef CONFIG_VGA_CONSOLE - if (vgacon_text_force() && vbox_modeset == -1) + if (drm_firmware_drivers_only() && vbox_modeset == -1) return -EINVAL; #endif diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c index cd92cdc145626..e457c71c1a81a 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.c +++ b/drivers/gpu/drm/virtio/virtgpu_drv.c @@ -27,7 +27,6 @@ */ #include -#include #include #include @@ -102,7 +101,7 @@ static int virtio_gpu_probe(struct virtio_device *vdev) struct drm_device *dev; int ret; - if (vgacon_text_force() && virtio_gpu_modeset == -1) + if (drm_firmware_drivers_only() && virtio_gpu_modeset == -1) return -EINVAL; if (virtio_gpu_modeset == 0) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 8449d09c06f7a..03937e710ab3d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -25,7 +25,6 @@ * **************************************************************************/ -#include #include #include #include @@ -1659,7 +1658,7 @@ static int __init vmwgfx_init(void) { int ret; - if (vgacon_text_force()) + if (drm_firmware_drivers_only()) return -EINVAL; ret = pci_register_driver(&vmw_pci_driver); diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index 9a49ea6b5112f..576612f18d596 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -97,30 +97,9 @@ static int vga_video_font_height; static int vga_scan_lines __read_mostly; static unsigned int vga_rolled_over; /* last vc_origin offset before wrap */ -static bool vgacon_text_mode_force; static bool vga_hardscroll_enabled; static bool vga_hardscroll_user_enable = true; -bool vgacon_text_force(void) -{ - return vgacon_text_mode_force; -} -EXPORT_SYMBOL(vgacon_text_force); - -static int __init text_mode(char *str) -{ - vgacon_text_mode_force = true; - - pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); - pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); - pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); - - return 1; -} - -/* force text mode - used by kernel modesetting */ -__setup("nomodeset", text_mode); - static int __init no_scroll(char *str) { /* diff --git a/gpu/drm/drm_nomodeset.c b/gpu/drm/drm_nomodeset.c new file mode 100644 index 0000000000000..287edfb18b5d7 --- /dev/null +++ b/gpu/drm/drm_nomodeset.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include + +static bool drm_nomodeset; + +bool drm_firmware_drivers_only(void) +{ + return drm_nomodeset; +} +EXPORT_SYMBOL(drm_firmware_drivers_only); + +static int __init disable_modeset(char *str) +{ + drm_nomodeset = true; + + pr_warn("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); + pr_warn("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); + pr_warn("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); + + return 1; +} + +/* Disable kernel modesetting */ +__setup("nomodeset", disable_modeset); diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h index 84d642e9faa35..eec308da88f40 100644 --- a/include/drm/drm_drv.h +++ b/include/drm/drm_drv.h @@ -595,5 +595,10 @@ static inline bool drm_drv_uses_atomic_modeset(struct drm_device *dev) int drm_dev_set_unique(struct drm_device *dev, const char *name); +#ifdef CONFIG_VGA_CONSOLE +extern bool drm_firmware_drivers_only(void); +#else +static inline bool drm_firmware_drivers_only(void) { return false; } +#endif #endif diff --git a/include/linux/console.h b/include/linux/console.h index a97f277cfdfa3..7cd758a4f44ed 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -219,12 +219,6 @@ extern atomic_t ignore_console_lock_warning; #define VESA_HSYNC_SUSPEND 2 #define VESA_POWERDOWN 3 -#ifdef CONFIG_VGA_CONSOLE -extern bool vgacon_text_force(void); -#else -static inline bool vgacon_text_force(void) { return false; } -#endif - extern void console_init(void); /* For deferred console takeover */ From 8c1c9e816ec0b0c3a29c08e1d787aba78bfdd974 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 8 Nov 2023 10:38:24 +0800 Subject: [PATCH 217/227] arm64/sysreg: Introduce helpers for access to sysreg fields mainline inclusion from mainline-v5.19-rc1 commit e6a6b34f97efe3ded077b31f4370b4c1206c9e56 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=e6a6b34f97efe3ded077b31f4370b4c1206c9e56 -------------------------------- The macros we define for the bitfields within sysregs have very regular names, especially once we switch to automatic generation of those macros. Take advantage of this to define wrappers around FIELD_PREP() allowing us to simplify setting values in fields either numerically SYS_FIELD_PREP(SCTLR_EL1, TCF0, 0x0) or using the values of enumerations within the fields SYS_FIELD_PREP_ENUM(SCTLR_EL1, TCF0, ASYMM) Suggested-by: Mark Rutland Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220503170233.507788-2-broonie@kernel.org Signed-off-by: Catalin Marinas Signed-off-by: Junhao He --- arch/arm64/include/asm/sysreg.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 995a1286cbc21..98b8909799c7e 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -1358,4 +1358,10 @@ #endif +#define SYS_FIELD_PREP(reg, field, val) \ + FIELD_PREP(reg##_##field##_MASK, val) + +#define SYS_FIELD_PREP_ENUM(reg, field, val) \ + FIELD_PREP(reg##_##field##_MASK, reg##_##field##_##val) + #endif /* __ASM_SYSREG_H */ From 56009191e94e70e5ebac04064bf068de0290f916 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:25 +0800 Subject: [PATCH 218/227] perf: Add system error and not in transaction branch types mainline inclusion from mainline-v6.1-rc1 commit a724ec82966d57e4b5d36341d3e3dc1a3c011564 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=a724ec82966d57e4b5d36341d3e3dc1a3c011564 -------------------------------- This expands generic branch type classification by adding two more entries there in i.e system error and not in transaction. This also updates the x86 implementation to process X86_BR_NO_TX records as appropriate. This changes branch types reported to user space on x86 platform but it should not be a problem. The possible scenarios and impacts are enumerated here. -------------------------------------------------------------------------- | kernel | perf tool | Impact | -------------------------------------------------------------------------- | old | old | Works as before | -------------------------------------------------------------------------- | old | new | PERF_BR_UNKNOWN is processed | -------------------------------------------------------------------------- | new | old | PERF_BR_NO_TX is blocked via old PERF_BR_MAX | -------------------------------------------------------------------------- | new | new | PERF_BR_NO_TX is recognized | -------------------------------------------------------------------------- When PERF_BR_NO_TX is blocked via old PERF_BR_MAX (new kernel with old perf tool) the user space might throw up an warning complaining about an unrecognized branch types being reported, but it's expected. PERF_BR_SERROR & PERF_BR_NO_TX branch types will be used for BRBE implementation on arm64 platform. PERF_BR_NO_TX complements 'abort' and 'in_tx' elements in perf_branch_entry which represent other transaction states for a given branch record. Because this completes the transaction state classification. Signed-off-by: Anshuman Khandual Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: James Clark Link: https://lkml.kernel.org/r/20220824044822.70230-2-anshuman.khandual@arm.com Signed-off-by: Junhao He --- include/uapi/linux/perf_event.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 868d009f4729b..0c5e273ce39aa 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -253,6 +253,8 @@ enum { PERF_BR_COND_RET = 10, /* conditional function return */ PERF_BR_ERET = 11, /* exception return */ PERF_BR_IRQ = 12, /* irq */ + PERF_BR_SERROR = 13, /* system error */ + PERF_BR_NO_TX = 14, /* not in transaction */ PERF_BR_MAX, }; From 6c4499de3a92cee2e3c98767482c98812abd8f1d Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:27 +0800 Subject: [PATCH 219/227] perf: Extend branch type classification mainline inclusion from mainline-v6.1-rc1 commit b190bc4ac9e6d9763b61654c5a0c085ff77d7a09 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=b190bc4ac9e6d9763b61654c5a0c085ff77d7a09 -------------------------------- branch_entry.type now has ran out of space to accommodate more branch types classification. This will prevent perf branch stack implementation on arm64 (via BRBE) to capture all available branch types. Extending this bit field i.e branch_entry.type [4 bits] is not an option as it will break user space ABI both for little and big endian perf tools. Extend branch classification with a new field branch_entry.new_type via a new branch type PERF_BR_EXTEND_ABI in branch_entry.type. Perf tools which could decode PERF_BR_EXTEND_ABI, will then parse branch_entry.new_type as well. branch_entry.new_type is a 4 bit field which can hold upto 16 branch types. The first three branch types will hold various generic page faults followed by five architecture specific branch types, which can be overridden by the platform for specific use cases. These architecture specific branch types gets overridden on arm64 platform for BRBE implementation. New generic branch types - PERF_BR_NEW_FAULT_ALGN - PERF_BR_NEW_FAULT_DATA - PERF_BR_NEW_FAULT_INST New arch specific branch types - PERF_BR_NEW_ARCH_1 - PERF_BR_NEW_ARCH_2 - PERF_BR_NEW_ARCH_3 - PERF_BR_NEW_ARCH_4 - PERF_BR_NEW_ARCH_5 Signed-off-by: Anshuman Khandual Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: James Clark Link: https://lkml.kernel.org/r/20220824044822.70230-3-anshuman.khandual@arm.com Signed-off-by: Junhao He --- include/uapi/linux/perf_event.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 0c5e273ce39aa..598c5e5f2d6cc 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -255,6 +255,7 @@ enum { PERF_BR_IRQ = 12, /* irq */ PERF_BR_SERROR = 13, /* system error */ PERF_BR_NO_TX = 14, /* not in transaction */ + PERF_BR_EXTEND_ABI = 15, /* extend ABI */ PERF_BR_MAX, }; @@ -269,6 +270,18 @@ enum { PERF_BR_SPEC_MAX, }; +enum { + PERF_BR_NEW_FAULT_ALGN = 0, /* Alignment fault */ + PERF_BR_NEW_FAULT_DATA = 1, /* Data fault */ + PERF_BR_NEW_FAULT_INST = 2, /* Inst fault */ + PERF_BR_NEW_ARCH_1 = 3, /* Architecture specific */ + PERF_BR_NEW_ARCH_2 = 4, /* Architecture specific */ + PERF_BR_NEW_ARCH_3 = 5, /* Architecture specific */ + PERF_BR_NEW_ARCH_4 = 6, /* Architecture specific */ + PERF_BR_NEW_ARCH_5 = 7, /* Architecture specific */ + PERF_BR_NEW_MAX, +}; + #define PERF_SAMPLE_BRANCH_PLM_ALL \ (PERF_SAMPLE_BRANCH_USER|\ PERF_SAMPLE_BRANCH_KERNEL|\ @@ -1382,7 +1395,8 @@ struct perf_branch_entry { cycles:16, /* cycle count to last branch */ type:4, /* branch type */ spec:2, /* branch speculation info */ - reserved:38; + new_type:4, /* additional branch type */ + reserved:34; }; union perf_sample_weight { From c45c8a397330978522a469456c621be06730ab7b Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:28 +0800 Subject: [PATCH 220/227] perf: Capture branch privilege information mainline inclusion from mainline-v6.1-rc1 commit 5402d25aa5710d240040f73fb13d7d5c303ef071 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=5402d25aa5710d240040f73fb13d7d5c303ef071 -------------------------------- Platforms like arm64 could capture privilege level information for all the branch records. Hence this adds a new element in the struct branch_entry to record the privilege level information, which could be requested through a new event.attr.branch_sample_type based flag PERF_SAMPLE_BRANCH_PRIV_SAVE. This flag helps user choose whether privilege information is captured. Signed-off-by: Anshuman Khandual Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: James Clark Link: https://lkml.kernel.org/r/20220824044822.70230-4-anshuman.khandual@arm.com Signed-off-by: Junhao He --- include/uapi/linux/perf_event.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 598c5e5f2d6cc..20b3f21463351 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -204,6 +204,8 @@ enum perf_branch_sample_type_shift { PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT = 17, /* save low level index of raw branch records */ + PERF_SAMPLE_BRANCH_PRIV_SAVE_SHIFT = 18, /* save privilege mode */ + PERF_SAMPLE_BRANCH_MAX_SHIFT /* non-ABI */ }; @@ -233,6 +235,8 @@ enum perf_branch_sample_type { PERF_SAMPLE_BRANCH_HW_INDEX = 1U << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT, + PERF_SAMPLE_BRANCH_PRIV_SAVE = 1U << PERF_SAMPLE_BRANCH_PRIV_SAVE_SHIFT, + PERF_SAMPLE_BRANCH_MAX = 1U << PERF_SAMPLE_BRANCH_MAX_SHIFT, }; @@ -282,6 +286,13 @@ enum { PERF_BR_NEW_MAX, }; +enum { + PERF_BR_PRIV_UNKNOWN = 0, + PERF_BR_PRIV_USER = 1, + PERF_BR_PRIV_KERNEL = 2, + PERF_BR_PRIV_HV = 3, +}; + #define PERF_SAMPLE_BRANCH_PLM_ALL \ (PERF_SAMPLE_BRANCH_USER|\ PERF_SAMPLE_BRANCH_KERNEL|\ @@ -1396,7 +1407,8 @@ struct perf_branch_entry { type:4, /* branch type */ spec:2, /* branch speculation info */ new_type:4, /* additional branch type */ - reserved:34; + priv:3, /* privilege level */ + reserved:31; }; union perf_sample_weight { From 4802390154c938a82242780ced5557619f4d83f4 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:29 +0800 Subject: [PATCH 221/227] perf: Add PERF_BR_NEW_ARCH_[N] map for BRBE on arm64 platform mainline inclusion from mainline-v6.1-rc1 commit f4054e522531038354bea5c924f286fdd8ae77b5 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=f4054e522531038354bea5c924f286fdd8ae77b5 -------------------------------- BRBE captured branch types will overflow perf_branch_entry.type and generic branch types in perf_branch_entry.new_type. So override each available arch specific branch type in the following manner to comprehensively process all reported branch types in BRBE. PERF_BR_ARM64_FIQ PERF_BR_NEW_ARCH_1 PERF_BR_ARM64_DEBUG_HALT PERF_BR_NEW_ARCH_2 PERF_BR_ARM64_DEBUG_EXIT PERF_BR_NEW_ARCH_3 PERF_BR_ARM64_DEBUG_INST PERF_BR_NEW_ARCH_4 PERF_BR_ARM64_DEBUG_DATA PERF_BR_NEW_ARCH_5 Signed-off-by: Anshuman Khandual Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: James Clark Link: https://lkml.kernel.org/r/20220824044822.70230-5-anshuman.khandual@arm.com Signed-off-by: Junhao He --- include/uapi/linux/perf_event.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index 20b3f21463351..8115ee2bbd427 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -293,6 +293,12 @@ enum { PERF_BR_PRIV_HV = 3, }; +#define PERF_BR_ARM64_FIQ PERF_BR_NEW_ARCH_1 +#define PERF_BR_ARM64_DEBUG_HALT PERF_BR_NEW_ARCH_2 +#define PERF_BR_ARM64_DEBUG_EXIT PERF_BR_NEW_ARCH_3 +#define PERF_BR_ARM64_DEBUG_INST PERF_BR_NEW_ARCH_4 +#define PERF_BR_ARM64_DEBUG_DATA PERF_BR_NEW_ARCH_5 + #define PERF_SAMPLE_BRANCH_PLM_ALL \ (PERF_SAMPLE_BRANCH_USER|\ PERF_SAMPLE_BRANCH_KERNEL|\ From ccde86cbedc0f0040ac348f132afbaf54377bc2c Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:34 +0800 Subject: [PATCH 222/227] perf: Consolidate branch sample filter helpers mainline inclusion from mainline-v6.1-rc1 commit 03b02db93be407103c385814033633364674a6f6 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=03b02db93be407103c385814033633364674a6f6 -------------------------------- Besides the branch type filtering requests, 'event.attr.branch_sample_type' also contains various flags indicating which additional information should be captured, along with the base branch record. These flags help configure the underlying hardware, and capture the branch records appropriately when required e.g after PMU interrupt. But first, this moves an existing helper perf_sample_save_hw_index() into the header before adding some more helpers for other branch sample filter flags. Signed-off-by: Anshuman Khandual Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20220906084414.396220-1-anshuman.khandual@arm.com Signed-off-by: Junhao He --- include/linux/perf_event.h | 26 ++++++++++++++++++++++++++ kernel/events/core.c | 9 ++------- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index dbbbe7fc51933..a1d7c82245c4d 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -1778,4 +1778,30 @@ static inline void perf_lopwr_cb(bool mode) } #endif +#ifdef CONFIG_PERF_EVENTS +static inline bool branch_sample_no_flags(const struct perf_event *event) +{ + return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_NO_FLAGS; +} + +static inline bool branch_sample_no_cycles(const struct perf_event *event) +{ + return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_NO_CYCLES; +} + +static inline bool branch_sample_type(const struct perf_event *event) +{ + return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_TYPE_SAVE; +} + +static inline bool branch_sample_hw_index(const struct perf_event *event) +{ + return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX; +} + +static inline bool branch_sample_priv(const struct perf_event *event) +{ + return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_PRIV_SAVE; +} +#endif /* CONFIG_PERF_EVENTS */ #endif /* _LINUX_PERF_EVENT_H */ diff --git a/kernel/events/core.c b/kernel/events/core.c index c5bbb16c8a319..ef58c61a0f143 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7272,11 +7272,6 @@ static void perf_output_read(struct perf_output_handle *handle, perf_output_read_one(handle, event, enabled, running); } -static inline bool perf_sample_save_hw_index(struct perf_event *event) -{ - return event->attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX; -} - void perf_output_sample(struct perf_output_handle *handle, struct perf_event_header *header, struct perf_sample_data *data, @@ -7365,7 +7360,7 @@ void perf_output_sample(struct perf_output_handle *handle, * sizeof(struct perf_branch_entry); perf_output_put(handle, data->br_stack->nr); - if (perf_sample_save_hw_index(event)) + if (branch_sample_hw_index(event)) perf_output_put(handle, data->br_stack->hw_idx); perf_output_copy(handle, data->br_stack->entries, size); } else { @@ -7669,7 +7664,7 @@ void perf_prepare_sample(struct perf_event_header *header, if (sample_type & PERF_SAMPLE_BRANCH_STACK) { int size = sizeof(u64); /* nr */ if (data->br_stack) { - if (perf_sample_save_hw_index(event)) + if (branch_sample_hw_index(event)) size += sizeof(u64); size += data->br_stack->nr From 26a76188fd70d6b46bdb5f2695ba88da39baa8a8 Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:35 +0800 Subject: [PATCH 223/227] perf record: Add remaining branch filters: "no_cycles", "no_flags" & "hw_index" mainline inclusion from mainline-v6.2-rc1 commit 955f6def5590ce6ca11a1c1ced0d2d1c95421059 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=955f6def5590ce6ca11a1c1ced0d2d1c95421059 -------------------------------- This adds all remaining branch filters i.e "no_cycles", "no_flags" and "hw_index". While here, also updates the documentation. Signed-off-by: Anshuman Khandual Cc: James Clark Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20221205064443.533587-1-anshuman.khandual@arm.com Signed-off-by: Arnaldo Carvalho de Melo Signed-off-by: Junhao He --- tools/perf/util/parse-branch-options.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c index 31faf2bb49ff1..fd67d204d720d 100644 --- a/tools/perf/util/parse-branch-options.c +++ b/tools/perf/util/parse-branch-options.c @@ -30,8 +30,11 @@ static const struct branch_mode branch_modes[] = { BRANCH_OPT("cond", PERF_SAMPLE_BRANCH_COND), BRANCH_OPT("ind_jmp", PERF_SAMPLE_BRANCH_IND_JUMP), BRANCH_OPT("call", PERF_SAMPLE_BRANCH_CALL), + BRANCH_OPT("no_flags", PERF_SAMPLE_BRANCH_NO_FLAGS), + BRANCH_OPT("no_cycles", PERF_SAMPLE_BRANCH_NO_CYCLES), BRANCH_OPT("save_type", PERF_SAMPLE_BRANCH_TYPE_SAVE), BRANCH_OPT("stack", PERF_SAMPLE_BRANCH_CALL_STACK), + BRANCH_OPT("hw_index", PERF_SAMPLE_BRANCH_HW_INDEX), BRANCH_OPT("priv", PERF_SAMPLE_BRANCH_PRIV_SAVE), BRANCH_END }; From 804123d2111567c301d975d3bdebe5d0c63be7df Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:37 +0800 Subject: [PATCH 224/227] arm64/sysreg: Add BRBE registers and fields maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://lore.kernel.org/lkml/20230711082455.215983-3-anshuman.khandual@arm.com/ -------------------------------- This adds BRBE related register definitions and various other related field macros there in. These will be used subsequently in a BRBE driver which is being added later on. Cc: Catalin Marinas Cc: Will Deacon Cc: Marc Zyngier Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Reviewed-by: Mark Brown Signed-off-by: Anshuman Khandual Signed-off-by: Junhao He --- arch/arm64/include/asm/sysreg.h | 497 ++++++++++++++++++++++++++++++++ 1 file changed, 497 insertions(+) diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 98b8909799c7e..995cb1eac11cf 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -145,6 +145,109 @@ #define SYS_DBGDTRTX_EL0 sys_reg(2, 3, 0, 5, 0) #define SYS_DBGVCR32_EL2 sys_reg(2, 4, 0, 7, 0) +#define __SYS_BRBINF(n) sys_reg(2, 1, 8, ((n) & 0xf), ((((n) & 0x10) >> 2) + 0)) +#define __SYS_BRBSRC(n) sys_reg(2, 1, 8, ((n) & 0xf), ((((n) & 0x10) >> 2) + 1)) +#define __SYS_BRBTGT(n) sys_reg(2, 1, 8, ((n) & 0xf), ((((n) & 0x10) >> 2) + 2)) + +#define SYS_BRBINF0_EL1 __SYS_BRBINF(0) +#define SYS_BRBINF1_EL1 __SYS_BRBINF(1) +#define SYS_BRBINF2_EL1 __SYS_BRBINF(2) +#define SYS_BRBINF3_EL1 __SYS_BRBINF(3) +#define SYS_BRBINF4_EL1 __SYS_BRBINF(4) +#define SYS_BRBINF5_EL1 __SYS_BRBINF(5) +#define SYS_BRBINF6_EL1 __SYS_BRBINF(6) +#define SYS_BRBINF7_EL1 __SYS_BRBINF(7) +#define SYS_BRBINF8_EL1 __SYS_BRBINF(8) +#define SYS_BRBINF9_EL1 __SYS_BRBINF(9) +#define SYS_BRBINF10_EL1 __SYS_BRBINF(10) +#define SYS_BRBINF11_EL1 __SYS_BRBINF(11) +#define SYS_BRBINF12_EL1 __SYS_BRBINF(12) +#define SYS_BRBINF13_EL1 __SYS_BRBINF(13) +#define SYS_BRBINF14_EL1 __SYS_BRBINF(14) +#define SYS_BRBINF15_EL1 __SYS_BRBINF(15) +#define SYS_BRBINF16_EL1 __SYS_BRBINF(16) +#define SYS_BRBINF17_EL1 __SYS_BRBINF(17) +#define SYS_BRBINF18_EL1 __SYS_BRBINF(18) +#define SYS_BRBINF19_EL1 __SYS_BRBINF(19) +#define SYS_BRBINF20_EL1 __SYS_BRBINF(20) +#define SYS_BRBINF21_EL1 __SYS_BRBINF(21) +#define SYS_BRBINF22_EL1 __SYS_BRBINF(22) +#define SYS_BRBINF23_EL1 __SYS_BRBINF(23) +#define SYS_BRBINF24_EL1 __SYS_BRBINF(24) +#define SYS_BRBINF25_EL1 __SYS_BRBINF(25) +#define SYS_BRBINF26_EL1 __SYS_BRBINF(26) +#define SYS_BRBINF27_EL1 __SYS_BRBINF(27) +#define SYS_BRBINF28_EL1 __SYS_BRBINF(28) +#define SYS_BRBINF29_EL1 __SYS_BRBINF(29) +#define SYS_BRBINF30_EL1 __SYS_BRBINF(30) +#define SYS_BRBINF31_EL1 __SYS_BRBINF(31) + +#define SYS_BRBSRC0_EL1 __SYS_BRBSRC(0) +#define SYS_BRBSRC1_EL1 __SYS_BRBSRC(1) +#define SYS_BRBSRC2_EL1 __SYS_BRBSRC(2) +#define SYS_BRBSRC3_EL1 __SYS_BRBSRC(3) +#define SYS_BRBSRC4_EL1 __SYS_BRBSRC(4) +#define SYS_BRBSRC5_EL1 __SYS_BRBSRC(5) +#define SYS_BRBSRC6_EL1 __SYS_BRBSRC(6) +#define SYS_BRBSRC7_EL1 __SYS_BRBSRC(7) +#define SYS_BRBSRC8_EL1 __SYS_BRBSRC(8) +#define SYS_BRBSRC9_EL1 __SYS_BRBSRC(9) +#define SYS_BRBSRC10_EL1 __SYS_BRBSRC(10) +#define SYS_BRBSRC11_EL1 __SYS_BRBSRC(11) +#define SYS_BRBSRC12_EL1 __SYS_BRBSRC(12) +#define SYS_BRBSRC13_EL1 __SYS_BRBSRC(13) +#define SYS_BRBSRC14_EL1 __SYS_BRBSRC(14) +#define SYS_BRBSRC15_EL1 __SYS_BRBSRC(15) +#define SYS_BRBSRC16_EL1 __SYS_BRBSRC(16) +#define SYS_BRBSRC17_EL1 __SYS_BRBSRC(17) +#define SYS_BRBSRC18_EL1 __SYS_BRBSRC(18) +#define SYS_BRBSRC19_EL1 __SYS_BRBSRC(19) +#define SYS_BRBSRC20_EL1 __SYS_BRBSRC(20) +#define SYS_BRBSRC21_EL1 __SYS_BRBSRC(21) +#define SYS_BRBSRC22_EL1 __SYS_BRBSRC(22) +#define SYS_BRBSRC23_EL1 __SYS_BRBSRC(23) +#define SYS_BRBSRC24_EL1 __SYS_BRBSRC(24) +#define SYS_BRBSRC25_EL1 __SYS_BRBSRC(25) +#define SYS_BRBSRC26_EL1 __SYS_BRBSRC(26) +#define SYS_BRBSRC27_EL1 __SYS_BRBSRC(27) +#define SYS_BRBSRC28_EL1 __SYS_BRBSRC(28) +#define SYS_BRBSRC29_EL1 __SYS_BRBSRC(29) +#define SYS_BRBSRC30_EL1 __SYS_BRBSRC(30) +#define SYS_BRBSRC31_EL1 __SYS_BRBSRC(31) + +#define SYS_BRBTGT0_EL1 __SYS_BRBTGT(0) +#define SYS_BRBTGT1_EL1 __SYS_BRBTGT(1) +#define SYS_BRBTGT2_EL1 __SYS_BRBTGT(2) +#define SYS_BRBTGT3_EL1 __SYS_BRBTGT(3) +#define SYS_BRBTGT4_EL1 __SYS_BRBTGT(4) +#define SYS_BRBTGT5_EL1 __SYS_BRBTGT(5) +#define SYS_BRBTGT6_EL1 __SYS_BRBTGT(6) +#define SYS_BRBTGT7_EL1 __SYS_BRBTGT(7) +#define SYS_BRBTGT8_EL1 __SYS_BRBTGT(8) +#define SYS_BRBTGT9_EL1 __SYS_BRBTGT(9) +#define SYS_BRBTGT10_EL1 __SYS_BRBTGT(10) +#define SYS_BRBTGT11_EL1 __SYS_BRBTGT(11) +#define SYS_BRBTGT12_EL1 __SYS_BRBTGT(12) +#define SYS_BRBTGT13_EL1 __SYS_BRBTGT(13) +#define SYS_BRBTGT14_EL1 __SYS_BRBTGT(14) +#define SYS_BRBTGT15_EL1 __SYS_BRBTGT(15) +#define SYS_BRBTGT16_EL1 __SYS_BRBTGT(16) +#define SYS_BRBTGT17_EL1 __SYS_BRBTGT(17) +#define SYS_BRBTGT18_EL1 __SYS_BRBTGT(18) +#define SYS_BRBTGT19_EL1 __SYS_BRBTGT(19) +#define SYS_BRBTGT20_EL1 __SYS_BRBTGT(20) +#define SYS_BRBTGT21_EL1 __SYS_BRBTGT(21) +#define SYS_BRBTGT22_EL1 __SYS_BRBTGT(22) +#define SYS_BRBTGT23_EL1 __SYS_BRBTGT(23) +#define SYS_BRBTGT24_EL1 __SYS_BRBTGT(24) +#define SYS_BRBTGT25_EL1 __SYS_BRBTGT(25) +#define SYS_BRBTGT26_EL1 __SYS_BRBTGT(26) +#define SYS_BRBTGT27_EL1 __SYS_BRBTGT(27) +#define SYS_BRBTGT28_EL1 __SYS_BRBTGT(28) +#define SYS_BRBTGT29_EL1 __SYS_BRBTGT(29) +#define SYS_BRBTGT30_EL1 __SYS_BRBTGT(30) +#define SYS_BRBTGT31_EL1 __SYS_BRBTGT(31) + #define SYS_MIDR_EL1 sys_reg(3, 0, 0, 0, 0) #define SYS_MPIDR_EL1 sys_reg(3, 0, 0, 0, 5) #define SYS_REVIDR_EL1 sys_reg(3, 0, 0, 0, 6) @@ -476,6 +579,396 @@ #define SYS_SCXTNUM_EL0 sys_reg(3, 3, 13, 0, 7) +/*** Branch Record Buffer Extension ***/ +/* ID registers */ +#define BRBINFx_EL1_CCU GENMASK(46, 46) +#define BRBINFx_EL1_CCU_MASK GENMASK(46, 46) +#define BRBINFx_EL1_CCU_SHIFT 46 +#define BRBINFx_EL1_CCU_WIDTH 1 + +#define BRBINFx_EL1_CC GENMASK(45, 32) +#define BRBINFx_EL1_CC_MASK GENMASK(45, 32) +#define BRBINFx_EL1_CC_SHIFT 32 +#define BRBINFx_EL1_CC_WIDTH 14 + +#define BRBINFx_EL1_LASTFAILED GENMASK(17, 17) +#define BRBINFx_EL1_LASTFAILED_MASK GENMASK(17, 17) +#define BRBINFx_EL1_LASTFAILED_SHIFT 17 +#define BRBINFx_EL1_LASTFAILED_WIDTH 1 + +#define BRBINFx_EL1_T GENMASK(16, 16) +#define BRBINFx_EL1_T_MASK GENMASK(16, 16) +#define BRBINFx_EL1_T_SHIFT 16 +#define BRBINFx_EL1_T_WIDTH 1 + +#define BRBINFx_EL1_TYPE GENMASK(13, 8) +#define BRBINFx_EL1_TYPE_MASK GENMASK(13, 8) +#define BRBINFx_EL1_TYPE_SHIFT 8 +#define BRBINFx_EL1_TYPE_WIDTH 6 +#define BRBINFx_EL1_TYPE_UNCOND_DIRECT UL(0b000000) +#define BRBINFx_EL1_TYPE_INDIRECT UL(0b000001) +#define BRBINFx_EL1_TYPE_DIRECT_LINK UL(0b000010) +#define BRBINFx_EL1_TYPE_INDIRECT_LINK UL(0b000011) +#define BRBINFx_EL1_TYPE_RET UL(0b000101) +#define BRBINFx_EL1_TYPE_ERET UL(0b000111) +#define BRBINFx_EL1_TYPE_COND_DIRECT UL(0b001000) +#define BRBINFx_EL1_TYPE_DEBUG_HALT UL(0b100001) +#define BRBINFx_EL1_TYPE_CALL UL(0b100010) +#define BRBINFx_EL1_TYPE_TRAP UL(0b100011) +#define BRBINFx_EL1_TYPE_SERROR UL(0b100100) +#define BRBINFx_EL1_TYPE_INSN_DEBUG UL(0b100110) +#define BRBINFx_EL1_TYPE_DATA_DEBUG UL(0b100111) +#define BRBINFx_EL1_TYPE_ALIGN_FAULT UL(0b101010) +#define BRBINFx_EL1_TYPE_INSN_FAULT UL(0b101011) +#define BRBINFx_EL1_TYPE_DATA_FAULT UL(0b101100) +#define BRBINFx_EL1_TYPE_IRQ UL(0b101110) +#define BRBINFx_EL1_TYPE_FIQ UL(0b101111) +#define BRBINFx_EL1_TYPE_DEBUG_EXIT UL(0b111001) + +#define BRBINFx_EL1_EL GENMASK(7, 6) +#define BRBINFx_EL1_EL_MASK GENMASK(7, 6) +#define BRBINFx_EL1_EL_SHIFT 6 +#define BRBINFx_EL1_EL_WIDTH 2 +#define BRBINFx_EL1_EL_EL0 UL(0b00) +#define BRBINFx_EL1_EL_EL1 UL(0b01) +#define BRBINFx_EL1_EL_EL2 UL(0b10) +#define BRBINFx_EL1_EL_EL3 UL(0b11) + +#define BRBINFx_EL1_MPRED GENMASK(5, 5) +#define BRBINFx_EL1_MPRED_MASK GENMASK(5, 5) +#define BRBINFx_EL1_MPRED_SHIFT 5 +#define BRBINFx_EL1_MPRED_WIDTH 1 + +#define BRBINFx_EL1_VALID GENMASK(1, 0) +#define BRBINFx_EL1_VALID_MASK GENMASK(1, 0) +#define BRBINFx_EL1_VALID_SHIFT 0 +#define BRBINFx_EL1_VALID_WIDTH 2 +#define BRBINFx_EL1_VALID_NONE UL(0b00) +#define BRBINFx_EL1_VALID_TARGET UL(0b01) +#define BRBINFx_EL1_VALID_SOURCE UL(0b10) +#define BRBINFx_EL1_VALID_FULL UL(0b11) + +#define BRBINFx_EL1_RES0 (UL(0) | GENMASK_ULL(63, 47) | \ + GENMASK_ULL(31, 18) | \ + GENMASK_ULL(15, 14) | \ + GENMASK_ULL(4, 2)) +#define BRBINFx_EL1_RES1 (UL(0)) +#define BRBINFx_EL1_UNKN (UL(0)) + +#define BRBCR_ELx_EXCEPTION GENMASK(23, 23) +#define BRBCR_ELx_EXCEPTION_MASK GENMASK(23, 23) +#define BRBCR_ELx_EXCEPTION_SHIFT 23 +#define BRBCR_ELx_EXCEPTION_WIDTH 1 + +#define BRBCR_ELx_ERTN GENMASK(22, 22) +#define BRBCR_ELx_ERTN_MASK GENMASK(22, 22) +#define BRBCR_ELx_ERTN_SHIFT 22 +#define BRBCR_ELx_ERTN_WIDTH 1 + +#define BRBCR_ELx_FZP GENMASK(8, 8) +#define BRBCR_ELx_FZP_MASK GENMASK(8, 8) +#define BRBCR_ELx_FZP_SHIFT 8 +#define BRBCR_ELx_FZP_WIDTH 1 + +#define BRBCR_ELx_TS GENMASK(6, 5) +#define BRBCR_ELx_TS_MASK GENMASK(6, 5) +#define BRBCR_ELx_TS_SHIFT 5 +#define BRBCR_ELx_TS_WIDTH 2 +#define BRBCR_ELx_TS_VIRTUAL UL(0b01) +#define BRBCR_ELx_TS_GUEST_PHYSICAL UL(0b10) +#define BRBCR_ELx_TS_PHYSICAL UL(0b11) + +#define BRBCR_ELx_MPRED GENMASK(4, 4) +#define BRBCR_ELx_MPRED_MASK GENMASK(4, 4) +#define BRBCR_ELx_MPRED_SHIFT 4 +#define BRBCR_ELx_MPRED_WIDTH 1 + +#define BRBCR_ELx_CC GENMASK(3, 3) +#define BRBCR_ELx_CC_MASK GENMASK(3, 3) +#define BRBCR_ELx_CC_SHIFT 3 +#define BRBCR_ELx_CC_WIDTH 1 + +#define BRBCR_ELx_ExBRE GENMASK(1, 1) +#define BRBCR_ELx_ExBRE_MASK GENMASK(1, 1) +#define BRBCR_ELx_ExBRE_SHIFT 1 +#define BRBCR_ELx_ExBRE_WIDTH 1 + +#define BRBCR_ELx_E0BRE GENMASK(0, 0) +#define BRBCR_ELx_E0BRE_MASK GENMASK(0, 0) +#define BRBCR_ELx_E0BRE_SHIFT 0 +#define BRBCR_ELx_E0BRE_WIDTH 1 + +#define BRBCR_ELx_RES0 (UL(0) | GENMASK_ULL(63, 24) | \ + GENMASK_ULL(21, 9) | \ + GENMASK_ULL(7, 7) | \ + GENMASK_ULL(2, 2)) +#define BRBCR_ELx_RES1 (UL(0)) +#define BRBCR_ELx_UNKN (UL(0)) + +#define REG_BRBCR_EL2 S2_4_C9_C0_0 +#define SYS_BRBCR_EL2 sys_reg(2, 4, 9, 0, 0) +#define SYS_BRBCR_EL2_Op0 2 +#define SYS_BRBCR_EL2_Op1 4 +#define SYS_BRBCR_EL2_CRn 9 +#define SYS_BRBCR_EL2_CRm 0 +#define SYS_BRBCR_EL2_Op2 0 + +/* For BRBCR_EL2 fields see BRBCR_ELx */ + +#define REG_BRBCR_EL1 S2_1_C9_C0_0 +#define SYS_BRBCR_EL1 sys_reg(2, 1, 9, 0, 0) +#define SYS_BRBCR_EL1_Op0 2 +#define SYS_BRBCR_EL1_Op1 1 +#define SYS_BRBCR_EL1_CRn 9 +#define SYS_BRBCR_EL1_CRm 0 +#define SYS_BRBCR_EL1_Op2 0 + +/* For BRBCR_EL1 fields see BRBCR_ELx */ + +#define REG_BRBCR_EL12 S2_5_C9_C0_0 +#define SYS_BRBCR_EL12 sys_reg(2, 5, 9, 0, 0) +#define SYS_BRBCR_EL12_Op0 2 +#define SYS_BRBCR_EL12_Op1 5 +#define SYS_BRBCR_EL12_CRn 9 +#define SYS_BRBCR_EL12_CRm 0 +#define SYS_BRBCR_EL12_Op2 0 + +/* For BRBCR_EL12 fields see BRBCR_ELx */ + +#define REG_BRBFCR_EL1 S2_1_C9_C0_1 +#define SYS_BRBFCR_EL1 sys_reg(2, 1, 9, 0, 1) +#define SYS_BRBFCR_EL1_Op0 2 +#define SYS_BRBFCR_EL1_Op1 1 +#define SYS_BRBFCR_EL1_CRn 9 +#define SYS_BRBFCR_EL1_CRm 0 +#define SYS_BRBFCR_EL1_Op2 1 + +#define BRBFCR_EL1_BANK GENMASK(29, 28) +#define BRBFCR_EL1_BANK_MASK GENMASK(29, 28) +#define BRBFCR_EL1_BANK_SHIFT 28 +#define BRBFCR_EL1_BANK_WIDTH 2 +#define BRBFCR_EL1_BANK_FIRST UL(0b0) +#define BRBFCR_EL1_BANK_SECOND UL(0b1) + +#define BRBFCR_EL1_CONDDIR GENMASK(22, 22) +#define BRBFCR_EL1_CONDDIR_MASK GENMASK(22, 22) +#define BRBFCR_EL1_CONDDIR_SHIFT 22 +#define BRBFCR_EL1_CONDDIR_WIDTH 1 + +#define BRBFCR_EL1_DIRCALL GENMASK(21, 21) +#define BRBFCR_EL1_DIRCALL_MASK GENMASK(21, 21) +#define BRBFCR_EL1_DIRCALL_SHIFT 21 +#define BRBFCR_EL1_DIRCALL_WIDTH 1 + +#define BRBFCR_EL1_INDCALL GENMASK(20, 20) +#define BRBFCR_EL1_INDCALL_MASK GENMASK(20, 20) +#define BRBFCR_EL1_INDCALL_SHIFT 20 +#define BRBFCR_EL1_INDCALL_WIDTH 1 + +#define BRBFCR_EL1_RTN GENMASK(19, 19) +#define BRBFCR_EL1_RTN_MASK GENMASK(19, 19) +#define BRBFCR_EL1_RTN_SHIFT 19 +#define BRBFCR_EL1_RTN_WIDTH 1 + +#define BRBFCR_EL1_INDIRECT GENMASK(18, 18) +#define BRBFCR_EL1_INDIRECT_MASK GENMASK(18, 18) +#define BRBFCR_EL1_INDIRECT_SHIFT 18 +#define BRBFCR_EL1_INDIRECT_WIDTH 1 + +#define BRBFCR_EL1_DIRECT GENMASK(17, 17) +#define BRBFCR_EL1_DIRECT_MASK GENMASK(17, 17) +#define BRBFCR_EL1_DIRECT_SHIFT 17 +#define BRBFCR_EL1_DIRECT_WIDTH 1 + +#define BRBFCR_EL1_EnI GENMASK(16, 16) +#define BRBFCR_EL1_EnI_MASK GENMASK(16, 16) +#define BRBFCR_EL1_EnI_SHIFT 16 +#define BRBFCR_EL1_EnI_WIDTH 1 + +#define BRBFCR_EL1_PAUSED GENMASK(7, 7) +#define BRBFCR_EL1_PAUSED_MASK GENMASK(7, 7) +#define BRBFCR_EL1_PAUSED_SHIFT 7 +#define BRBFCR_EL1_PAUSED_WIDTH 1 + +#define BRBFCR_EL1_LASTFAILED GENMASK(6, 6) +#define BRBFCR_EL1_LASTFAILED_MASK GENMASK(6, 6) +#define BRBFCR_EL1_LASTFAILED_SHIFT 6 +#define BRBFCR_EL1_LASTFAILED_WIDTH 1 + +#define BRBFCR_EL1_RES0 (UL(0) | GENMASK_ULL(63, 30) | \ + GENMASK_ULL(27, 23) | \ + GENMASK_ULL(15, 8) | \ + GENMASK_ULL(5, 0)) +#define BRBFCR_EL1_RES1 (UL(0)) +#define BRBFCR_EL1_UNKN (UL(0)) + +#define REG_BRBTS_EL1 S2_1_C9_C0_2 +#define SYS_BRBTS_EL1 sys_reg(2, 1, 9, 0, 2) +#define SYS_BRBTS_EL1_Op0 2 +#define SYS_BRBTS_EL1_Op1 1 +#define SYS_BRBTS_EL1_CRn 9 +#define SYS_BRBTS_EL1_CRm 0 +#define SYS_BRBTS_EL1_Op2 2 + +#define BRBTS_EL1_TS GENMASK(63, 0) +#define BRBTS_EL1_TS_MASK GENMASK(63, 0) +#define BRBTS_EL1_TS_SHIFT 0 +#define BRBTS_EL1_TS_WIDTH 64 + +#define BRBTS_EL1_RES0 (UL(0)) +#define BRBTS_EL1_RES1 (UL(0)) +#define BRBTS_EL1_UNKN (UL(0)) + +#define REG_BRBINFINJ_EL1 S2_1_C9_C1_0 +#define SYS_BRBINFINJ_EL1 sys_reg(2, 1, 9, 1, 0) +#define SYS_BRBINFINJ_EL1_Op0 2 +#define SYS_BRBINFINJ_EL1_Op1 1 +#define SYS_BRBINFINJ_EL1_CRn 9 +#define SYS_BRBINFINJ_EL1_CRm 1 +#define SYS_BRBINFINJ_EL1_Op2 0 + +#define BRBINFINJ_EL1_CCU GENMASK(46, 46) +#define BRBINFINJ_EL1_CCU_MASK GENMASK(46, 46) +#define BRBINFINJ_EL1_CCU_SHIFT 46 +#define BRBINFINJ_EL1_CCU_WIDTH 1 + +#define BRBINFINJ_EL1_CC GENMASK(45, 32) +#define BRBINFINJ_EL1_CC_MASK GENMASK(45, 32) +#define BRBINFINJ_EL1_CC_SHIFT 32 +#define BRBINFINJ_EL1_CC_WIDTH 14 + +#define BRBINFINJ_EL1_LASTFAILED GENMASK(17, 17) +#define BRBINFINJ_EL1_LASTFAILED_MASK GENMASK(17, 17) +#define BRBINFINJ_EL1_LASTFAILED_SHIFT 17 +#define BRBINFINJ_EL1_LASTFAILED_WIDTH 1 + +#define BRBINFINJ_EL1_T GENMASK(16, 16) +#define BRBINFINJ_EL1_T_MASK GENMASK(16, 16) +#define BRBINFINJ_EL1_T_SHIFT 16 +#define BRBINFINJ_EL1_T_WIDTH 1 + +#define BRBINFINJ_EL1_TYPE GENMASK(13, 8) +#define BRBINFINJ_EL1_TYPE_MASK GENMASK(13, 8) +#define BRBINFINJ_EL1_TYPE_SHIFT 8 +#define BRBINFINJ_EL1_TYPE_WIDTH 6 +#define BRBINFINJ_EL1_TYPE_UNCOND_DIRECT UL(0b000000) +#define BRBINFINJ_EL1_TYPE_INDIRECT UL(0b000001) +#define BRBINFINJ_EL1_TYPE_DIRECT_LINK UL(0b000010) +#define BRBINFINJ_EL1_TYPE_INDIRECT_LINK UL(0b000011) +#define BRBINFINJ_EL1_TYPE_RET UL(0b000101) +#define BRBINFINJ_EL1_TYPE_ERET UL(0b000111) +#define BRBINFINJ_EL1_TYPE_COND_DIRECT UL(0b001000) +#define BRBINFINJ_EL1_TYPE_DEBUG_HALT UL(0b100001) +#define BRBINFINJ_EL1_TYPE_CALL UL(0b100010) +#define BRBINFINJ_EL1_TYPE_TRAP UL(0b100011) +#define BRBINFINJ_EL1_TYPE_SERROR UL(0b100100) +#define BRBINFINJ_EL1_TYPE_INSN_DEBUG UL(0b100110) +#define BRBINFINJ_EL1_TYPE_DATA_DEBUG UL(0b100111) +#define BRBINFINJ_EL1_TYPE_ALIGN_FAULT UL(0b101010) +#define BRBINFINJ_EL1_TYPE_INSN_FAULT UL(0b101011) +#define BRBINFINJ_EL1_TYPE_DATA_FAULT UL(0b101100) +#define BRBINFINJ_EL1_TYPE_IRQ UL(0b101110) +#define BRBINFINJ_EL1_TYPE_FIQ UL(0b101111) +#define BRBINFINJ_EL1_TYPE_DEBUG_EXIT UL(0b111001) + +#define BRBINFINJ_EL1_EL GENMASK(7, 6) +#define BRBINFINJ_EL1_EL_MASK GENMASK(7, 6) +#define BRBINFINJ_EL1_EL_SHIFT 6 +#define BRBINFINJ_EL1_EL_WIDTH 2 +#define BRBINFINJ_EL1_EL_EL0 UL(0b00) +#define BRBINFINJ_EL1_EL_EL1 UL(0b01) +#define BRBINFINJ_EL1_EL_EL2 UL(0b10) +#define BRBINFINJ_EL1_EL_EL3 UL(0b11) + +#define BRBINFINJ_EL1_MPRED GENMASK(5, 5) +#define BRBINFINJ_EL1_MPRED_MASK GENMASK(5, 5) +#define BRBINFINJ_EL1_MPRED_SHIFT 5 +#define BRBINFINJ_EL1_MPRED_WIDTH 1 + +#define BRBINFINJ_EL1_VALID GENMASK(1, 0) +#define BRBINFINJ_EL1_VALID_MASK GENMASK(1, 0) +#define BRBINFINJ_EL1_VALID_SHIFT 0 +#define BRBINFINJ_EL1_VALID_WIDTH 2 +#define BRBINFINJ_EL1_VALID_NONE UL(0b00) +#define BRBINFINJ_EL1_VALID_TARGET UL(0b01) +#define BRBINFINJ_EL1_VALID_SOURCE UL(0b10) +#define BRBINFINJ_EL1_VALID_FULL UL(0b11) + +#define BRBINFINJ_EL1_RES0 (UL(0) | GENMASK_ULL(63, 47) | \ + GENMASK_ULL(31, 18) | \ + GENMASK_ULL(15, 14) | \ + GENMASK_ULL(4, 2)) +#define BRBINFINJ_EL1_RES1 (UL(0)) +#define BRBINFINJ_EL1_UNKN (UL(0)) + +#define REG_BRBSRCINJ_EL1 S2_1_C9_C1_1 +#define SYS_BRBSRCINJ_EL1 sys_reg(2, 1, 9, 1, 1) +#define SYS_BRBSRCINJ_EL1_Op0 2 +#define SYS_BRBSRCINJ_EL1_Op1 1 +#define SYS_BRBSRCINJ_EL1_CRn 9 +#define SYS_BRBSRCINJ_EL1_CRm 1 +#define SYS_BRBSRCINJ_EL1_Op2 1 + +#define BRBSRCINJ_EL1_ADDRESS GENMASK(63, 0) +#define BRBSRCINJ_EL1_ADDRESS_MASK GENMASK(63, 0) +#define BRBSRCINJ_EL1_ADDRESS_SHIFT 0 +#define BRBSRCINJ_EL1_ADDRESS_WIDTH 64 + +#define BRBSRCINJ_EL1_RES0 (UL(0)) +#define BRBSRCINJ_EL1_RES1 (UL(0)) +#define BRBSRCINJ_EL1_UNKN (UL(0)) + +#define REG_BRBTGTINJ_EL1 S2_1_C9_C1_2 +#define SYS_BRBTGTINJ_EL1 sys_reg(2, 1, 9, 1, 2) +#define SYS_BRBTGTINJ_EL1_Op0 2 +#define SYS_BRBTGTINJ_EL1_Op1 1 +#define SYS_BRBTGTINJ_EL1_CRn 9 +#define SYS_BRBTGTINJ_EL1_CRm 1 +#define SYS_BRBTGTINJ_EL1_Op2 2 + +#define BRBTGTINJ_EL1_ADDRESS GENMASK(63, 0) +#define BRBTGTINJ_EL1_ADDRESS_MASK GENMASK(63, 0) +#define BRBTGTINJ_EL1_ADDRESS_SHIFT 0 +#define BRBTGTINJ_EL1_ADDRESS_WIDTH 64 + +#define BRBTGTINJ_EL1_RES0 (UL(0)) +#define BRBTGTINJ_EL1_RES1 (UL(0)) +#define BRBTGTINJ_EL1_UNKN (UL(0)) + +#define REG_BRBIDR0_EL1 S2_1_C9_C2_0 +#define SYS_BRBIDR0_EL1 sys_reg(2, 1, 9, 2, 0) +#define SYS_BRBIDR0_EL1_Op0 2 +#define SYS_BRBIDR0_EL1_Op1 1 +#define SYS_BRBIDR0_EL1_CRn 9 +#define SYS_BRBIDR0_EL1_CRm 2 +#define SYS_BRBIDR0_EL1_Op2 0 + +#define BRBIDR0_EL1_CC GENMASK(15, 12) +#define BRBIDR0_EL1_CC_MASK GENMASK(15, 12) +#define BRBIDR0_EL1_CC_SHIFT 12 +#define BRBIDR0_EL1_CC_WIDTH 4 +#define BRBIDR0_EL1_CC_20_BIT UL(0b101) + +#define BRBIDR0_EL1_FORMAT GENMASK(11, 8) +#define BRBIDR0_EL1_FORMAT_MASK GENMASK(11, 8) +#define BRBIDR0_EL1_FORMAT_SHIFT 8 +#define BRBIDR0_EL1_FORMAT_WIDTH 4 +#define BRBIDR0_EL1_FORMAT_0 UL(0b0) + +#define BRBIDR0_EL1_NUMREC GENMASK(7, 0) +#define BRBIDR0_EL1_NUMREC_MASK GENMASK(7, 0) +#define BRBIDR0_EL1_NUMREC_SHIFT 0 +#define BRBIDR0_EL1_NUMREC_WIDTH 8 +#define BRBIDR0_EL1_NUMREC_8 UL(0b0001000) +#define BRBIDR0_EL1_NUMREC_16 UL(0b0010000) +#define BRBIDR0_EL1_NUMREC_32 UL(0b0100000) +#define BRBIDR0_EL1_NUMREC_64 UL(0b1000000) + +#define BRBIDR0_EL1_RES0 (UL(0) | GENMASK_ULL(63, 16)) +#define BRBIDR0_EL1_RES1 (UL(0)) +#define BRBIDR0_EL1_UNKN (UL(0)) +/*** End of Branch Record Buffer Extension ***/ + /* Definitions for system register interface to AMU for ARMv8.4 onwards */ #define SYS_AM_EL0(crm, op2) sys_reg(3, 3, 13, (crm), (op2)) #define SYS_AMCR_EL0 SYS_AM_EL0(2, 0) @@ -963,6 +1456,7 @@ #define ID_AA64MMFR2_CNP_SHIFT 0 /* id_aa64dfr0 */ +#define ID_AA64DFR0_EL1_BRBE_SHIFT 52 #define ID_AA64DFR0_MTPMU_SHIFT 48 #define ID_AA64DFR0_TRBE_SHIFT 44 #define ID_AA64DFR0_TRACE_FILT_SHIFT 40 @@ -975,6 +1469,9 @@ #define ID_AA64DFR0_TRACEVER_SHIFT 4 #define ID_AA64DFR0_DEBUGVER_SHIFT 0 +#define ID_AA64DFR0_EL1_BRBE_NI 0x0 +#define ID_AA64DFR0_EL1_BRBE_IMP 0x1 +#define ID_AA64DFR0_EL1_BRBE_BRBE_V1P1 0x2 #define ID_AA64DFR0_PMUVER_8_0 0x1 #define ID_AA64DFR0_PMUVER_8_1 0x4 #define ID_AA64DFR0_PMUVER_8_4 0x5 From 417feb045c1d3ceb8d945d880122f88ef37dc9af Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:38 +0800 Subject: [PATCH 225/227] drivers: perf: arm_pmu: Add new sched_task() callback maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://lore.kernel.org/lkml/20230711082455.215983-2-anshuman.khandual@arm.com/ -------------------------------- This adds armpmu_sched_task(), as generic pmu's sched_task() override which in turn can utilize a new arm_pmu.sched_task() callback when available from the arm_pmu instance. This new callback will be used while enabling BRBE in ARMV8 PMUV3. Cc: Catalin Marinas Cc: Will Deacon Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Mark Rutland Signed-off-by: Anshuman Khandual Signed-off-by: Junhao He --- drivers/perf/arm_pmu.c | 9 +++++++++ include/linux/perf/arm_pmu.h | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 9b1d6e3abcdc1..f4e7e784f9031 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -518,6 +518,14 @@ static int armpmu_event_init(struct perf_event *event) return __hw_perf_event_init(event); } +static void armpmu_sched_task(struct perf_event_pmu_context *ctx, bool sched_in) +{ + struct arm_pmu *armpmu = to_arm_pmu(ctx->pmu); + + if (armpmu->sched_task) + armpmu->sched_task(ctx, sched_in); +} + static void armpmu_enable(struct pmu *pmu) { struct arm_pmu *armpmu = to_arm_pmu(pmu); @@ -868,6 +876,7 @@ static struct arm_pmu *__armpmu_alloc(gfp_t flags) } pmu->pmu = (struct pmu) { + .sched_task = armpmu_sched_task, .pmu_enable = armpmu_enable, .pmu_disable = armpmu_disable, .event_init = armpmu_event_init, diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index b00569a47cd82..2f6a6af7a7dbf 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -97,6 +97,7 @@ struct arm_pmu { void (*stop)(struct arm_pmu *); void (*reset)(void *); int (*map_event)(struct perf_event *event); + void (*sched_task)(struct perf_event_pmu_context *ctx, bool sched_in); int num_events; bool secure_access; /* 32-bit ARM only */ #define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40 From 9284c17021245cddd1ac8b643c8f57b5d3ae87fe Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:39 +0800 Subject: [PATCH 226/227] drivers: perf: arm_pmuv3: Enable branch stack sampling framework maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://lore.kernel.org/lkml/20230711082455.215983-6-anshuman.khandual@arm.com/ -------------------------------- Branch stack sampling support i.e capturing branch records during execution in core perf, rides along with normal HW events being scheduled on the PMU. This prepares ARMV8 PMU framework for branch stack support on relevant PMUs with required HW implementation. ARMV8 PMU hardware support for branch stack sampling is indicated via a new feature flag called 'has_branch_stack' that can be ascertained via probing. This modifies current gate in armpmu_event_init() which blocks branch stack sampling based perf events unconditionally. Instead allows such perf events getting initialized on supporting PMU hardware. Branch stack sampling is enabled and disabled along with regular PMU events the relevant hardware also needs to be driven in tandem. This adds required function callbacks in armv8pmu_branch_xxx() format, to drive the PMU branch stack hardware when supported. This also adds fallback stub definitions for these callbacks for PMUs which would not have required support. Finally this adds a new buffer i.e 'struct branch_records', which can hold captured branch records during PMU IRQ processing before being passed on to the perf ring buffer. These buffers are per cpu, and dynamically allocated only for supporting ARMV8 PMU. These buffers can hold 'MAX_BRANCH_RECORDS' branch record entries. This enables PERF_ATTACH_TASK_DATA for branch stack sampling perf events to make them hold context branch records in their task_ctx_data. This will get used to stash branch records that would have been lost when a given process schedules out after a short run on the CPU without an event overflow. Cc: Catalin Marinas Cc: Will Deacon Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Anshuman Khandual Signed-off-by: Junhao He --- arch/arm64/include/asm/perf_event.h | 44 ++++++++++++++ drivers/perf/arm_pmu.c | 19 +++++- drivers/perf/arm_pmuv3.c | 90 ++++++++++++++++++++++++++++- include/linux/perf/arm_pmu.h | 22 ++++++- 4 files changed, 171 insertions(+), 4 deletions(-) diff --git a/arch/arm64/include/asm/perf_event.h b/arch/arm64/include/asm/perf_event.h index eb7071c9eb343..0aa4dac0129aa 100644 --- a/arch/arm64/include/asm/perf_event.h +++ b/arch/arm64/include/asm/perf_event.h @@ -9,6 +9,10 @@ #include #include +struct pmu_hw_events; +struct arm_pmu; +struct perf_event; + #ifdef CONFIG_PERF_EVENTS struct pt_regs; extern unsigned long perf_instruction_pointer(struct pt_regs *regs); @@ -24,4 +28,44 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs); (regs)->pstate = PSR_MODE_EL1h; \ } + +static inline void armv8pmu_branch_reset(void) +{ +} + +static inline void armv8pmu_branch_probe(struct arm_pmu *arm_pmu) +{ +} + +static inline bool armv8pmu_branch_attr_valid(struct perf_event *event) +{ + return false; +} + +static inline void armv8pmu_branch_enable(struct perf_event *event) +{ +} + +static inline void armv8pmu_branch_disable(struct perf_event *event) +{ +} + +static inline void armv8pmu_branch_read(struct pmu_hw_events *cpuc, + struct perf_event *event) +{ +} + +static inline void armv8pmu_branch_save(struct arm_pmu *arm_pmu, void *ctx) +{ +} + +static inline int armv8pmu_task_ctx_cache_alloc(struct arm_pmu *arm_pmu) +{ + return 0; +} + +static inline void armv8pmu_task_ctx_cache_free(struct arm_pmu *arm_pmu) +{ +} + #endif diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index f4e7e784f9031..709cebc9bd9d9 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -313,6 +313,11 @@ armpmu_del(struct perf_event *event, int flags) struct hw_perf_event *hwc = &event->hw; int idx = hwc->idx; + WARN_ON_ONCE(!hw_events->brbe_users); + hw_events->brbe_users--; + if (!hw_events->brbe_users) + hw_events->brbe_context = NULL; + armpmu_stop(event, PERF_EF_UPDATE); hw_events->events[idx] = NULL; armpmu->clear_event_idx(hw_events, event); @@ -329,6 +334,13 @@ armpmu_add(struct perf_event *event, int flags) struct hw_perf_event *hwc = &event->hw; int idx; + if (event->ctx->task && hw_events->brbe_context != event->ctx) { + hw_events->brbe_context = event->ctx; + if (armpmu->branch_reset) + armpmu->branch_reset(); + } + hw_events->brbe_users++; + /* An event following a process won't be stopped earlier */ if (!cpumask_test_cpu(smp_processor_id(), &armpmu->supported_cpus)) return -ENOENT; @@ -508,8 +520,11 @@ static int armpmu_event_init(struct perf_event *event) !cpumask_test_cpu(event->cpu, &armpmu->supported_cpus)) return -ENOENT; - /* does not support taken branch sampling */ - if (has_branch_stack(event)) + /* + * Branch stack sampling events are allowed + * only on PMU which has required support. + */ + if (has_branch_stack(event) && !armpmu->has_branch_stack) return -EOPNOTSUPP; if (armpmu->map_event(event) == -ENOENT) diff --git a/drivers/perf/arm_pmuv3.c b/drivers/perf/arm_pmuv3.c index 317282b063c64..b3d14f46d0768 100644 --- a/drivers/perf/arm_pmuv3.c +++ b/drivers/perf/arm_pmuv3.c @@ -736,10 +736,16 @@ static void armv8pmu_enable_event(struct perf_event *event) * Enable counter */ armv8pmu_enable_event_counter(event); + + if (has_branch_stack(event)) + armv8pmu_branch_enable(event); } static void armv8pmu_disable_event(struct perf_event *event) { + if (has_branch_stack(event)) + armv8pmu_branch_disable(event); + /* * Disable counter */ @@ -824,6 +830,15 @@ static irqreturn_t armv8pmu_handle_irq(struct arm_pmu *cpu_pmu) perf_sample_data_init(&data, 0, hwc->last_period); if (!armpmu_event_set_period(event)) continue; + /* + * PMU IRQ should remain asserted until all branch records + * are captured and processed into struct perf_sample_data. + */ + if (has_branch_stack(event) && !WARN_ON(!cpuc->branches)) { + armv8pmu_branch_read(cpuc, event); + data.br_stack = &cpuc->branches->branch_stack; + data.sample_flags |= PERF_SAMPLE_BRANCH_STACK; + } /* * Perf event overflow will queue the processing of the event as @@ -923,6 +938,24 @@ static int armv8pmu_user_event_idx(struct perf_event *event) return event->hw.idx; } +static void armv8pmu_sched_task(struct perf_event_pmu_context *ctx, bool sched_in) +{ + struct arm_pmu *armpmu = to_arm_pmu(ctx->pmu); + void *task_ctx = ctx ? ctx->task_ctx_data : NULL; + + if (armpmu->has_branch_stack) { + /* Save branch records in task_ctx on sched out */ + if (task_ctx && !sched_in) { + armv8pmu_branch_save(armpmu, task_ctx); + return; + } + + /* Reset branch records on sched in */ + if (sched_in) + armv8pmu_branch_reset(); + } +} + /* * Add an event filter to a given event. */ @@ -993,6 +1026,9 @@ static void armv8pmu_reset(void *info) pmcr |= ARMV8_PMU_PMCR_LP; armv8pmu_pmcr_write(pmcr); + + if (cpu_pmu->has_branch_stack) + armv8pmu_branch_reset(); } static int __armv8_pmuv3_map_event(struct perf_event *event, @@ -1018,6 +1054,12 @@ static int __armv8_pmuv3_map_event(struct perf_event *event, if (hw_event_id == ARMV8_PMUV3_PERFCTR_CHAIN) return -EINVAL; + if (has_branch_stack(event)) { + event->attach_state |= PERF_ATTACH_TASK_DATA; + if (!armv8pmu_branch_attr_valid(event)) + return -EOPNOTSUPP; + } + if (armv8pmu_event_is_64bit(event)) event->hw.flags |= ARMPMU_EVT_64BIT; @@ -1126,8 +1168,38 @@ static void __armv8pmu_probe_pmu(void *info) cpu_pmu->reg_pmmir = read_pmmir(); else cpu_pmu->reg_pmmir = 0; + + armv8pmu_branch_probe(cpu_pmu); } +static int branch_records_alloc(struct arm_pmu *armpmu) +{ + struct branch_records __percpu *records; + int cpu; + + records = alloc_percpu_gfp(struct branch_records, GFP_KERNEL); + if (!records) + return -ENOMEM; + + /* + * percpu memory allocated for 'records' gets completely consumed + * here, and never required to be freed up later. So permanently + * losing access to this anchor i.e 'records' is acceptable. + * + * Otherwise this allocation handle would have to be saved up for + * free_percpu() release later if required. + */ + for_each_possible_cpu(cpu) { + struct pmu_hw_events *events_cpu; + struct branch_records *records_cpu; + + events_cpu = per_cpu_ptr(armpmu->hw_events, cpu); + records_cpu = per_cpu_ptr(records, cpu); + events_cpu->branches = records_cpu; + } + return 0; + } + static int armv8pmu_probe_pmu(struct arm_pmu *cpu_pmu) { struct armv8pmu_probe_info probe = { @@ -1142,7 +1214,21 @@ static int armv8pmu_probe_pmu(struct arm_pmu *cpu_pmu) if (ret) return ret; - return probe.present ? 0 : -ENODEV; + if (!probe.present) + return -ENODEV; + + if (cpu_pmu->has_branch_stack) { + ret = armv8pmu_task_ctx_cache_alloc(cpu_pmu); + if (ret) + return ret; + + ret = branch_records_alloc(cpu_pmu); + if (ret) { + armv8pmu_task_ctx_cache_free(cpu_pmu); + return ret; + } + } + return 0; } static void armv8pmu_disable_user_access_ipi(void *unused) @@ -1200,6 +1286,8 @@ static int armv8_pmu_init(struct arm_pmu *cpu_pmu, char *name, cpu_pmu->stop = armv8pmu_stop; cpu_pmu->reset = armv8pmu_reset; cpu_pmu->set_event_filter = armv8pmu_set_event_filter; + cpu_pmu->sched_task = armv8pmu_sched_task; + cpu_pmu->branch_reset = armv8pmu_branch_reset; cpu_pmu->pmu.event_idx = armv8pmu_user_event_idx; diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index 2f6a6af7a7dbf..ca92ea5984099 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -41,6 +41,18 @@ }, \ } +/* + * Maximum branch record entries which could be processed + * for core perf branch stack sampling support, regardless + * of the hardware support available on a given ARM PMU. + */ +#define MAX_BRANCH_RECORDS 64 + +struct branch_records { + struct perf_branch_stack branch_stack; + struct perf_branch_entry branch_entries[MAX_BRANCH_RECORDS]; +}; + /* The events for a given PMU register set. */ struct pmu_hw_events { /* @@ -67,6 +79,11 @@ struct pmu_hw_events { struct arm_pmu *percpu_pmu; int irq; + + struct branch_records *branches; + void *brbe_context; + unsigned int brbe_users; + unsigned long brbe_sample_type; }; enum armpmu_attr_groups { @@ -98,8 +115,11 @@ struct arm_pmu { void (*reset)(void *); int (*map_event)(struct perf_event *event); void (*sched_task)(struct perf_event_pmu_context *ctx, bool sched_in); + void (*branch_reset)(void); int num_events; - bool secure_access; /* 32-bit ARM only */ + unsigned int secure_access:1, /* 32-bit ARM only */ + has_branch_stack:1, /* 64-bit ARM only */ + reserved:30; #define ARMV8_PMUV3_MAX_COMMON_EVENTS 0x40 DECLARE_BITMAP(pmceid_bitmap, ARMV8_PMUV3_MAX_COMMON_EVENTS); #define ARMV8_PMUV3_EXT_COMMON_EVENT_BASE 0x4000 From de90963c93c0a2fc4538f2277cc2dd3ec5506cee Mon Sep 17 00:00:00 2001 From: Anshuman Khandual Date: Wed, 8 Nov 2023 10:38:40 +0800 Subject: [PATCH 227/227] drivers: perf: arm_pmuv3: Enable branch stack sampling via FEAT_BRBE maillist inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I8EC9K CVE: NA Reference: https://lore.kernel.org/lkml/20230711082455.215983-7-anshuman.khandual@arm.com/ -------------------------------- This extends recently added branch stack sampling framework in ARMV8 PMU to enable such events via new architecture feature called Branch Record Buffer Extension aka BRBE. This implements all the armv8pmu_branch_xxx() callbacks as expected at ARMV8 PMU level required to drive perf branch stack sampling events. This adds a new config option CONFIG_ARM64_BRBE to encapsulate this BRBE based implementation, available only on ARM64 platforms. BRBE hardware captures a branch record via three distinct system registers representing branch source address, branch target address, and other branch information. A BRBE buffer implementation is organized as multiple banks of 32 branch records each, which is a collection of BRBSRC_EL1, BRBTGT_EL1 and BRBINF_EL1 registers. Though total BRBE record entries i.e BRBE_MAX_ENTRIES cannot exceed MAX_BRANCH_RECORDS as defined for ARM PMU. BRBE hardware attributes get captured in a new reg_brbidr element in struct arm_pmu during armv8pmu_branch_probe() which is called from broader probing function __armv8pmu_probe_pmu(). Attributes such as number of branch record entries implemented in the hardware can be derived from armpmu->reg_brbidr. BRBE gets enabled via armv8pmu_branch_enable() where it also derives branch filter, and additional requirements from event's 'attr.branch_sample_type' and configures them via BRBFCR_EL1 and BRBCR_EL1 registers. PMU event overflow triggers IRQ, where current branch records get captured, stitched along with older records available in 'task_ctx', before getting processed for core perf ring buffer. Task context switch outs incrementally save current branch records in event's 'pmu_ctx->task_ctx_data' to optimize workload's branch record samples. In case multiple events with different branch sample type requests converge on the same PMU, BRBE gets enabled for branch filters for the last event's branch sample type. No branch records will be captured and processed for an event if BRBE hardware config does not match its branch sample type, while handling the PMU IRQ. Cc: Catalin Marinas Cc: Will Deacon Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Anshuman Khandual Signed-off-by: Junhao He --- arch/arm64/include/asm/el2_setup.h | 49 ++ arch/arm64/include/asm/perf_event.h | 14 +- arch/arm64/include/asm/sysreg.h | 8 + drivers/perf/Kconfig | 11 + drivers/perf/Makefile | 1 + drivers/perf/arm_brbe.c | 779 ++++++++++++++++++++++++++++ drivers/perf/arm_brbe.h | 258 +++++++++ drivers/perf/arm_pmu.c | 6 + include/linux/perf/arm_pmu.h | 5 + 9 files changed, 1130 insertions(+), 1 deletion(-) create mode 100644 drivers/perf/arm_brbe.c create mode 100644 drivers/perf/arm_brbe.h diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h index 7f3c87f7a0cec..a9f16e2e4a4e4 100644 --- a/arch/arm64/include/asm/el2_setup.h +++ b/arch/arm64/include/asm/el2_setup.h @@ -177,6 +177,52 @@ msr spsr_el2, x0 .endm +#ifdef CONFIG_ARM64_BRBE +/* + * Enable BRBE cycle count + * + * BRBE requires both BRBCR_EL1.CC and BRBCR_EL2.CC fields, be set + * for the cycle counts to be available in BRBINF_EL1.CC during + * branch record processing after a PMU interrupt. This enables CC + * field on both these registers while still executing inside EL2. + * + * BRBE driver would still be able to toggle branch records cycle + * count support via BRBCR_EL1.CC field regardless of whether the + * kernel ends up executing in EL1 or EL2. + */ +.macro __init_el2_brbe + mrs x1, id_aa64dfr0_el1 + ubfx x1, x1, #ID_AA64DFR0_EL1_BRBE_SHIFT, #4 + cbz x1, .Lskip_brbe_cc_\@ + + mrs_s x0, SYS_BRBCR_EL2 + orr x0, x0, BRBCR_ELx_CC + msr_s SYS_BRBCR_EL2, x0 + + /* + * Accessing BRBCR_EL1 register here does not require + * BRBCR_EL12 addressing mode as HCR_EL2.E2H is still + * clear. Regardless, check for HCR_E2H and be on the + * safer side. + */ + mrs x1, hcr_el2 + and x1, x1, #HCR_E2H + cbz x1, .Lset_brbe_el1_direct_\@ + + mrs_s x0, SYS_BRBCR_EL12 + orr x0, x0, BRBCR_ELx_CC + msr_s SYS_BRBCR_EL12, x0 + b .Lskip_brbe_cc_\@ + +.Lset_brbe_el1_direct_\@: + mrs_s x0, SYS_BRBCR_EL1 + orr x0, x0, BRBCR_ELx_CC + msr_s SYS_BRBCR_EL1, x0 +.Lskip_brbe_cc_\@: +.endm + +#endif + /** * Initialize EL2 registers to sane values. This should be called early on all * cores that were booted in EL2. Note that everything gets initialised as @@ -197,6 +243,9 @@ __init_el2_nvhe_cptr __init_el2_nvhe_sve __init_el2_fgt +#ifdef CONFIG_ARM64_BRBE + __init_el2_brbe +#endif __init_el2_nvhe_prepare_eret .endm diff --git a/arch/arm64/include/asm/perf_event.h b/arch/arm64/include/asm/perf_event.h index 0aa4dac0129aa..cb63bac99cfbe 100644 --- a/arch/arm64/include/asm/perf_event.h +++ b/arch/arm64/include/asm/perf_event.h @@ -28,7 +28,18 @@ extern unsigned long perf_misc_flags(struct pt_regs *regs); (regs)->pstate = PSR_MODE_EL1h; \ } - +#ifdef CONFIG_ARM64_BRBE +void armv8pmu_branch_reset(void); +void armv8pmu_branch_probe(struct arm_pmu *arm_pmu); +bool armv8pmu_branch_attr_valid(struct perf_event *event); +void armv8pmu_branch_enable(struct perf_event *event); +void armv8pmu_branch_disable(struct perf_event *event); +void armv8pmu_branch_read(struct pmu_hw_events *cpuc, + struct perf_event *event); +void armv8pmu_branch_save(struct arm_pmu *arm_pmu, void *ctx); +int armv8pmu_task_ctx_cache_alloc(struct arm_pmu *arm_pmu); +void armv8pmu_task_ctx_cache_free(struct arm_pmu *arm_pmu); +#else /* !CONFIG_ARM64_BRBE */ static inline void armv8pmu_branch_reset(void) { } @@ -68,4 +79,5 @@ static inline void armv8pmu_task_ctx_cache_free(struct arm_pmu *arm_pmu) { } +#endif /* CONFIG_ARM64_BRBE */ #endif diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index 995cb1eac11cf..70bbd78184fe6 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h @@ -117,6 +117,14 @@ #define SYS_DC_CISW sys_insn(1, 0, 7, 14, 2) #define SYS_DC_CIGSW sys_insn(1, 0, 7, 14, 4) #define SYS_DC_CIGDSW sys_insn(1, 0, 7, 14, 6) +#define SYS_BRB_IALL sys_insn(1, 1, 7, 2, 4) +#define SYS_BRB_INJ sys_insn(1, 1, 7, 2, 5) + +/* + * BRBE Instructions + */ +#define BRB_IALL_INSN __emit_inst(0xd5000000 | SYS_BRB_IALL | (0x1f)) +#define BRB_INJ_INSN __emit_inst(0xd5000000 | SYS_BRB_INJ | (0x1f)) /* * System registers, organised loosely by encoding but grouped together diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig index caf464df11187..8483df824dbf1 100644 --- a/drivers/perf/Kconfig +++ b/drivers/perf/Kconfig @@ -147,6 +147,17 @@ config ARM_DMC620_PMU Support for PMU events monitoring on the ARM DMC-620 memory controller. +config ARM64_BRBE + bool "Enable support for Branch Record Buffer Extension (BRBE)" + depends on PERF_EVENTS && ARM64 && ARM_PMU + default y + help + Enable perf support for Branch Record Buffer Extension (BRBE) which + records all branches taken in an execution path. This supports some + branch types and privilege based filtering. It captures additional + relevant information such as cycle count, misprediction and branch + type, branch privilege level etc. + source "drivers/perf/hisilicon/Kconfig" source "drivers/perf/arm_cspmu/Kconfig" diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile index 339fe96baa427..553928ec29ebf 100644 --- a/drivers/perf/Makefile +++ b/drivers/perf/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o obj-$(CONFIG_ARM_SPE_PMU) += arm_spe_pmu.o obj-$(CONFIG_ARM_DMC620_PMU) += arm_dmc620_pmu.o obj-$(CONFIG_ARM_CORESIGHT_PMU_ARCH_SYSTEM_PMU) += arm_cspmu/ +obj-$(CONFIG_ARM64_BRBE) += arm_brbe.o diff --git a/drivers/perf/arm_brbe.c b/drivers/perf/arm_brbe.c new file mode 100644 index 0000000000000..e04ff36a9c9fe --- /dev/null +++ b/drivers/perf/arm_brbe.c @@ -0,0 +1,779 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Branch Record Buffer Extension Driver. + * + * Copyright (C) 2022-2023 ARM Limited + * + * Author: Anshuman Khandual + */ +#include "arm_brbe.h" + +void armv8pmu_branch_reset(void) +{ + asm volatile(BRB_IALL_INSN); + isb(); +} + +static bool valid_brbe_nr(int brbe_nr) +{ + return brbe_nr == BRBIDR0_EL1_NUMREC_8 || + brbe_nr == BRBIDR0_EL1_NUMREC_16 || + brbe_nr == BRBIDR0_EL1_NUMREC_32 || + brbe_nr == BRBIDR0_EL1_NUMREC_64; +} + +static bool valid_brbe_cc(int brbe_cc) +{ + return brbe_cc == BRBIDR0_EL1_CC_20_BIT; +} + +static bool valid_brbe_format(int brbe_format) +{ + return brbe_format == BRBIDR0_EL1_FORMAT_0; +} + +static bool valid_brbe_version(int brbe_version) +{ + return brbe_version == ID_AA64DFR0_EL1_BRBE_IMP || + brbe_version == ID_AA64DFR0_EL1_BRBE_BRBE_V1P1; +} + +static void select_brbe_bank(int bank) +{ + u64 brbfcr; + + WARN_ON(bank > BRBE_BANK_IDX_1); + brbfcr = read_sysreg_s(SYS_BRBFCR_EL1); + brbfcr &= ~BRBFCR_EL1_BANK_MASK; + brbfcr |= SYS_FIELD_PREP(BRBFCR_EL1, BANK, bank); + write_sysreg_s(brbfcr, SYS_BRBFCR_EL1); + isb(); +} + +static bool __read_brbe_regset(struct brbe_regset *entry, int idx) +{ + entry->brbinf = get_brbinf_reg(idx); + + if (brbe_invalid(entry->brbinf)) + return false; + + entry->brbsrc = get_brbsrc_reg(idx); + entry->brbtgt = get_brbtgt_reg(idx); + return true; +} + +/* + * Read all BRBE entries in HW until the first invalid entry. + * + * The caller must ensure that the BRBE is not concurrently modifying these + * branch entries. + */ +static int capture_brbe_regset(struct brbe_regset *buf, int nr_hw_entries) +{ + int idx = 0; + + select_brbe_bank(BRBE_BANK_IDX_0); + while (idx < nr_hw_entries && idx <= BRBE_BANK0_IDX_MAX) { + if (!__read_brbe_regset(&buf[idx], idx)) + return idx; + idx++; + } + + select_brbe_bank(BRBE_BANK_IDX_1); + while (idx < nr_hw_entries && idx <= BRBE_BANK1_IDX_MAX) { + if (!__read_brbe_regset(&buf[idx], idx)) + return idx; + idx++; + } + return idx; +} + +/* + * This function concatenates branch records from stored and live buffer + * up to maximum nr_max records and the stored buffer holds the resultant + * buffer. The concatenated buffer contains all the branch records from + * the live buffer but might contain some from stored buffer considering + * the maximum combined length does not exceed 'nr_max'. + * + * Stored records Live records + * ------------------------------------------------^ + * | S0 | L0 | Newest | + * --------------------------------- | + * | S1 | L1 | | + * --------------------------------- | + * | S2 | L2 | | + * --------------------------------- | + * | S3 | L3 | | + * --------------------------------- | + * | S4 | L4 | nr_max + * --------------------------------- | + * | | L5 | | + * --------------------------------- | + * | | L6 | | + * --------------------------------- | + * | | L7 | | + * --------------------------------- | + * | | | | + * --------------------------------- | + * | | | Oldest | + * ------------------------------------------------V + * + * + * S0 is the newest in the stored records, where as L7 is the oldest in + * the live records. Unless the live buffer is detected as being full + * thus potentially dropping off some older records, L7 and S0 records + * are contiguous in time for a user task context. The stitched buffer + * here represents maximum possible branch records, contiguous in time. + * + * Stored records Live records + * ------------------------------------------------^ + * | L0 | L0 | Newest | + * --------------------------------- | + * | L0 | L1 | | + * --------------------------------- | + * | L2 | L2 | | + * --------------------------------- | + * | L3 | L3 | | + * --------------------------------- | + * | L4 | L4 | nr_max + * --------------------------------- | + * | L5 | L5 | | + * --------------------------------- | + * | L6 | L6 | | + * --------------------------------- | + * | L7 | L7 | | + * --------------------------------- | + * | S0 | | | + * --------------------------------- | + * | S1 | | Oldest | + * ------------------------------------------------V + * | S2 | <----| + * ----------------- | + * | S3 | <----| Dropped off after nr_max + * ----------------- | + * | S4 | <----| + * ----------------- + */ +static int stitch_stored_live_entries(struct brbe_regset *stored, + struct brbe_regset *live, + int nr_stored, int nr_live, + int nr_max) +{ + int nr_move = min(nr_stored, nr_max - nr_live); + + /* Move the tail of the buffer to make room for the new entries */ + memmove(&stored[nr_live], &stored[0], nr_move * sizeof(*stored)); + + /* Copy the new entries into the head of the buffer */ + memcpy(&stored[0], &live[0], nr_live * sizeof(*stored)); + + /* Return the number of entries in the stitched buffer */ + return min(nr_live + nr_stored, nr_max); +} + +static int brbe_branch_save(struct brbe_regset *live, int nr_hw_entries) +{ + u64 brbfcr = read_sysreg_s(SYS_BRBFCR_EL1); + int nr_live; + + write_sysreg_s(brbfcr | BRBFCR_EL1_PAUSED, SYS_BRBFCR_EL1); + isb(); + + nr_live = capture_brbe_regset(live, nr_hw_entries); + + write_sysreg_s(brbfcr & ~BRBFCR_EL1_PAUSED, SYS_BRBFCR_EL1); + isb(); + + return nr_live; +} + +void armv8pmu_branch_save(struct arm_pmu *arm_pmu, void *ctx) +{ + struct arm64_perf_task_context *task_ctx = ctx; + struct brbe_regset live[BRBE_MAX_ENTRIES]; + int nr_live, nr_store, nr_hw_entries; + + nr_hw_entries = brbe_get_numrec(arm_pmu->reg_brbidr); + nr_live = brbe_branch_save(live, nr_hw_entries); + nr_store = task_ctx->nr_brbe_records; + nr_store = stitch_stored_live_entries(task_ctx->store, live, nr_store, + nr_live, nr_hw_entries); + task_ctx->nr_brbe_records = nr_store; +} + +/* + * Generic perf branch filters supported on BRBE + * + * New branch filters need to be evaluated whether they could be supported on + * BRBE. This ensures that such branch filters would not just be accepted, to + * fail silently. PERF_SAMPLE_BRANCH_HV is a special case that is selectively + * supported only on platforms where kernel is in hyp mode. + */ +#define BRBE_EXCLUDE_BRANCH_FILTERS (PERF_SAMPLE_BRANCH_ABORT_TX | \ + PERF_SAMPLE_BRANCH_IN_TX | \ + PERF_SAMPLE_BRANCH_NO_TX | \ + PERF_SAMPLE_BRANCH_CALL_STACK) + +#define BRBE_ALLOWED_BRANCH_FILTERS (PERF_SAMPLE_BRANCH_USER | \ + PERF_SAMPLE_BRANCH_KERNEL | \ + PERF_SAMPLE_BRANCH_HV | \ + PERF_SAMPLE_BRANCH_ANY | \ + PERF_SAMPLE_BRANCH_ANY_CALL | \ + PERF_SAMPLE_BRANCH_ANY_RETURN | \ + PERF_SAMPLE_BRANCH_IND_CALL | \ + PERF_SAMPLE_BRANCH_COND | \ + PERF_SAMPLE_BRANCH_IND_JUMP | \ + PERF_SAMPLE_BRANCH_CALL | \ + PERF_SAMPLE_BRANCH_NO_FLAGS | \ + PERF_SAMPLE_BRANCH_NO_CYCLES | \ + PERF_SAMPLE_BRANCH_TYPE_SAVE | \ + PERF_SAMPLE_BRANCH_HW_INDEX | \ + PERF_SAMPLE_BRANCH_PRIV_SAVE) + +#define BRBE_PERF_BRANCH_FILTERS (BRBE_ALLOWED_BRANCH_FILTERS | \ + BRBE_EXCLUDE_BRANCH_FILTERS) + +bool armv8pmu_branch_attr_valid(struct perf_event *event) +{ + u64 branch_type = event->attr.branch_sample_type; + + /* + * Ensure both perf branch filter allowed and exclude + * masks are always in sync with the generic perf ABI. + */ + BUILD_BUG_ON(BRBE_PERF_BRANCH_FILTERS != (PERF_SAMPLE_BRANCH_MAX - 1)); + + if (branch_type & ~BRBE_ALLOWED_BRANCH_FILTERS) { + pr_debug_once("requested branch filter not supported 0x%llx\n", branch_type); + return false; + } + + /* + * If the event does not have at least one of the privilege + * branch filters as in PERF_SAMPLE_BRANCH_PLM_ALL, the core + * perf will adjust its value based on perf event's existing + * privilege level via attr.exclude_[user|kernel|hv]. + * + * As event->attr.branch_sample_type might have been changed + * when the event reaches here, it is not possible to figure + * out whether the event originally had HV privilege request + * or got added via the core perf. Just report this situation + * once and continue ignoring if there are other instances. + */ + if ((branch_type & PERF_SAMPLE_BRANCH_HV) && !is_kernel_in_hyp_mode()) + pr_debug_once("hypervisor privilege filter not supported 0x%llx\n", branch_type); + + return true; +} + +int armv8pmu_task_ctx_cache_alloc(struct arm_pmu *arm_pmu) +{ + size_t size = sizeof(struct arm64_perf_task_context); + + arm_pmu->pmu.task_ctx_cache = kmem_cache_create("arm64_brbe_task_ctx", size, 0, 0, NULL); + if (!arm_pmu->pmu.task_ctx_cache) + return -ENOMEM; + return 0; +} + +void armv8pmu_task_ctx_cache_free(struct arm_pmu *arm_pmu) +{ + kmem_cache_destroy(arm_pmu->pmu.task_ctx_cache); +} + +static int brbe_attributes_probe(struct arm_pmu *armpmu, u32 brbe) +{ + u64 brbidr = read_sysreg_s(SYS_BRBIDR0_EL1); + int brbe_version, brbe_format, brbe_cc, brbe_nr; + + brbe_version = brbe; + brbe_format = brbe_get_format(brbidr); + brbe_cc = brbe_get_cc_bits(brbidr); + brbe_nr = brbe_get_numrec(brbidr); + armpmu->reg_brbidr = brbidr; + + if (!valid_brbe_version(brbe_version) || + !valid_brbe_format(brbe_format) || + !valid_brbe_cc(brbe_cc) || + !valid_brbe_nr(brbe_nr)) + return -EOPNOTSUPP; + return 0; +} + +void armv8pmu_branch_probe(struct arm_pmu *armpmu) +{ + u64 aa64dfr0 = read_sysreg_s(SYS_ID_AA64DFR0_EL1); + u32 brbe; + + /* + * BRBE implementation's branch entries cannot exceed maximum + * branch records supported at the ARM PMU level abstraction. + * Otherwise there is always a possibility of array overflow, + * while processing BRBE branch records. + */ + BUILD_BUG_ON(BRBE_BANK_MAX_ENTRIES > MAX_BRANCH_RECORDS); + + brbe = cpuid_feature_extract_unsigned_field(aa64dfr0, ID_AA64DFR0_EL1_BRBE_SHIFT); + if (!brbe) + return; + + if (brbe_attributes_probe(armpmu, brbe)) + return; + + armpmu->has_branch_stack = 1; +} + +/* + * BRBE supports the following functional branch type filters while + * generating branch records. These branch filters can be enabled, + * either individually or as a group i.e ORing multiple filters + * with each other. + * + * BRBFCR_EL1_CONDDIR - Conditional direct branch + * BRBFCR_EL1_DIRCALL - Direct call + * BRBFCR_EL1_INDCALL - Indirect call + * BRBFCR_EL1_INDIRECT - Indirect branch + * BRBFCR_EL1_DIRECT - Direct branch + * BRBFCR_EL1_RTN - Subroutine return + */ +static u64 branch_type_to_brbfcr(int branch_type) +{ + u64 brbfcr = 0; + + if (branch_type & PERF_SAMPLE_BRANCH_ANY) { + brbfcr |= BRBFCR_EL1_BRANCH_FILTERS; + return brbfcr; + } + + if (branch_type & PERF_SAMPLE_BRANCH_ANY_CALL) { + brbfcr |= BRBFCR_EL1_INDCALL; + brbfcr |= BRBFCR_EL1_DIRCALL; + } + + if (branch_type & PERF_SAMPLE_BRANCH_ANY_RETURN) + brbfcr |= BRBFCR_EL1_RTN; + + if (branch_type & PERF_SAMPLE_BRANCH_IND_CALL) + brbfcr |= BRBFCR_EL1_INDCALL; + + if (branch_type & PERF_SAMPLE_BRANCH_COND) + brbfcr |= BRBFCR_EL1_CONDDIR; + + if (branch_type & PERF_SAMPLE_BRANCH_IND_JUMP) + brbfcr |= BRBFCR_EL1_INDIRECT; + + if (branch_type & PERF_SAMPLE_BRANCH_CALL) + brbfcr |= BRBFCR_EL1_DIRCALL; + + return brbfcr; +} + +/* + * BRBE supports the following privilege mode filters while generating + * branch records. + * + * BRBCR_ELx_E0BRE - EL0 branch records + * BRBCR_ELx_ExBRE - EL1/EL2 branch records + * + * BRBE also supports the following additional functional branch type + * filters while generating branch records. + * + * BRBCR_ELx_EXCEPTION - Exception + * BRBCR_ELx_ERTN - Exception return + */ +static u64 branch_type_to_brbcr(int branch_type) +{ + u64 brbcr = BRBCR_ELx_DEFAULT_TS; + + /* + * BRBE should be paused on PMU interrupt while tracing kernel + * space to stop capturing further branch records. Otherwise + * interrupt handler branch records might get into the samples + * which is not desired. + * + * BRBE need not be paused on PMU interrupt while tracing only + * the user space, because it will automatically be inside the + * prohibited region. But even after PMU overflow occurs, the + * interrupt could still take much more cycles, before it can + * be taken and by that time BRBE will have been overwritten. + * Hence enable pause on PMU interrupt mechanism even for user + * only traces as well. + */ + brbcr |= BRBCR_ELx_FZP; + + if (branch_type & PERF_SAMPLE_BRANCH_USER) + brbcr |= BRBCR_ELx_E0BRE; + + /* + * When running in the hyp mode, writing into BRBCR_EL1 + * actually writes into BRBCR_EL2 instead. Field E2BRE + * is also at the same position as E1BRE. + */ + if (branch_type & PERF_SAMPLE_BRANCH_KERNEL) + brbcr |= BRBCR_ELx_ExBRE; + + if (branch_type & PERF_SAMPLE_BRANCH_HV) { + if (is_kernel_in_hyp_mode()) + brbcr |= BRBCR_ELx_ExBRE; + } + + if (!(branch_type & PERF_SAMPLE_BRANCH_NO_CYCLES)) + brbcr |= BRBCR_ELx_CC; + + if (!(branch_type & PERF_SAMPLE_BRANCH_NO_FLAGS)) + brbcr |= BRBCR_ELx_MPRED; + + /* + * The exception and exception return branches could be + * captured, irrespective of the perf event's privilege. + * If the perf event does not have enough privilege for + * a given exception level, then addresses which falls + * under that exception level will be reported as zero + * for the captured branch record, creating source only + * or target only records. + */ + if (branch_type & PERF_SAMPLE_BRANCH_ANY) { + brbcr |= BRBCR_ELx_EXCEPTION; + brbcr |= BRBCR_ELx_ERTN; + } + + if (branch_type & PERF_SAMPLE_BRANCH_ANY_CALL) + brbcr |= BRBCR_ELx_EXCEPTION; + + if (branch_type & PERF_SAMPLE_BRANCH_ANY_RETURN) + brbcr |= BRBCR_ELx_ERTN; + + return brbcr & BRBCR_ELx_CONFIG_MASK; +} + +void armv8pmu_branch_enable(struct perf_event *event) +{ + struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); + struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); + u64 brbfcr, brbcr; + + if (!cpuc->brbe_users) + return; + + /* + * Skip enabling BRBE again with same filters and configs + */ + if (cpuc->brbe_sample_type == event->attr.branch_sample_type) + return; + + /* + * BRBE gets configured with a new mismatched branch sample + * type request, overriding any previous branch filters. + */ + cpuc->brbe_sample_type = event->attr.branch_sample_type; + brbfcr = read_sysreg_s(SYS_BRBFCR_EL1); + brbfcr &= ~BRBFCR_EL1_DEFAULT_CONFIG; + brbfcr |= branch_type_to_brbfcr(cpuc->brbe_sample_type); + write_sysreg_s(brbfcr, SYS_BRBFCR_EL1); + isb(); + + brbcr = read_sysreg_s(SYS_BRBCR_EL1); + brbcr &= ~BRBCR_ELx_CONFIG_MASK; + brbcr |= branch_type_to_brbcr(cpuc->brbe_sample_type); + write_sysreg_s(brbcr, SYS_BRBCR_EL1); + isb(); +} + +void armv8pmu_branch_disable(struct perf_event *event) +{ + struct arm_pmu *cpu_pmu = to_arm_pmu(event->pmu); + struct pmu_hw_events *cpuc = this_cpu_ptr(cpu_pmu->hw_events); + u64 brbfcr, brbcr; + + if (cpuc->brbe_users) + return; + + cpuc->brbe_sample_type = 0; + brbcr = read_sysreg_s(SYS_BRBCR_EL1); + brbfcr = read_sysreg_s(SYS_BRBFCR_EL1); + brbcr &= ~(BRBCR_ELx_E0BRE | BRBCR_ELx_ExBRE); + brbfcr |= BRBFCR_EL1_PAUSED; + write_sysreg_s(brbcr, SYS_BRBCR_EL1); + write_sysreg_s(brbfcr, SYS_BRBFCR_EL1); + isb(); +} + +static void brbe_set_perf_entry_type(struct perf_branch_entry *entry, u64 brbinf) +{ + int brbe_type = brbe_get_type(brbinf); + + switch (brbe_type) { + case BRBINFx_EL1_TYPE_UNCOND_DIRECT: + entry->type = PERF_BR_UNCOND; + break; + case BRBINFx_EL1_TYPE_INDIRECT: + entry->type = PERF_BR_IND; + break; + case BRBINFx_EL1_TYPE_DIRECT_LINK: + entry->type = PERF_BR_CALL; + break; + case BRBINFx_EL1_TYPE_INDIRECT_LINK: + entry->type = PERF_BR_IND_CALL; + break; + case BRBINFx_EL1_TYPE_RET: + entry->type = PERF_BR_RET; + break; + case BRBINFx_EL1_TYPE_COND_DIRECT: + entry->type = PERF_BR_COND; + break; + case BRBINFx_EL1_TYPE_CALL: + entry->type = PERF_BR_CALL; + break; + case BRBINFx_EL1_TYPE_TRAP: + entry->type = PERF_BR_SYSCALL; + break; + case BRBINFx_EL1_TYPE_ERET: + entry->type = PERF_BR_ERET; + break; + case BRBINFx_EL1_TYPE_IRQ: + entry->type = PERF_BR_IRQ; + break; + case BRBINFx_EL1_TYPE_DEBUG_HALT: + entry->type = PERF_BR_EXTEND_ABI; + entry->new_type = PERF_BR_ARM64_DEBUG_HALT; + break; + case BRBINFx_EL1_TYPE_SERROR: + entry->type = PERF_BR_SERROR; + break; + case BRBINFx_EL1_TYPE_INSN_DEBUG: + entry->type = PERF_BR_EXTEND_ABI; + entry->new_type = PERF_BR_ARM64_DEBUG_INST; + break; + case BRBINFx_EL1_TYPE_DATA_DEBUG: + entry->type = PERF_BR_EXTEND_ABI; + entry->new_type = PERF_BR_ARM64_DEBUG_DATA; + break; + case BRBINFx_EL1_TYPE_ALIGN_FAULT: + entry->type = PERF_BR_EXTEND_ABI; + entry->new_type = PERF_BR_NEW_FAULT_ALGN; + break; + case BRBINFx_EL1_TYPE_INSN_FAULT: + entry->type = PERF_BR_EXTEND_ABI; + entry->new_type = PERF_BR_NEW_FAULT_INST; + break; + case BRBINFx_EL1_TYPE_DATA_FAULT: + entry->type = PERF_BR_EXTEND_ABI; + entry->new_type = PERF_BR_NEW_FAULT_DATA; + break; + case BRBINFx_EL1_TYPE_FIQ: + entry->type = PERF_BR_EXTEND_ABI; + entry->new_type = PERF_BR_ARM64_FIQ; + break; + case BRBINFx_EL1_TYPE_DEBUG_EXIT: + entry->type = PERF_BR_EXTEND_ABI; + entry->new_type = PERF_BR_ARM64_DEBUG_EXIT; + break; + default: + pr_warn_once("%d - unknown branch type captured\n", brbe_type); + entry->type = PERF_BR_UNKNOWN; + break; + } +} + +static int brbe_get_perf_priv(u64 brbinf) +{ + int brbe_el = brbe_get_el(brbinf); + + switch (brbe_el) { + case BRBINFx_EL1_EL_EL0: + return PERF_BR_PRIV_USER; + case BRBINFx_EL1_EL_EL1: + return PERF_BR_PRIV_KERNEL; + case BRBINFx_EL1_EL_EL2: + if (is_kernel_in_hyp_mode()) + return PERF_BR_PRIV_KERNEL; + return PERF_BR_PRIV_HV; + default: + pr_warn_once("%d - unknown branch privilege captured\n", brbe_el); + return PERF_BR_PRIV_UNKNOWN; + } +} + +static void capture_brbe_flags(struct perf_branch_entry *entry, struct perf_event *event, + u64 brbinf) +{ + if (branch_sample_type(event)) + brbe_set_perf_entry_type(entry, brbinf); + + if (!branch_sample_no_cycles(event)) + entry->cycles = brbe_get_cycles(brbinf); + + if (!branch_sample_no_flags(event)) { + /* + * BRBINFx_EL1.LASTFAILED indicates that a TME transaction failed (or + * was cancelled) prior to this record, and some number of records + * prior to this one, may have been generated during an attempt to + * execute the transaction. + * + * We will remove such entries later in process_branch_aborts(). + */ + entry->abort = brbe_get_lastfailed(brbinf); + + /* + * All these information (i.e transaction state and mispredicts) + * are available for source only and complete branch records. + */ + if (brbe_record_is_complete(brbinf) || + brbe_record_is_source_only(brbinf)) { + entry->mispred = brbe_get_mispredict(brbinf); + entry->predicted = !entry->mispred; + entry->in_tx = brbe_get_in_tx(brbinf); + } + } + + if (branch_sample_priv(event)) { + /* + * All these information (i.e branch privilege level) are + * available for target only and complete branch records. + */ + if (brbe_record_is_complete(brbinf) || + brbe_record_is_target_only(brbinf)) + entry->priv = brbe_get_perf_priv(brbinf); + } +} + +/* + * A branch record with BRBINFx_EL1.LASTFAILED set, implies that all + * preceding consecutive branch records, that were in a transaction + * (i.e their BRBINFx_EL1.TX set) have been aborted. + * + * Similarly BRBFCR_EL1.LASTFAILED set, indicate that all preceding + * consecutive branch records up to the last record, which were in a + * transaction (i.e their BRBINFx_EL1.TX set) have been aborted. + * + * --------------------------------- ------------------- + * | 00 | BRBSRC | BRBTGT | BRBINF | | TX = 1 | LF = 0 | [TX success] + * --------------------------------- ------------------- + * | 01 | BRBSRC | BRBTGT | BRBINF | | TX = 1 | LF = 0 | [TX success] + * --------------------------------- ------------------- + * | 02 | BRBSRC | BRBTGT | BRBINF | | TX = 0 | LF = 0 | + * --------------------------------- ------------------- + * | 03 | BRBSRC | BRBTGT | BRBINF | | TX = 1 | LF = 0 | [TX failed] + * --------------------------------- ------------------- + * | 04 | BRBSRC | BRBTGT | BRBINF | | TX = 1 | LF = 0 | [TX failed] + * --------------------------------- ------------------- + * | 05 | BRBSRC | BRBTGT | BRBINF | | TX = 0 | LF = 1 | + * --------------------------------- ------------------- + * | .. | BRBSRC | BRBTGT | BRBINF | | TX = 0 | LF = 0 | + * --------------------------------- ------------------- + * | 61 | BRBSRC | BRBTGT | BRBINF | | TX = 1 | LF = 0 | [TX failed] + * --------------------------------- ------------------- + * | 62 | BRBSRC | BRBTGT | BRBINF | | TX = 1 | LF = 0 | [TX failed] + * --------------------------------- ------------------- + * | 63 | BRBSRC | BRBTGT | BRBINF | | TX = 1 | LF = 0 | [TX failed] + * --------------------------------- ------------------- + * + * BRBFCR_EL1.LASTFAILED == 1 + * + * BRBFCR_EL1.LASTFAILED fails all those consecutive, in transaction + * branches records near the end of the BRBE buffer. + * + * Architecture does not guarantee a non transaction (TX = 0) branch + * record between two different transactions. So it is possible that + * a subsequent lastfailed record (TX = 0, LF = 1) might erroneously + * mark more than required transactions as aborted. + */ +static void process_branch_aborts(struct pmu_hw_events *cpuc) +{ + u64 brbfcr = read_sysreg_s(SYS_BRBFCR_EL1); + bool lastfailed = !!(brbfcr & BRBFCR_EL1_LASTFAILED); + int idx = brbe_get_numrec(cpuc->percpu_pmu->reg_brbidr) - 1; + struct perf_branch_entry *entry; + + do { + entry = &cpuc->branches->branch_entries[idx]; + if (entry->in_tx) { + entry->abort = lastfailed; + } else { + lastfailed = entry->abort; + entry->abort = false; + } + } while (idx--, idx >= 0); +} + +static void brbe_regset_branch_entries(struct pmu_hw_events *cpuc, struct perf_event *event, + struct brbe_regset *regset, int idx) +{ + struct perf_branch_entry *entry = &cpuc->branches->branch_entries[idx]; + u64 brbinf = regset[idx].brbinf; + + perf_clear_branch_entry_bitfields(entry); + if (brbe_record_is_complete(brbinf)) { + entry->from = regset[idx].brbsrc; + entry->to = regset[idx].brbtgt; + } else if (brbe_record_is_source_only(brbinf)) { + entry->from = regset[idx].brbsrc; + entry->to = 0; + } else if (brbe_record_is_target_only(brbinf)) { + entry->from = 0; + entry->to = regset[idx].brbtgt; + } + capture_brbe_flags(entry, event, brbinf); +} + +static void process_branch_entries(struct pmu_hw_events *cpuc, struct perf_event *event, + struct brbe_regset *regset, int nr_regset) +{ + int idx; + + for (idx = 0; idx < nr_regset; idx++) + brbe_regset_branch_entries(cpuc, event, regset, idx); + + cpuc->branches->branch_stack.nr = nr_regset; + cpuc->branches->branch_stack.hw_idx = -1ULL; +} + +void armv8pmu_branch_read(struct pmu_hw_events *cpuc, struct perf_event *event) +{ + struct arm64_perf_task_context *task_ctx = event->pmu_ctx->task_ctx_data; + struct brbe_regset live[BRBE_MAX_ENTRIES]; + int nr_live, nr_store, nr_hw_entries; + u64 brbfcr, brbcr; + + brbcr = read_sysreg_s(SYS_BRBCR_EL1); + brbfcr = read_sysreg_s(SYS_BRBFCR_EL1); + + /* Ensure pause on PMU interrupt is enabled */ + WARN_ON_ONCE(!(brbcr & BRBCR_ELx_FZP)); + + /* Pause the buffer */ + write_sysreg_s(brbfcr | BRBFCR_EL1_PAUSED, SYS_BRBFCR_EL1); + isb(); + + /* + * Overflown event's branch_sample_type does not match the configured + * branch filters in the BRBE HW. So the captured branch records here + * cannot be co-related to the overflown event. Report to the user as + * if no branch records have been captured, and flush branch records. + */ + if ((cpuc->brbe_sample_type != event->attr.branch_sample_type) || + (event->ctx->task && cpuc->brbe_context != event->ctx)) { + cpuc->branches->branch_stack.nr = 0; + cpuc->branches->branch_stack.hw_idx = -1ULL; + goto unpause_reset; + } + + nr_hw_entries = brbe_get_numrec(cpuc->percpu_pmu->reg_brbidr); + nr_live = capture_brbe_regset(live, nr_hw_entries); + if (event->ctx->task) { + nr_store = task_ctx->nr_brbe_records; + nr_store = stitch_stored_live_entries(task_ctx->store, live, nr_store, + nr_live, nr_hw_entries); + process_branch_entries(cpuc, event, task_ctx->store, nr_store); + task_ctx->nr_brbe_records = 0; + } else { + process_branch_entries(cpuc, event, live, nr_live); + } + process_branch_aborts(cpuc); + +unpause_reset: + /* Unpause the buffer */ + write_sysreg_s(brbfcr & ~BRBFCR_EL1_PAUSED, SYS_BRBFCR_EL1); + isb(); + armv8pmu_branch_reset(); +} diff --git a/drivers/perf/arm_brbe.h b/drivers/perf/arm_brbe.h new file mode 100644 index 0000000000000..5ba6cf1b0099d --- /dev/null +++ b/drivers/perf/arm_brbe.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Branch Record Buffer Extension Helpers. + * + * Copyright (C) 2022-2023 ARM Limited + * + * Author: Anshuman Khandual + */ +#define pr_fmt(fmt) "brbe: " fmt + +#include + +#define BRBFCR_EL1_BRANCH_FILTERS (BRBFCR_EL1_DIRECT | \ + BRBFCR_EL1_INDIRECT | \ + BRBFCR_EL1_RTN | \ + BRBFCR_EL1_INDCALL | \ + BRBFCR_EL1_DIRCALL | \ + BRBFCR_EL1_CONDDIR) + +#define BRBFCR_EL1_DEFAULT_CONFIG (BRBFCR_EL1_BANK_MASK | \ + BRBFCR_EL1_PAUSED | \ + BRBFCR_EL1_EnI | \ + BRBFCR_EL1_BRANCH_FILTERS) + +/* + * BRBTS_EL1 is currently not used for branch stack implementation + * purpose but BRBCR_ELx.TS needs to have a valid value from all + * available options. BRBCR_ELx_TS_VIRTUAL is selected for this. + */ +#define BRBCR_ELx_DEFAULT_TS FIELD_PREP(BRBCR_ELx_TS_MASK, BRBCR_ELx_TS_VIRTUAL) + +#define BRBCR_ELx_CONFIG_MASK (BRBCR_ELx_EXCEPTION | \ + BRBCR_ELx_ERTN | \ + BRBCR_ELx_CC | \ + BRBCR_ELx_MPRED | \ + BRBCR_ELx_ExBRE | \ + BRBCR_ELx_E0BRE | \ + BRBCR_ELx_FZP | \ + BRBCR_ELx_TS_MASK) +/* + * BRBE Buffer Organization + * + * BRBE buffer is arranged as multiple banks of 32 branch record + * entries each. An individual branch record in a given bank could + * be accessed, after selecting the bank in BRBFCR_EL1.BANK and + * accessing the registers i.e [BRBSRC, BRBTGT, BRBINF] set with + * indices [0..31]. + * + * Bank 0 + * + * --------------------------------- ------ + * | 00 | BRBSRC | BRBTGT | BRBINF | | 00 | + * --------------------------------- ------ + * | 01 | BRBSRC | BRBTGT | BRBINF | | 01 | + * --------------------------------- ------ + * | .. | BRBSRC | BRBTGT | BRBINF | | .. | + * --------------------------------- ------ + * | 31 | BRBSRC | BRBTGT | BRBINF | | 31 | + * --------------------------------- ------ + * + * Bank 1 + * + * --------------------------------- ------ + * | 32 | BRBSRC | BRBTGT | BRBINF | | 00 | + * --------------------------------- ------ + * | 33 | BRBSRC | BRBTGT | BRBINF | | 01 | + * --------------------------------- ------ + * | .. | BRBSRC | BRBTGT | BRBINF | | .. | + * --------------------------------- ------ + * | 63 | BRBSRC | BRBTGT | BRBINF | | 31 | + * --------------------------------- ------ + */ +#define BRBE_BANK_MAX_ENTRIES 32 +#define BRBE_MAX_BANK 2 +#define BRBE_MAX_ENTRIES (BRBE_BANK_MAX_ENTRIES * BRBE_MAX_BANK) + +#define BRBE_BANK0_IDX_MIN 0 +#define BRBE_BANK0_IDX_MAX 31 +#define BRBE_BANK1_IDX_MIN 32 +#define BRBE_BANK1_IDX_MAX 63 + +struct brbe_regset { + unsigned long brbsrc; + unsigned long brbtgt; + unsigned long brbinf; +}; + +struct arm64_perf_task_context { + struct brbe_regset store[BRBE_MAX_ENTRIES]; + int nr_brbe_records; +}; + +struct brbe_hw_attr { + int brbe_version; + int brbe_cc; + int brbe_nr; + int brbe_format; +}; + +enum brbe_bank_idx { + BRBE_BANK_IDX_INVALID = -1, + BRBE_BANK_IDX_0, + BRBE_BANK_IDX_1, + BRBE_BANK_IDX_MAX +}; + +#define RETURN_READ_BRBSRCN(n) \ + read_sysreg_s(SYS_BRBSRC##n##_EL1) + +#define RETURN_READ_BRBTGTN(n) \ + read_sysreg_s(SYS_BRBTGT##n##_EL1) + +#define RETURN_READ_BRBINFN(n) \ + read_sysreg_s(SYS_BRBINF##n##_EL1) + +#define BRBE_REGN_SWITCH(x, case_macro) \ + do { \ + switch (x) { \ + case 0: return case_macro(0); break; \ + case 1: return case_macro(1); break; \ + case 2: return case_macro(2); break; \ + case 3: return case_macro(3); break; \ + case 4: return case_macro(4); break; \ + case 5: return case_macro(5); break; \ + case 6: return case_macro(6); break; \ + case 7: return case_macro(7); break; \ + case 8: return case_macro(8); break; \ + case 9: return case_macro(9); break; \ + case 10: return case_macro(10); break; \ + case 11: return case_macro(11); break; \ + case 12: return case_macro(12); break; \ + case 13: return case_macro(13); break; \ + case 14: return case_macro(14); break; \ + case 15: return case_macro(15); break; \ + case 16: return case_macro(16); break; \ + case 17: return case_macro(17); break; \ + case 18: return case_macro(18); break; \ + case 19: return case_macro(19); break; \ + case 20: return case_macro(20); break; \ + case 21: return case_macro(21); break; \ + case 22: return case_macro(22); break; \ + case 23: return case_macro(23); break; \ + case 24: return case_macro(24); break; \ + case 25: return case_macro(25); break; \ + case 26: return case_macro(26); break; \ + case 27: return case_macro(27); break; \ + case 28: return case_macro(28); break; \ + case 29: return case_macro(29); break; \ + case 30: return case_macro(30); break; \ + case 31: return case_macro(31); break; \ + default: \ + pr_warn("unknown register index\n"); \ + return -1; \ + } \ + } while (0) + +static inline int buffer_to_brbe_idx(int buffer_idx) +{ + return buffer_idx % BRBE_BANK_MAX_ENTRIES; +} + +static inline u64 get_brbsrc_reg(int buffer_idx) +{ + int brbe_idx = buffer_to_brbe_idx(buffer_idx); + + BRBE_REGN_SWITCH(brbe_idx, RETURN_READ_BRBSRCN); +} + +static inline u64 get_brbtgt_reg(int buffer_idx) +{ + int brbe_idx = buffer_to_brbe_idx(buffer_idx); + + BRBE_REGN_SWITCH(brbe_idx, RETURN_READ_BRBTGTN); +} + +static inline u64 get_brbinf_reg(int buffer_idx) +{ + int brbe_idx = buffer_to_brbe_idx(buffer_idx); + + BRBE_REGN_SWITCH(brbe_idx, RETURN_READ_BRBINFN); +} + +static inline u64 brbe_record_valid(u64 brbinf) +{ + return FIELD_GET(BRBINFx_EL1_VALID_MASK, brbinf); +} + +static inline bool brbe_invalid(u64 brbinf) +{ + return brbe_record_valid(brbinf) == BRBINFx_EL1_VALID_NONE; +} + +static inline bool brbe_record_is_complete(u64 brbinf) +{ + return brbe_record_valid(brbinf) == BRBINFx_EL1_VALID_FULL; +} + +static inline bool brbe_record_is_source_only(u64 brbinf) +{ + return brbe_record_valid(brbinf) == BRBINFx_EL1_VALID_SOURCE; +} + +static inline bool brbe_record_is_target_only(u64 brbinf) +{ + return brbe_record_valid(brbinf) == BRBINFx_EL1_VALID_TARGET; +} + +static inline int brbe_get_in_tx(u64 brbinf) +{ + return FIELD_GET(BRBINFx_EL1_T_MASK, brbinf); +} + +static inline int brbe_get_mispredict(u64 brbinf) +{ + return FIELD_GET(BRBINFx_EL1_MPRED_MASK, brbinf); +} + +static inline int brbe_get_lastfailed(u64 brbinf) +{ + return FIELD_GET(BRBINFx_EL1_LASTFAILED_MASK, brbinf); +} + +static inline int brbe_get_cycles(u64 brbinf) +{ + /* + * Captured cycle count is unknown and hence + * should not be passed on to the user space. + */ + if (brbinf & BRBINFx_EL1_CCU) + return 0; + + return FIELD_GET(BRBINFx_EL1_CC_MASK, brbinf); +} + +static inline int brbe_get_type(u64 brbinf) +{ + return FIELD_GET(BRBINFx_EL1_TYPE_MASK, brbinf); +} + +static inline int brbe_get_el(u64 brbinf) +{ + return FIELD_GET(BRBINFx_EL1_EL_MASK, brbinf); +} + +static inline int brbe_get_numrec(u64 brbidr) +{ + return FIELD_GET(BRBIDR0_EL1_NUMREC_MASK, brbidr); +} + +static inline int brbe_get_format(u64 brbidr) +{ + return FIELD_GET(BRBIDR0_EL1_FORMAT_MASK, brbidr); +} + +static inline int brbe_get_cc_bits(u64 brbidr) +{ + return FIELD_GET(BRBIDR0_EL1_CC_MASK, brbidr); +} diff --git a/drivers/perf/arm_pmu.c b/drivers/perf/arm_pmu.c index 709cebc9bd9d9..8b8b9b63e2c57 100644 --- a/drivers/perf/arm_pmu.c +++ b/drivers/perf/arm_pmu.c @@ -334,6 +334,12 @@ armpmu_add(struct perf_event *event, int flags) struct hw_perf_event *hwc = &event->hw; int idx; + /* + * Reset branch records buffer if a new task event gets + * scheduled on a PMU which might have existing records. + * Otherwise older branch records present in the buffer + * might leak into the new task event. + */ if (event->ctx->task && hw_events->brbe_context != event->ctx) { hw_events->brbe_context = event->ctx; if (armpmu->branch_reset) diff --git a/include/linux/perf/arm_pmu.h b/include/linux/perf/arm_pmu.h index ca92ea5984099..789f5d60691b0 100644 --- a/include/linux/perf/arm_pmu.h +++ b/include/linux/perf/arm_pmu.h @@ -133,6 +133,11 @@ struct arm_pmu { /* store the PMMIR_EL1 to expose slots */ u64 reg_pmmir; +#ifdef CONFIG_ARM64_BRBE + /* store the BRBIDR0_EL1 capturing attributes */ + u64 reg_brbidr; +#endif + /* Only to be used by ACPI probing code */ unsigned long acpi_cpuid; };