From 885d1c2a30b73e50d0bbc098ddd717e22ccff266 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 6 May 2025 09:35:29 -0700 Subject: [PATCH 01/16] platform/x86: ISST: Support SST-TF revision 2 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit SST-TF revision 2 supports a higher number of cores per bucket, as the current limit of 256 cores may be insufficient. To accommodate this, a new offset, "SST_TF_INFO-8," is introduced, allowing for a higher core count. Utilize this offset instead of the current "SST_TF_INFO-1" offset, based on SST-TF revision 2 or higher, and if there is a non-zero core count in any bucket. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20250506163531.1061185-2-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../intel/speed_select_if/isst_tpmi_core.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index 9978cdd19851..a52fc2f5ea89 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1328,9 +1328,14 @@ static int isst_if_get_tpmi_instance_count(void __user *argp) #define SST_TF_INFO_0_OFFSET 0 #define SST_TF_INFO_1_OFFSET 8 #define SST_TF_INFO_2_OFFSET 16 +#define SST_TF_INFO_8_OFFSET 64 +#define SST_TF_INFO_8_BUCKETS 3 #define SST_TF_MAX_LP_CLIP_RATIOS TRL_MAX_LEVELS +#define SST_TF_FEATURE_REV_START 4 +#define SST_TF_FEATURE_REV_WIDTH 8 + #define SST_TF_LP_CLIP_RATIO_0_START 16 #define SST_TF_LP_CLIP_RATIO_0_WIDTH 8 @@ -1340,10 +1345,14 @@ static int isst_if_get_tpmi_instance_count(void __user *argp) #define SST_TF_NUM_CORE_0_START 0 #define SST_TF_NUM_CORE_0_WIDTH 8 +#define SST_TF_NUM_MOD_0_START 0 +#define SST_TF_NUM_MOD_0_WIDTH 16 + static int isst_if_get_turbo_freq_info(void __user *argp) { static struct isst_turbo_freq_info turbo_freq; struct tpmi_per_power_domain_info *power_domain_info; + u8 feature_rev; int i, j; if (copy_from_user(&turbo_freq, argp, sizeof(turbo_freq))) @@ -1360,6 +1369,10 @@ static int isst_if_get_turbo_freq_info(void __user *argp) turbo_freq.max_trl_levels = TRL_MAX_LEVELS; turbo_freq.max_clip_freqs = SST_TF_MAX_LP_CLIP_RATIOS; + _read_tf_level_info("feature_rev", feature_rev, turbo_freq.level, + SST_TF_INFO_0_OFFSET, SST_TF_FEATURE_REV_START, + SST_TF_FEATURE_REV_WIDTH, SST_MUL_FACTOR_NONE); + for (i = 0; i < turbo_freq.max_clip_freqs; ++i) _read_tf_level_info("lp_clip*", turbo_freq.lp_clip_freq_mhz[i], turbo_freq.level, SST_TF_INFO_0_OFFSET, @@ -1376,12 +1389,32 @@ static int isst_if_get_turbo_freq_info(void __user *argp) SST_MUL_FACTOR_FREQ) } + if (feature_rev >= 2) { + bool has_tf_info_8 = false; + + for (i = 0; i < SST_TF_INFO_8_BUCKETS; ++i) { + _read_tf_level_info("bucket_*_mod_count", turbo_freq.bucket_core_counts[i], + turbo_freq.level, SST_TF_INFO_8_OFFSET, + SST_TF_NUM_MOD_0_WIDTH * i, SST_TF_NUM_MOD_0_WIDTH, + SST_MUL_FACTOR_NONE) + + if (turbo_freq.bucket_core_counts[i]) + has_tf_info_8 = true; + } + + if (has_tf_info_8) + goto done_core_count; + } + for (i = 0; i < TRL_MAX_BUCKETS; ++i) _read_tf_level_info("bucket_*_core_count", turbo_freq.bucket_core_counts[i], turbo_freq.level, SST_TF_INFO_1_OFFSET, SST_TF_NUM_CORE_0_WIDTH * i, SST_TF_NUM_CORE_0_WIDTH, SST_MUL_FACTOR_NONE) + +done_core_count: + if (copy_to_user(argp, &turbo_freq, sizeof(turbo_freq))) return -EFAULT; -- 2.51.0 From d6644d737bec473a38dbd44a71553cacd636a920 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 6 May 2025 09:35:30 -0700 Subject: [PATCH 02/16] platform/x86: ISST: Support SST-PP revision 2 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit SST PP revision 2 added fabric 1 P0, P1 and Pm frequencies. Export them by using a new IOCTL ISST_IF_GET_PERF_LEVEL_FABRIC_INFO. This IOCTL requires platforms with SST PP revision 2 or higher. To accommodate potential future increases in fabric count and avoid ABI changes, support is extended for up to 8 fabrics. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20250506163531.1061185-3-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../intel/speed_select_if/isst_tpmi_core.c | 66 +++++++++++++++++++ include/uapi/linux/isst_if.h | 26 ++++++++ 2 files changed, 92 insertions(+) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index a52fc2f5ea89..f56bb63f7947 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1016,6 +1016,7 @@ static int isst_if_set_perf_feature(void __user *argp) #define SST_PP_INFO_10_OFFSET 80 #define SST_PP_INFO_11_OFFSET 88 +#define SST_PP_INFO_12_OFFSET 96 #define SST_PP_P1_SSE_START 0 #define SST_PP_P1_SSE_WIDTH 8 @@ -1068,6 +1069,15 @@ static int isst_if_set_perf_feature(void __user *argp) #define SST_PP_CORE_RATIO_PM_FABRIC_START 48 #define SST_PP_CORE_RATIO_PM_FABRIC_WIDTH 8 +#define SST_PP_CORE_RATIO_P0_FABRIC_1_START 0 +#define SST_PP_CORE_RATIO_P0_FABRIC_1_WIDTH 8 + +#define SST_PP_CORE_RATIO_P1_FABRIC_1_START 8 +#define SST_PP_CORE_RATIO_P1_FABRIC_1_WIDTH 8 + +#define SST_PP_CORE_RATIO_PM_FABRIC_1_START 16 +#define SST_PP_CORE_RATIO_PM_FABRIC_1_WIDTH 8 + static int isst_if_get_perf_level_info(void __user *argp) { struct isst_perf_level_data_info perf_level; @@ -1167,6 +1177,59 @@ static int isst_if_get_perf_level_info(void __user *argp) return 0; } +static int isst_if_get_perf_level_fabric_info(void __user *argp) +{ + struct isst_perf_level_fabric_info perf_level_fabric; + struct tpmi_per_power_domain_info *power_domain_info; + int start = SST_PP_CORE_RATIO_P0_FABRIC_START; + int width = SST_PP_CORE_RATIO_P0_FABRIC_WIDTH; + int offset = SST_PP_INFO_11_OFFSET; + int i; + + if (copy_from_user(&perf_level_fabric, argp, sizeof(perf_level_fabric))) + return -EFAULT; + + power_domain_info = get_instance(perf_level_fabric.socket_id, + perf_level_fabric.power_domain_id); + if (!power_domain_info) + return -EINVAL; + + if (perf_level_fabric.level > power_domain_info->max_level) + return -EINVAL; + + if (power_domain_info->pp_header.feature_rev < 2) + return -EINVAL; + + if (!(power_domain_info->pp_header.level_en_mask & BIT(perf_level_fabric.level))) + return -EINVAL; + + /* For revision 2, maximum number of fabrics is 2 */ + perf_level_fabric.max_fabrics = 2; + + for (i = 0; i < perf_level_fabric.max_fabrics; i++) { + _read_pp_level_info("p0_fabric_freq_mhz", perf_level_fabric.p0_fabric_freq_mhz[i], + perf_level_fabric.level, offset, start, width, + SST_MUL_FACTOR_FREQ) + start += width; + + _read_pp_level_info("p1_fabric_freq_mhz", perf_level_fabric.p1_fabric_freq_mhz[i], + perf_level_fabric.level, offset, start, width, + SST_MUL_FACTOR_FREQ) + start += width; + + _read_pp_level_info("pm_fabric_freq_mhz", perf_level_fabric.pm_fabric_freq_mhz[i], + perf_level_fabric.level, offset, start, width, + SST_MUL_FACTOR_FREQ) + offset = SST_PP_INFO_12_OFFSET; + start = SST_PP_CORE_RATIO_P0_FABRIC_1_START; + } + + if (copy_to_user(argp, &perf_level_fabric, sizeof(perf_level_fabric))) + return -EFAULT; + + return 0; +} + #define SST_PP_FUSED_CORE_COUNT_START 0 #define SST_PP_FUSED_CORE_COUNT_WIDTH 8 @@ -1453,6 +1516,9 @@ static long isst_if_def_ioctl(struct file *file, unsigned int cmd, case ISST_IF_GET_PERF_LEVEL_INFO: ret = isst_if_get_perf_level_info(argp); break; + case ISST_IF_GET_PERF_LEVEL_FABRIC_INFO: + ret = isst_if_get_perf_level_fabric_info(argp); + break; case ISST_IF_GET_PERF_LEVEL_CPU_MASK: ret = isst_if_get_perf_level_mask(argp); break; diff --git a/include/uapi/linux/isst_if.h b/include/uapi/linux/isst_if.h index 0df1a1c3caf4..8197a4800604 100644 --- a/include/uapi/linux/isst_if.h +++ b/include/uapi/linux/isst_if.h @@ -375,6 +375,30 @@ struct isst_perf_level_data_info { __u16 trl_freq_mhz[TRL_MAX_LEVELS][TRL_MAX_BUCKETS]; }; +#define MAX_FABRIC_COUNT 8 + +/** + * struct isst_perf_level_fabric_info - Structure to get SST-PP fabric details + * @socket_id: Socket/package id + * @power_domain_id: Power Domain id + * @level: SST-PP level for which caller wants to get information + * @max_fabrics: Count of fabrics in resonse + * @p0_fabric_freq_mhz: Fabric (Uncore) maximum frequency + * @p1_fabric_freq_mhz: Fabric (Uncore) TDP frequency + * @pm_fabric_freq_mhz: Fabric (Uncore) minimum frequency + * + * Structure used to get information on frequencies for fabrics. + */ +struct isst_perf_level_fabric_info { + __u8 socket_id; + __u8 power_domain_id; + __u16 level; + __u16 max_fabrics; + __u16 p0_fabric_freq_mhz[MAX_FABRIC_COUNT]; + __u16 p1_fabric_freq_mhz[MAX_FABRIC_COUNT]; + __u16 pm_fabric_freq_mhz[MAX_FABRIC_COUNT]; +}; + /** * struct isst_perf_level_cpu_mask - Structure to get SST-PP level CPU mask * @socket_id: Socket/package id @@ -471,5 +495,7 @@ struct isst_turbo_freq_info { #define ISST_IF_GET_BASE_FREQ_INFO _IOR(ISST_IF_MAGIC, 14, struct isst_base_freq_info *) #define ISST_IF_GET_BASE_FREQ_CPU_MASK _IOR(ISST_IF_MAGIC, 15, struct isst_perf_level_cpu_mask *) #define ISST_IF_GET_TURBO_FREQ_INFO _IOR(ISST_IF_MAGIC, 16, struct isst_turbo_freq_info *) +#define ISST_IF_GET_PERF_LEVEL_FABRIC_INFO _IOR(ISST_IF_MAGIC, 17,\ + struct isst_perf_level_fabric_info *) #endif -- 2.51.0 From c9a20142d3420a5113f560e7dea711d2f9693117 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 6 May 2025 09:35:31 -0700 Subject: [PATCH 03/16] platform/x86: ISST: Update minor version MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Update SST minor version after supporting SST-PP and SST-TF version 2. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20250506163531.1061185-4-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index f56bb63f7947..f08d78536839 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -34,7 +34,7 @@ /* Supported SST hardware version by this driver */ #define ISST_MAJOR_VERSION 0 -#define ISST_MINOR_VERSION 1 +#define ISST_MINOR_VERSION 2 /* * Used to indicate if value read from MMIO needs to get multiplied @@ -380,7 +380,7 @@ static int sst_main(struct auxiliary_device *auxdev, struct tpmi_per_power_domai return -ENODEV; } - if (TPMI_MINOR_VERSION(pd_info->sst_header.interface_version) != ISST_MINOR_VERSION) + if (TPMI_MINOR_VERSION(pd_info->sst_header.interface_version) > ISST_MINOR_VERSION) dev_info(dev, "SST: Ignore: Unsupported minor version:%lx\n", TPMI_MINOR_VERSION(pd_info->sst_header.interface_version)); -- 2.51.0 From c935ddfe65da020f5896fa58746fc7a57a876df3 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 5 May 2025 13:27:22 -0700 Subject: [PATCH 04/16] platform/x86: ISST: Do Not Restore SST MSRs on CPU Online Operation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit During CPU offline/online operations, the hardware retains MSR settings. Even if all CPUs are offlined, the package does not lose its MSR settings. Therefore, it is unnecessary to restore MSRs which are modified during the online operation, and this extra step can be removed. Signed-off-by: Srinivas Pandruvada Link: https://lore.kernel.org/r/20250505202722.1048675-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../intel/speed_select_if/isst_if_common.c | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index 31239a93dd71..3c4198ce17f2 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -198,25 +198,6 @@ void isst_resume_common(void) } EXPORT_SYMBOL_GPL(isst_resume_common); -static void isst_restore_msr_local(int cpu) -{ - struct isst_cmd *sst_cmd; - int i; - - mutex_lock(&isst_hash_lock); - for (i = 0; i < ARRAY_SIZE(punit_msr_white_list); ++i) { - if (!punit_msr_white_list[i]) - break; - - hash_for_each_possible(isst_hash, sst_cmd, hnode, - punit_msr_white_list[i]) { - if (!sst_cmd->mbox_cmd_type && sst_cmd->cpu == cpu) - wrmsrl_safe(sst_cmd->cmd, sst_cmd->data); - } - } - mutex_unlock(&isst_hash_lock); -} - /** * isst_if_mbox_cmd_invalid() - Check invalid mailbox commands * @cmd: Pointer to the command structure to verify. @@ -434,8 +415,6 @@ static int isst_if_cpu_online(unsigned int cpu) set_punit_id: isst_cpu_info[cpu].punit_cpu_id = data; - isst_restore_msr_local(cpu); - return 0; } -- 2.51.0 From 9950f94e485908fc42e7bb2533aff13b7e97a36c Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Mon, 5 May 2025 16:25:58 +0100 Subject: [PATCH 05/16] platform/x86/sony-laptop: Remove unused sony laptop camera code MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit commit ba47652ba655 ("media: meye: remove this deprecated driver") removed the meye driver but left behind the code in sony-laptop.c which that driver used to call. Remove the sony_pic_camera_command() function, and the set of defines (SONY_PIC_COMMAND_*) in a header used for the interface and the static helpers it called. Cleanup remaining #defines. Signed-off-by: Dr. David Alan Gilbert Link: https://lore.kernel.org/r/20250505152558.40526-1-linux@treblig.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 1 - drivers/platform/x86/sony-laptop.c | 167 ----------------------------- include/linux/sony-laptop.h | 39 ------- 3 files changed, 207 deletions(-) delete mode 100644 include/linux/sony-laptop.h diff --git a/MAINTAINERS b/MAINTAINERS index 49adae693221..f9417b4e9baf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22597,7 +22597,6 @@ W: http://www.linux.it/~malattia/wiki/index.php/Sony_drivers F: Documentation/admin-guide/laptops/sony-laptop.rst F: drivers/char/sonypi.c F: drivers/platform/x86/sony-laptop.c -F: include/linux/sony-laptop.h SOPHGO DEVICETREES and DRIVERS M: Chen Wang diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 492c275ba9ac..56beebc38850 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -48,7 +48,6 @@ #include #include #include -#include #include #ifdef CONFIG_SONYPI_COMPAT #include @@ -3621,22 +3620,6 @@ static u8 sony_pic_call2(u8 dev, u8 fn) return v1; } -static u8 sony_pic_call3(u8 dev, u8 fn, u8 v) -{ - u8 v1; - - wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(dev, spic_dev.cur_ioport->io1.minimum + 4); - wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(fn, spic_dev.cur_ioport->io1.minimum); - wait_on_command(inb_p(spic_dev.cur_ioport->io1.minimum + 4) & 2, ITERATIONS_LONG); - outb(v, spic_dev.cur_ioport->io1.minimum); - v1 = inb_p(spic_dev.cur_ioport->io1.minimum); - dprintk("sony_pic_call3(0x%.2x - 0x%.2x - 0x%.2x): 0x%.4x\n", - dev, fn, v, v1); - return v1; -} - /* * minidrivers for SPIC models */ @@ -3724,156 +3707,6 @@ out: dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3); } -/* camera tests and poweron/poweroff */ -#define SONYPI_CAMERA_PICTURE 5 -#define SONYPI_CAMERA_CONTROL 0x10 - -#define SONYPI_CAMERA_BRIGHTNESS 0 -#define SONYPI_CAMERA_CONTRAST 1 -#define SONYPI_CAMERA_HUE 2 -#define SONYPI_CAMERA_COLOR 3 -#define SONYPI_CAMERA_SHARPNESS 4 - -#define SONYPI_CAMERA_EXPOSURE_MASK 0xC -#define SONYPI_CAMERA_WHITE_BALANCE_MASK 0x3 -#define SONYPI_CAMERA_PICTURE_MODE_MASK 0x30 -#define SONYPI_CAMERA_MUTE_MASK 0x40 - -/* the rest don't need a loop until not 0xff */ -#define SONYPI_CAMERA_AGC 6 -#define SONYPI_CAMERA_AGC_MASK 0x30 -#define SONYPI_CAMERA_SHUTTER_MASK 0x7 - -#define SONYPI_CAMERA_SHUTDOWN_REQUEST 7 -#define SONYPI_CAMERA_CONTROL 0x10 - -#define SONYPI_CAMERA_STATUS 7 -#define SONYPI_CAMERA_STATUS_READY 0x2 -#define SONYPI_CAMERA_STATUS_POSITION 0x4 - -#define SONYPI_DIRECTION_BACKWARDS 0x4 - -#define SONYPI_CAMERA_REVISION 8 -#define SONYPI_CAMERA_ROMVERSION 9 - -static int __sony_pic_camera_ready(void) -{ - u8 v; - - v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS); - return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY)); -} - -static int __sony_pic_camera_off(void) -{ - if (!camera) { - pr_warn("camera control not enabled\n"); - return -ENODEV; - } - - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, - SONYPI_CAMERA_MUTE_MASK), - ITERATIONS_SHORT); - - if (spic_dev.camera_power) { - sony_pic_call2(0x91, 0); - spic_dev.camera_power = 0; - } - return 0; -} - -static int __sony_pic_camera_on(void) -{ - int i, j, x; - - if (!camera) { - pr_warn("camera control not enabled\n"); - return -ENODEV; - } - - if (spic_dev.camera_power) - return 0; - - for (j = 5; j > 0; j--) { - - for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++) - msleep(10); - sony_pic_call1(0x93); - - for (i = 400; i > 0; i--) { - if (__sony_pic_camera_ready()) - break; - msleep(10); - } - if (i) - break; - } - - if (j == 0) { - pr_warn("failed to power on camera\n"); - return -ENODEV; - } - - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL, - 0x5a), - ITERATIONS_SHORT); - - spic_dev.camera_power = 1; - return 0; -} - -/* External camera command (exported to the motion eye v4l driver) */ -int sony_pic_camera_command(int command, u8 value) -{ - if (!camera) - return -EIO; - - mutex_lock(&spic_dev.lock); - - switch (command) { - case SONY_PIC_COMMAND_SETCAMERA: - if (value) - __sony_pic_camera_on(); - else - __sony_pic_camera_off(); - break; - case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS: - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value), - ITERATIONS_SHORT); - break; - case SONY_PIC_COMMAND_SETCAMERACONTRAST: - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value), - ITERATIONS_SHORT); - break; - case SONY_PIC_COMMAND_SETCAMERAHUE: - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value), - ITERATIONS_SHORT); - break; - case SONY_PIC_COMMAND_SETCAMERACOLOR: - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value), - ITERATIONS_SHORT); - break; - case SONY_PIC_COMMAND_SETCAMERASHARPNESS: - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value), - ITERATIONS_SHORT); - break; - case SONY_PIC_COMMAND_SETCAMERAPICTURE: - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value), - ITERATIONS_SHORT); - break; - case SONY_PIC_COMMAND_SETCAMERAAGC: - wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value), - ITERATIONS_SHORT); - break; - default: - pr_err("sony_pic_camera_command invalid: %d\n", command); - break; - } - mutex_unlock(&spic_dev.lock); - return 0; -} -EXPORT_SYMBOL(sony_pic_camera_command); - /* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */ static void __sony_pic_set_wwanpower(u8 state) { diff --git a/include/linux/sony-laptop.h b/include/linux/sony-laptop.h deleted file mode 100644 index 1e3c92feea6e..000000000000 --- a/include/linux/sony-laptop.h +++ /dev/null @@ -1,39 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _SONYLAPTOP_H_ -#define _SONYLAPTOP_H_ - -#include - -#ifdef __KERNEL__ - -/* used only for communication between v4l and sony-laptop */ - -#define SONY_PIC_COMMAND_GETCAMERA 1 /* obsolete */ -#define SONY_PIC_COMMAND_SETCAMERA 2 -#define SONY_PIC_COMMAND_GETCAMERABRIGHTNESS 3 /* obsolete */ -#define SONY_PIC_COMMAND_SETCAMERABRIGHTNESS 4 -#define SONY_PIC_COMMAND_GETCAMERACONTRAST 5 /* obsolete */ -#define SONY_PIC_COMMAND_SETCAMERACONTRAST 6 -#define SONY_PIC_COMMAND_GETCAMERAHUE 7 /* obsolete */ -#define SONY_PIC_COMMAND_SETCAMERAHUE 8 -#define SONY_PIC_COMMAND_GETCAMERACOLOR 9 /* obsolete */ -#define SONY_PIC_COMMAND_SETCAMERACOLOR 10 -#define SONY_PIC_COMMAND_GETCAMERASHARPNESS 11 /* obsolete */ -#define SONY_PIC_COMMAND_SETCAMERASHARPNESS 12 -#define SONY_PIC_COMMAND_GETCAMERAPICTURE 13 /* obsolete */ -#define SONY_PIC_COMMAND_SETCAMERAPICTURE 14 -#define SONY_PIC_COMMAND_GETCAMERAAGC 15 /* obsolete */ -#define SONY_PIC_COMMAND_SETCAMERAAGC 16 -#define SONY_PIC_COMMAND_GETCAMERADIRECTION 17 /* obsolete */ -#define SONY_PIC_COMMAND_GETCAMERAROMVERSION 18 /* obsolete */ -#define SONY_PIC_COMMAND_GETCAMERAREVISION 19 /* obsolete */ - -#if IS_ENABLED(CONFIG_SONY_LAPTOP) -int sony_pic_camera_command(int command, u8 value); -#else -static inline int sony_pic_camera_command(int command, u8 value) { return 0; } -#endif - -#endif /* __KERNEL__ */ - -#endif /* _SONYLAPTOP_H_ */ -- 2.51.0 From 3c415b1df95c06ae4f9bdb166541ab366b862cc2 Mon Sep 17 00:00:00 2001 From: Nitin Joshi Date: Tue, 6 May 2025 00:01:52 +0900 Subject: [PATCH 06/16] platform/x86: thinkpad-acpi: Add support for new hotkey for camera shutter switch MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit New Lenovo Thinkpad models, e.g. the 'X9-14 Gen 1' and 'X9-15 Gen 1' has new shortcut on F9 key i.e to switch camera shutter and it send a new 0x131b hkey event when F9 key is pressed. This commit adds support for new hkey 0x131b. Reviewed-by: Mark Pearson Reviewed-by: Hans de Goede Signed-off-by: Nitin Joshi Link: https://lore.kernel.org/r/20250505150152.27968-1-nitjoshi@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/thinkpad_acpi.c | 43 +++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 9bb191a32e80..bdd33671a79f 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -182,6 +182,7 @@ enum tpacpi_hkey_event_t { * directly in the sparse-keymap. */ TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */ + TP_HKEY_EV_CAMERASHUTTER_TOGGLE = 0x131b, /* Toggle Camera Shutter */ TP_HKEY_EV_DOUBLETAP_TOGGLE = 0x131c, /* Toggle trackpoint doubletap on/off */ TP_HKEY_EV_PROFILE_TOGGLE = 0x131f, /* Toggle platform profile in 2024 systems */ TP_HKEY_EV_PROFILE_TOGGLE2 = 0x1401, /* Toggle platform profile in 2025 + systems */ @@ -2250,6 +2251,25 @@ static void tpacpi_input_send_tabletsw(void) } } +#define GCES_NO_SHUTTER_DEVICE BIT(31) + +static int get_camera_shutter(void) +{ + acpi_handle gces_handle; + int output; + + if (ACPI_FAILURE(acpi_get_handle(hkey_handle, "GCES", &gces_handle))) + return -ENODEV; + + if (!acpi_evalf(gces_handle, &output, NULL, "dd", 0)) + return -EIO; + + if (output & GCES_NO_SHUTTER_DEVICE) + return -ENODEV; + + return output; +} + static bool tpacpi_input_send_key(const u32 hkey, bool *send_acpi_ev) { bool known_ev; @@ -3303,7 +3323,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) const struct key_entry *keymap; bool radiosw_state = false; bool tabletsw_state = false; - int hkeyv, res, status; + int hkeyv, res, status, camera_shutter_state; vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_HKEY, "initializing hotkey subdriver\n"); @@ -3467,6 +3487,12 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (res) return res; + camera_shutter_state = get_camera_shutter(); + if (camera_shutter_state >= 0) { + input_set_capability(tpacpi_inputdev, EV_SW, SW_CAMERA_LENS_COVER); + input_report_switch(tpacpi_inputdev, SW_CAMERA_LENS_COVER, camera_shutter_state); + } + if (tp_features.hotkey_wlsw) { input_set_capability(tpacpi_inputdev, EV_SW, SW_RFKILL_ALL); input_report_switch(tpacpi_inputdev, @@ -11161,6 +11187,8 @@ static struct platform_driver tpacpi_hwmon_pdriver = { */ static bool tpacpi_driver_event(const unsigned int hkey_event) { + int camera_shutter_state; + switch (hkey_event) { case TP_HKEY_EV_BRGHT_UP: case TP_HKEY_EV_BRGHT_DOWN: @@ -11236,6 +11264,19 @@ static bool tpacpi_driver_event(const unsigned int hkey_event) else dytc_control_amt(!dytc_amt_active); + return true; + case TP_HKEY_EV_CAMERASHUTTER_TOGGLE: + camera_shutter_state = get_camera_shutter(); + if (camera_shutter_state < 0) { + pr_err("Error retrieving camera shutter state after shutter event\n"); + return true; + } + mutex_lock(&tpacpi_inputdev_send_mutex); + + input_report_switch(tpacpi_inputdev, SW_CAMERA_LENS_COVER, camera_shutter_state); + input_sync(tpacpi_inputdev); + + mutex_unlock(&tpacpi_inputdev_send_mutex); return true; case TP_HKEY_EV_DOUBLETAP_TOGGLE: tp_features.trackpoint_doubletap = !tp_features.trackpoint_doubletap; -- 2.51.0 From 53eddae9af0c0b46f9c77a02d23c21c1aa824739 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 May 2025 20:47:32 +0200 Subject: [PATCH 07/16] platform/x86: int3472: Move common.h to public includes, symbols to INTEL_INT3472 MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Move the common.h header file to include/linux/platform_data/x86/int3472.h and add a "INTEL_INT3472" kernel-symbol-namespace to the exported symbols. This is a preparation patch for exporting some more symbols for re-use in the atomisp driver. Signed-off-by: Hans de Goede Reviewed-by: Sakari Ailus Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250507184737.154747-2-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 1 + drivers/platform/x86/intel/int3472/clk_and_regulator.c | 3 +-- drivers/platform/x86/intel/int3472/common.c | 9 ++++----- drivers/platform/x86/intel/int3472/discrete.c | 4 ++-- drivers/platform/x86/intel/int3472/discrete_quirks.c | 3 +-- drivers/platform/x86/intel/int3472/led.c | 2 +- drivers/platform/x86/intel/int3472/tps68470.c | 3 ++- .../linux/platform_data/x86/int3472.h | 10 +++++++--- 8 files changed, 19 insertions(+), 16 deletions(-) rename drivers/platform/x86/intel/int3472/common.h => include/linux/platform_data/x86/int3472.h (96%) diff --git a/MAINTAINERS b/MAINTAINERS index f9417b4e9baf..08a99fe91bc1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12241,6 +12241,7 @@ INTEL SKYLAKE INT3472 ACPI DEVICE DRIVER M: Daniel Scally S: Maintained F: drivers/platform/x86/intel/int3472/ +F: include/linux/platform_data/x86/int3472.h INTEL SPEED SELECT TECHNOLOGY M: Srinivas Pandruvada diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index c85cbfbc16c1..4d00494a7670 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -6,11 +6,10 @@ #include #include #include +#include #include #include -#include "common.h" - /* * 82c0d13a-78c5-4244-9bb1-eb8b539a8d11 * This _DSM GUID allows controlling the sensor clk when it is not controlled diff --git a/drivers/platform/x86/intel/int3472/common.c b/drivers/platform/x86/intel/int3472/common.c index 1638be8fa71e..6dc38d5cbd0b 100644 --- a/drivers/platform/x86/intel/int3472/common.c +++ b/drivers/platform/x86/intel/int3472/common.c @@ -2,10 +2,9 @@ /* Author: Dan Scally */ #include +#include #include -#include "common.h" - union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *id) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -29,7 +28,7 @@ union acpi_object *skl_int3472_get_acpi_buffer(struct acpi_device *adev, char *i return obj; } -EXPORT_SYMBOL_GPL(skl_int3472_get_acpi_buffer); +EXPORT_SYMBOL_NS_GPL(skl_int3472_get_acpi_buffer, "INTEL_INT3472"); int skl_int3472_fill_cldb(struct acpi_device *adev, struct int3472_cldb *cldb) { @@ -53,7 +52,7 @@ out_free_obj: kfree(obj); return ret; } -EXPORT_SYMBOL_GPL(skl_int3472_fill_cldb); +EXPORT_SYMBOL_NS_GPL(skl_int3472_fill_cldb, "INTEL_INT3472"); /* sensor_adev_ret may be NULL, name_ret must not be NULL */ int skl_int3472_get_sensor_adev_and_name(struct device *dev, @@ -84,7 +83,7 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev, return ret; } -EXPORT_SYMBOL_GPL(skl_int3472_get_sensor_adev_and_name); +EXPORT_SYMBOL_NS_GPL(skl_int3472_get_sensor_adev_and_name, "INTEL_INT3472"); MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Device Driver library"); MODULE_AUTHOR("Daniel Scally "); diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 394975f55d64..d0938da0a591 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -12,12 +12,11 @@ #include #include #include +#include #include #include #include -#include "common.h" - /* * 79234640-9e10-4fea-a5c1-b5aa8b19756f * This _DSM GUID returns information about the GPIO lines mapped to a @@ -479,3 +478,4 @@ module_platform_driver(int3472_discrete); MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI Discrete Device Driver"); MODULE_AUTHOR("Daniel Scally "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("INTEL_INT3472"); diff --git a/drivers/platform/x86/intel/int3472/discrete_quirks.c b/drivers/platform/x86/intel/int3472/discrete_quirks.c index bf88863803b2..552869ef91ab 100644 --- a/drivers/platform/x86/intel/int3472/discrete_quirks.c +++ b/drivers/platform/x86/intel/int3472/discrete_quirks.c @@ -2,8 +2,7 @@ /* Author: Hans de Goede */ #include - -#include "common.h" +#include static const struct int3472_discrete_quirks lenovo_miix_510_quirks = { .avdd_second_sensor = "i2c-OVTI2680:00", diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index 9cbed694e2ca..c5588e143f7d 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -4,7 +4,7 @@ #include #include #include -#include "common.h" +#include static int int3472_pled_set(struct led_classdev *led_cdev, enum led_brightness brightness) diff --git a/drivers/platform/x86/intel/int3472/tps68470.c b/drivers/platform/x86/intel/int3472/tps68470.c index 81ac4c691963..0133405697dc 100644 --- a/drivers/platform/x86/intel/int3472/tps68470.c +++ b/drivers/platform/x86/intel/int3472/tps68470.c @@ -8,10 +8,10 @@ #include #include #include +#include #include #include -#include "common.h" #include "tps68470.h" #define DESIGNED_FOR_CHROMEOS 1 @@ -261,4 +261,5 @@ module_i2c_driver(int3472_tps68470); MODULE_DESCRIPTION("Intel SkyLake INT3472 ACPI TPS68470 Device Driver"); MODULE_AUTHOR("Daniel Scally "); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS("INTEL_INT3472"); MODULE_SOFTDEP("pre: clk-tps68470 tps68470-regulator"); diff --git a/drivers/platform/x86/intel/int3472/common.h b/include/linux/platform_data/x86/int3472.h similarity index 96% rename from drivers/platform/x86/intel/int3472/common.h rename to include/linux/platform_data/x86/int3472.h index 51b818e62a25..4cf02df6f753 100644 --- a/drivers/platform/x86/intel/int3472/common.h +++ b/include/linux/platform_data/x86/int3472.h @@ -1,8 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0 */ -/* Author: Dan Scally */ +/* + * Intel INT3472 ACPI camera sensor power-management support + * + * Author: Dan Scally + */ -#ifndef _INTEL_SKL_INT3472_H -#define _INTEL_SKL_INT3472_H +#ifndef __PLATFORM_DATA_X86_INT3472_H +#define __PLATFORM_DATA_X86_INT3472_H #include #include -- 2.51.0 From 1e5d088a52c207bcef6a43a6f6ffe162c514ed64 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 May 2025 20:47:33 +0200 Subject: [PATCH 08/16] platform/x86: int3472: Stop using devm_gpiod_get() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit The intent is to re-use the INT3472 code for parsing Intel camera sensor GPIOs and mapping them to the sensor for the atomisp driver, which currently has duplicate code. On atomisp devices there is no special INT3472 ACPI device, instead the Intel _DSM to get the GPIO type is part of the ACPI device for the sensor itself. To deal with this the mapping is done from ipu_bridge_init() instead of from a platform-device probe() function, there is no device to tie the lifetime of the gpiod_get() calls done by the INT3472 code to. Switch from devm_gpiod_get() to plain gpiod_get() + explicit gpiod_put() calls, to prepare for the code being re-used in the atomisp driver. Signed-off-by: Hans de Goede Reviewed-by: Sakari Ailus Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250507184737.154747-3-hdegoede@redhat.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/clk_and_regulator.c | 6 +++++- drivers/platform/x86/intel/int3472/discrete.c | 6 +++++- drivers/platform/x86/intel/int3472/led.c | 1 + include/linux/platform_data/x86/int3472.h | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index 4d00494a7670..476ec24d3702 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -182,6 +182,7 @@ void skl_int3472_unregister_clock(struct int3472_discrete_device *int3472) clkdev_drop(int3472->clock.cl); clk_unregister(int3472->clock.clk); + gpiod_put(int3472->clock.ena_gpio); } int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, @@ -244,12 +245,15 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, if (IS_ERR(regulator->rdev)) return PTR_ERR(regulator->rdev); + int3472->regulators[int3472->n_regulator_gpios].ena_gpio = gpio; int3472->n_regulator_gpios++; return 0; } void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472) { - for (int i = 0; i < int3472->n_regulator_gpios; i++) + for (int i = 0; i < int3472->n_regulator_gpios; i++) { regulator_unregister(int3472->regulators[i].rdev); + gpiod_put(int3472->regulators[i].ena_gpio); + } } diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index d0938da0a591..808d75e8deda 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -117,7 +117,7 @@ skl_int3472_gpiod_get_from_temp_lookup(struct int3472_discrete_device *int3472, return ERR_PTR(ret); gpiod_add_lookup_table(lookup); - desc = devm_gpiod_get(int3472->dev, con_id, GPIOD_OUT_LOW); + desc = gpiod_get(int3472->dev, con_id, GPIOD_OUT_LOW); gpiod_remove_lookup_table(lookup); return desc; @@ -340,6 +340,10 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, ret = -EINVAL; break; } + + if (ret) + gpiod_put(gpio); + break; default: dev_warn(int3472->dev, diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index c5588e143f7d..f1d6d7b0cb75 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -56,4 +56,5 @@ void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472) led_remove_lookup(&int3472->pled.lookup); led_classdev_unregister(&int3472->pled.classdev); + gpiod_put(int3472->pled.gpio); } diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index 4cf02df6f753..0a835cc85c67 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -99,6 +99,7 @@ struct int3472_gpio_regulator { struct regulator_consumer_supply supply_map[GPIO_REGULATOR_SUPPLY_MAP_COUNT * 2]; char supply_name_upper[GPIO_SUPPLY_NAME_LENGTH]; char regulator_name[GPIO_REGULATOR_NAME_LENGTH]; + struct gpio_desc *ena_gpio; struct regulator_dev *rdev; struct regulator_desc rdesc; }; -- 2.51.0 From 1cfa1bb9b4033e51d3c3f5ed5bf55475e57cc686 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 May 2025 20:47:34 +0200 Subject: [PATCH 09/16] platform/x86: int3472: Export int3472_discrete_parse_crs() MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit At the moment the atomisp has duplicate code for parsing Intel camera sensor GPIOS and calling the special 79234640-9e10-4fea-a5c1-b5aa8b19756f _DSM to get the GPIO type and map it to the sensor. Export int3472_discrete_parse_crs() so that the atomisp driver can reuse the INT3472 code for this. Signed-off-by: Hans de Goede Reviewed-by: Sakari Ailus Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250507184737.154747-4-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 15 ++++++++++----- include/linux/platform_data/x86/int3472.h | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 808d75e8deda..c706671e2f63 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -363,7 +363,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, return 1; } -static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) +int int3472_discrete_parse_crs(struct int3472_discrete_device *int3472) { LIST_HEAD(resource_list); int ret; @@ -388,17 +388,22 @@ static int skl_int3472_parse_crs(struct int3472_discrete_device *int3472) return 0; } +EXPORT_SYMBOL_NS_GPL(int3472_discrete_parse_crs, "INTEL_INT3472_DISCRETE"); -static void skl_int3472_discrete_remove(struct platform_device *pdev) +void int3472_discrete_cleanup(struct int3472_discrete_device *int3472) { - struct int3472_discrete_device *int3472 = platform_get_drvdata(pdev); - gpiod_remove_lookup_table(&int3472->gpios); skl_int3472_unregister_clock(int3472); skl_int3472_unregister_pled(int3472); skl_int3472_unregister_regulator(int3472); } +EXPORT_SYMBOL_NS_GPL(int3472_discrete_cleanup, "INTEL_INT3472_DISCRETE"); + +static void skl_int3472_discrete_remove(struct platform_device *pdev) +{ + int3472_discrete_cleanup(platform_get_drvdata(pdev)); +} static int skl_int3472_discrete_probe(struct platform_device *pdev) { @@ -453,7 +458,7 @@ static int skl_int3472_discrete_probe(struct platform_device *pdev) */ INIT_LIST_HEAD(&int3472->gpios.list); - ret = skl_int3472_parse_crs(int3472); + ret = int3472_discrete_parse_crs(int3472); if (ret) { skl_int3472_discrete_remove(pdev); return ret; diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index 0a835cc85c67..89410f0cb73a 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -147,6 +147,9 @@ int skl_int3472_get_sensor_adev_and_name(struct device *dev, struct acpi_device **sensor_adev_ret, const char **name_ret); +int int3472_discrete_parse_crs(struct int3472_discrete_device *int3472); +void int3472_discrete_cleanup(struct int3472_discrete_device *int3472); + int skl_int3472_register_gpio_clock(struct int3472_discrete_device *int3472, struct gpio_desc *gpio); int skl_int3472_register_dsm_clock(struct int3472_discrete_device *int3472); -- 2.51.0 From 45adb05473aa4afd6ff19fd0c46c17a6294ae788 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 May 2025 20:47:35 +0200 Subject: [PATCH 10/16] platform/x86: int3472: Remove unused sensor_config struct member MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit sensor_config is not used anywhere and its struct int3472_sensor_config type also is not declared anywhere, drop it. Signed-off-by: Hans de Goede Reviewed-by: Sakari Ailus Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250507184737.154747-5-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- include/linux/platform_data/x86/int3472.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index 89410f0cb73a..78276a11c48d 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -110,8 +110,6 @@ struct int3472_discrete_device { struct acpi_device *sensor; const char *sensor_name; - const struct int3472_sensor_config *sensor_config; - struct int3472_gpio_regulator regulators[INT3472_MAX_REGULATORS]; struct int3472_clock { -- 2.51.0 From d4860025a3fd9c20a801c2273227906874e76413 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 May 2025 20:47:36 +0200 Subject: [PATCH 11/16] platform/x86: int3472: For mt9m114 sensors map powerdown to powerenable MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit mt9m114 atomisp designs declare both reset and powerdown pins in their GPIO type DSM, but the mt9m114 only has a reset pin. The powerdown pin seems to control the regulators suppyling power to the sensor and the privacy LED. Add a mapping of powerdown to powerenable for the mt9m114 for this. While at is also add a comment to document the ov7251 mapping. Signed-off-by: Hans de Goede Reviewed-by: Sakari Ailus Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250507184737.154747-6-hdegoede@redhat.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index c706671e2f63..4c0aed6e626f 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -142,6 +142,9 @@ struct int3472_gpio_map { }; static const struct int3472_gpio_map int3472_gpio_map[] = { + /* mt9m114 designs declare a powerdown pin which controls the regulators */ + { "INT33F0", INT3472_GPIO_TYPE_POWERDOWN, INT3472_GPIO_TYPE_POWER_ENABLE, false, "vdd" }, + /* ov7251 driver / DT-bindings expect "enable" as con_id for reset */ { "INT347E", INT3472_GPIO_TYPE_RESET, INT3472_GPIO_TYPE_RESET, false, "enable" }, }; -- 2.51.0 From 83579675331059689e2869bf752ca9e17fadbd82 Mon Sep 17 00:00:00 2001 From: Yen-Chi Huang Date: Tue, 6 May 2025 18:23:14 +0800 Subject: [PATCH 12/16] platform/x86: portwell-ec: Add GPIO and WDT driver for Portwell EC MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Adds a driver for the ITE Embedded Controller (EC) on Portwell boards. It integrates with the Linux GPIO and watchdog subsystems to provide: - Control/monitoring of up to 8 EC GPIO pins. - Hardware watchdog timer with 1-255 second timeouts. The driver communicates with the EC via I/O port 0xe300 and identifies the hardware by the "PWG" firmware signature. This enables enhanced system management for Portwell embedded/industrial platforms. Signed-off-by: Yen-Chi Huang Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/a04be962-b207-4085-af5b-523f59bffcbc@portwell.com.tw Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 6 + drivers/platform/x86/Kconfig | 15 ++ drivers/platform/x86/Makefile | 3 + drivers/platform/x86/portwell-ec.c | 291 +++++++++++++++++++++++++++++ 4 files changed, 315 insertions(+) create mode 100644 drivers/platform/x86/portwell-ec.c diff --git a/MAINTAINERS b/MAINTAINERS index 08a99fe91bc1..cfb3b51cec83 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19216,6 +19216,12 @@ S: Maintained F: drivers/pnp/ F: include/linux/pnp.h +PORTWELL EC DRIVER +M: Yen-Chi Huang +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/portwell-ec.c + POSIX CLOCKS and TIMERS M: Anna-Maria Behnsen M: Frederic Weisbecker diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index f1c82729c4f1..e5cbd58a99f3 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -779,6 +779,21 @@ config PCENGINES_APU2 To compile this driver as a module, choose M here: the module will be called pcengines-apuv2. +config PORTWELL_EC + tristate "Portwell Embedded Controller driver" + depends on X86 && HAS_IOPORT && WATCHDOG && GPIOLIB + select WATCHDOG_CORE + help + This driver provides support for the GPIO pins and watchdog timer + embedded in Portwell's EC. + + Theoretically, this driver should work on multiple Portwell platforms, + but it has only been tested on the Portwell NANO-6064 board. + If you encounter any issues on other boards, please report them. + + To compile this driver as a module, choose M here: the module + will be called portwell-ec. + config BARCO_P50_GPIO tristate "Barco P50 GPIO driver for identify LED/button" depends on GPIOLIB diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index c2a07c7e614c..abbc2644ff6d 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -92,6 +92,9 @@ obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o # PC Engines obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o +# Portwell +obj-$(CONFIG_PORTWELL_EC) += portwell-ec.o + # Barco obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o diff --git a/drivers/platform/x86/portwell-ec.c b/drivers/platform/x86/portwell-ec.c new file mode 100644 index 000000000000..8b788822237b --- /dev/null +++ b/drivers/platform/x86/portwell-ec.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * portwell-ec.c: Portwell embedded controller driver. + * + * Tested on: + * - Portwell NANO-6064 + * + * This driver provides support for GPIO and Watchdog Timer + * functionalities of the Portwell boards with ITE embedded controller (EC). + * The EC is accessed through I/O ports and provides: + * - 8 GPIO pins for control and monitoring + * - Hardware watchdog with 1-15300 second timeout range + * + * It integrates with the Linux GPIO and Watchdog subsystems, allowing + * userspace interaction with EC GPIO pins and watchdog control, + * ensuring system stability and configurability. + * + * (C) Copyright 2025 Portwell, Inc. + * Author: Yen-Chi Huang (jesse.huang@portwell.com.tw) + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PORTWELL_EC_IOSPACE 0xe300 +#define PORTWELL_EC_IOSPACE_LEN SZ_256 + +#define PORTWELL_GPIO_PINS 8 +#define PORTWELL_GPIO_DIR_REG 0x2b +#define PORTWELL_GPIO_VAL_REG 0x2c + +#define PORTWELL_WDT_EC_CONFIG_ADDR 0x06 +#define PORTWELL_WDT_CONFIG_ENABLE 0x1 +#define PORTWELL_WDT_CONFIG_DISABLE 0x0 +#define PORTWELL_WDT_EC_COUNT_MIN_ADDR 0x07 +#define PORTWELL_WDT_EC_COUNT_SEC_ADDR 0x08 +#define PORTWELL_WDT_EC_MAX_COUNT_SECOND (255 * 60) + +#define PORTWELL_EC_FW_VENDOR_ADDRESS 0x4d +#define PORTWELL_EC_FW_VENDOR_LENGTH 3 +#define PORTWELL_EC_FW_VENDOR_NAME "PWG" + +static bool force; +module_param(force, bool, 0444); +MODULE_PARM_DESC(force, "Force loading EC driver without checking DMI boardname"); + +static const struct dmi_system_id pwec_dmi_table[] = { + { + .ident = "NANO-6064 series", + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NANO-6064"), + }, + }, + { } +}; +MODULE_DEVICE_TABLE(dmi, pwec_dmi_table); + +/* Functions for access EC via IOSPACE */ + +static void pwec_write(u8 index, u8 data) +{ + outb(data, PORTWELL_EC_IOSPACE + index); +} + +static u8 pwec_read(u8 address) +{ + return inb(PORTWELL_EC_IOSPACE + address); +} + +/* GPIO functions */ + +static int pwec_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + return pwec_read(PORTWELL_GPIO_VAL_REG) & BIT(offset) ? 1 : 0; +} + +static int pwec_gpio_set_rv(struct gpio_chip *chip, unsigned int offset, int val) +{ + u8 tmp = pwec_read(PORTWELL_GPIO_VAL_REG); + + if (val) + tmp |= BIT(offset); + else + tmp &= ~BIT(offset); + pwec_write(PORTWELL_GPIO_VAL_REG, tmp); + + return 0; +} + +static int pwec_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) +{ + u8 direction = pwec_read(PORTWELL_GPIO_DIR_REG) & BIT(offset); + + if (direction) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; +} + +/* + * Changing direction causes issues on some boards, + * so direction_input and direction_output are disabled for now. + */ + +static int pwec_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) +{ + return -EOPNOTSUPP; +} + +static int pwec_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int value) +{ + return -EOPNOTSUPP; +} + +static struct gpio_chip pwec_gpio_chip = { + .label = "portwell-ec-gpio", + .get_direction = pwec_gpio_get_direction, + .direction_input = pwec_gpio_direction_input, + .direction_output = pwec_gpio_direction_output, + .get = pwec_gpio_get, + .set_rv = pwec_gpio_set_rv, + .base = -1, + .ngpio = PORTWELL_GPIO_PINS, +}; + +/* Watchdog functions */ + +static void pwec_wdt_write_timeout(unsigned int timeout) +{ + pwec_write(PORTWELL_WDT_EC_COUNT_MIN_ADDR, timeout / 60); + pwec_write(PORTWELL_WDT_EC_COUNT_SEC_ADDR, timeout % 60); +} + +static int pwec_wdt_trigger(struct watchdog_device *wdd) +{ + pwec_wdt_write_timeout(wdd->timeout); + pwec_write(PORTWELL_WDT_EC_CONFIG_ADDR, PORTWELL_WDT_CONFIG_ENABLE); + + return 0; +} + +static int pwec_wdt_start(struct watchdog_device *wdd) +{ + return pwec_wdt_trigger(wdd); +} + +static int pwec_wdt_stop(struct watchdog_device *wdd) +{ + pwec_write(PORTWELL_WDT_EC_CONFIG_ADDR, PORTWELL_WDT_CONFIG_DISABLE); + return 0; +} + +static int pwec_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout) +{ + wdd->timeout = timeout; + pwec_wdt_write_timeout(wdd->timeout); + + return 0; +} + +/* Ensure consistent min/sec read in case of second rollover. */ +static unsigned int pwec_wdt_get_timeleft(struct watchdog_device *wdd) +{ + u8 sec, min, old_min; + + do { + old_min = pwec_read(PORTWELL_WDT_EC_COUNT_MIN_ADDR); + sec = pwec_read(PORTWELL_WDT_EC_COUNT_SEC_ADDR); + min = pwec_read(PORTWELL_WDT_EC_COUNT_MIN_ADDR); + } while (min != old_min); + + return min * 60 + sec; +} + +static const struct watchdog_ops pwec_wdt_ops = { + .owner = THIS_MODULE, + .start = pwec_wdt_start, + .stop = pwec_wdt_stop, + .ping = pwec_wdt_trigger, + .set_timeout = pwec_wdt_set_timeout, + .get_timeleft = pwec_wdt_get_timeleft, +}; + +static struct watchdog_device ec_wdt_dev = { + .info = &(struct watchdog_info){ + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, + .identity = "Portwell EC watchdog", + }, + .ops = &pwec_wdt_ops, + .timeout = 60, + .min_timeout = 1, + .max_timeout = PORTWELL_WDT_EC_MAX_COUNT_SECOND, +}; + +static int pwec_firmware_vendor_check(void) +{ + u8 buf[PORTWELL_EC_FW_VENDOR_LENGTH + 1]; + u8 i; + + for (i = 0; i < PORTWELL_EC_FW_VENDOR_LENGTH; i++) + buf[i] = pwec_read(PORTWELL_EC_FW_VENDOR_ADDRESS + i); + buf[PORTWELL_EC_FW_VENDOR_LENGTH] = '\0'; + + return !strcmp(PORTWELL_EC_FW_VENDOR_NAME, buf) ? 0 : -ENODEV; +} + +static int pwec_probe(struct platform_device *pdev) +{ + int ret; + + if (!devm_request_region(&pdev->dev, PORTWELL_EC_IOSPACE, + PORTWELL_EC_IOSPACE_LEN, dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "failed to get IO region\n"); + return -EBUSY; + } + + ret = pwec_firmware_vendor_check(); + if (ret < 0) + return ret; + + ret = devm_gpiochip_add_data(&pdev->dev, &pwec_gpio_chip, NULL); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register Portwell EC GPIO\n"); + return ret; + } + + ret = devm_watchdog_register_device(&pdev->dev, &ec_wdt_dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register Portwell EC Watchdog\n"); + return ret; + } + + return 0; +} + +static struct platform_driver pwec_driver = { + .driver = { + .name = "portwell-ec", + }, + .probe = pwec_probe, +}; + +static struct platform_device *pwec_dev; + +static int __init pwec_init(void) +{ + int ret; + + if (!dmi_check_system(pwec_dmi_table)) { + if (!force) + return -ENODEV; + pr_warn("force load portwell-ec without DMI check\n"); + } + + ret = platform_driver_register(&pwec_driver); + if (ret) + return ret; + + pwec_dev = platform_device_register_simple("portwell-ec", -1, NULL, 0); + if (IS_ERR(pwec_dev)) { + platform_driver_unregister(&pwec_driver); + return PTR_ERR(pwec_dev); + } + + return 0; +} + +static void __exit pwec_exit(void) +{ + platform_device_unregister(pwec_dev); + platform_driver_unregister(&pwec_driver); +} + +module_init(pwec_init); +module_exit(pwec_exit); + +MODULE_AUTHOR("Yen-Chi Huang "); +MODULE_DESCRIPTION("Portwell EC Driver"); +MODULE_LICENSE("GPL"); -- 2.51.0 From 8e725ff0419ad74dc79abfbdcd4cfba936a5d167 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Fri, 9 May 2025 11:58:01 +0100 Subject: [PATCH 13/16] platform: mellanox: nvsw-sn2200: Fix .items in nvsw_sn2201_busbar_hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Clang warns (or errors with CONFIG_WERROR=y): drivers/platform/mellanox/nvsw-sn2201.c:531:32: error: variable 'nvsw_sn2201_busbar_items' is not needed and will not be emitted [-Werror,-Wunneeded-internal-declaration] 531 | static struct mlxreg_core_item nvsw_sn2201_busbar_items[] = { | ^~~~~~~~~~~~~~~~~~~~~~~~ nvsw_sn2201_busbar_items is only used in ARRAY_SIZE(), which uses sizeof(), so this variable is only used at compile time. It appears that this may be a copy and paste issue, so use nvsw_sn2201_busbar_items as the .items member in nvsw_sn2201_busbar_hotplug, clearing up the warning. Fixes: 56b0bb7f9069 ("platform: mellanox: nvsw-sn2200: Add support for new system flavour") Signed-off-by: Nathan Chancellor Link: https://lore.kernel.org/r/20250509-nvsw-sn2200-fix-items-busbar-hotplug-v1-1-8844fff38dc8@kernel.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/nvsw-sn2201.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/mellanox/nvsw-sn2201.c b/drivers/platform/mellanox/nvsw-sn2201.c index 8c31189a94dd..db31c8bf2255 100644 --- a/drivers/platform/mellanox/nvsw-sn2201.c +++ b/drivers/platform/mellanox/nvsw-sn2201.c @@ -551,7 +551,7 @@ static struct mlxreg_core_item nvsw_sn2201_busbar_items[] = { static struct mlxreg_core_hotplug_platform_data nvsw_sn2201_busbar_hotplug = { - .items = nvsw_sn2201_items, + .items = nvsw_sn2201_busbar_items, .count = ARRAY_SIZE(nvsw_sn2201_busbar_items), .cell = NVSW_SN2201_SYS_INT_STATUS_OFFSET, .mask = NVSW_SN2201_CPLD_AGGR_BUSBAR_MASK_DEF, -- 2.51.0 From f94ffc3f0b90bf4880e0abf2c056fc465e2c3be8 Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Thu, 8 May 2025 23:31:39 +0300 Subject: [PATCH 14/16] platform/mellanox: mlxreg-dpu: Fix smatch warnings MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add missed call to release adapter. Remove wrong error pointer conversion. Fixes: 3e75f2954116 ("platform/mellanox: mlxreg-dpu: Add initial support for Nvidia DPU") Signed-off-by: Vadim Pasternak Link: https://lore.kernel.org/r/20250508203139.55171-1-vadimp@nvidia.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/mlxreg-dpu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/platform/mellanox/mlxreg-dpu.c b/drivers/platform/mellanox/mlxreg-dpu.c index 277e4b8cc5cb..52260106a9f1 100644 --- a/drivers/platform/mellanox/mlxreg-dpu.c +++ b/drivers/platform/mellanox/mlxreg-dpu.c @@ -535,8 +535,10 @@ static int mlxreg_dpu_probe(struct platform_device *pdev) return -EPROBE_DEFER; mlxreg_dpu = devm_kzalloc(&pdev->dev, sizeof(*mlxreg_dpu), GFP_KERNEL); - if (!mlxreg_dpu) - return -ENOMEM; + if (!mlxreg_dpu) { + err = -ENOMEM; + goto alloc_fail; + } /* Create device at the top of DPU I2C tree. */ data->hpdev.client = i2c_new_client_device(data->hpdev.adapter, @@ -562,7 +564,6 @@ static int mlxreg_dpu_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "Failed to sync regmap for client %s at bus %d at addr 0x%02x\n", data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); - err = PTR_ERR(regmap); goto regcache_sync_fail; } @@ -581,6 +582,7 @@ regcache_sync_fail: devm_regmap_init_i2c_fail: i2c_unregister_device(data->hpdev.client); i2c_new_device_fail: +alloc_fail: i2c_put_adapter(data->hpdev.adapter); return err; } -- 2.51.0 From cf8dea42e42b243e41878b57c0ecd898688234e6 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Tue, 6 May 2025 10:15:40 +0000 Subject: [PATCH 15/16] platform/x86/amd/hsmp: Use a single DRIVER_VERSION for all hsmp modules MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Use a single DRIVER_VERSION for the plat, hsmp and acpi modules, as all these modules are connected to a common functionality. Signed-off-by: Suma Hegde Reviewed-by: Naveen Krishna Chatradhi Link: https://lore.kernel.org/r/20250506101542.200811-1-suma.hegde@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp/acpi.c | 1 - drivers/platform/x86/amd/hsmp/hsmp.c | 2 -- drivers/platform/x86/amd/hsmp/hsmp.h | 2 ++ drivers/platform/x86/amd/hsmp/plat.c | 1 - 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index eaae044e4f82..12f4950afcd9 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -28,7 +28,6 @@ #include "hsmp.h" #define DRIVER_NAME "hsmp_acpi" -#define DRIVER_VERSION "2.3" /* These are the strings specified in ACPI table */ #define MSG_IDOFF_STR "MsgIdOffset" diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index a3ac09a90de4..3df34d7436a9 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -32,8 +32,6 @@ #define HSMP_WR true #define HSMP_RD false -#define DRIVER_VERSION "2.4" - /* * When same message numbers are used for both GET and SET operation, * bit:31 indicates whether its SET or GET operation. diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h index d58d4f0c20d5..7877cb97993b 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.h +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -25,6 +25,8 @@ #define HSMP_DEVNODE_NAME "hsmp" #define ACPI_HSMP_DEVICE_HID "AMDI0097" +#define DRIVER_VERSION "2.4" + struct hsmp_mbaddr_info { u32 base_addr; u32 msg_id_off; diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index 81931e808bbc..4f03fdf988c1 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -24,7 +24,6 @@ #include "hsmp.h" #define DRIVER_NAME "amd_hsmp" -#define DRIVER_VERSION "2.3" /* * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox -- 2.51.0 From 92c025db52bb94a032eb3d473bb81e62c19ddbd3 Mon Sep 17 00:00:00 2001 From: Suma Hegde Date: Tue, 6 May 2025 10:15:41 +0000 Subject: [PATCH 16/16] platform/x86/amd/hsmp: Report power via hwmon sensors MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Expose power reading and power limits via hwmon power sensors. Signed-off-by: Suma Hegde Reviewed-by: Naveen Krishna Chatradhi Link: https://lore.kernel.org/r/20250506101542.200811-2-suma.hegde@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/arch/x86/amd_hsmp.rst | 8 ++ drivers/platform/x86/amd/hsmp/Makefile | 1 + drivers/platform/x86/amd/hsmp/acpi.c | 4 + drivers/platform/x86/amd/hsmp/hsmp.h | 8 +- drivers/platform/x86/amd/hsmp/hwmon.c | 121 +++++++++++++++++++++++++ drivers/platform/x86/amd/hsmp/plat.c | 5 + 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/x86/amd/hsmp/hwmon.c diff --git a/Documentation/arch/x86/amd_hsmp.rst b/Documentation/arch/x86/amd_hsmp.rst index 2fd917638e42..3ef3e0a71df9 100644 --- a/Documentation/arch/x86/amd_hsmp.rst +++ b/Documentation/arch/x86/amd_hsmp.rst @@ -116,6 +116,14 @@ for socket with ID00 is given below:: }) } +HSMP HWMON interface +==================== +HSMP power sensors are registered with the hwmon interface. A separate hwmon +directory is created for each socket and the following files are generated +within the hwmon directory. +- power1_input (read only) +- power1_cap_max (read only) +- power1_cap (read, write) An example ========== diff --git a/drivers/platform/x86/amd/hsmp/Makefile b/drivers/platform/x86/amd/hsmp/Makefile index 0759bbcd13f6..ce8342e71f50 100644 --- a/drivers/platform/x86/amd/hsmp/Makefile +++ b/drivers/platform/x86/amd/hsmp/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_AMD_HSMP) += hsmp_common.o hsmp_common-y := hsmp.o +hsmp_common-$(CONFIG_HWMON) += hwmon.o obj-$(CONFIG_AMD_HSMP_PLAT) += amd_hsmp.o amd_hsmp-y := plat.o obj-$(CONFIG_AMD_HSMP_ACPI) += hsmp_acpi.o diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c index 12f4950afcd9..93b413e0a6e6 100644 --- a/drivers/platform/x86/amd/hsmp/acpi.c +++ b/drivers/platform/x86/amd/hsmp/acpi.c @@ -281,6 +281,10 @@ static int init_acpi(struct device *dev) dev_err(dev, "Failed to init metric table\n"); } + ret = hsmp_create_sensor(dev, sock_ind); + if (ret) + dev_err(dev, "Failed to register HSMP sensors with hwmon\n"); + return ret; } diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h index 7877cb97993b..d5729f318e52 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.h +++ b/drivers/platform/x86/amd/hsmp/hsmp.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -25,7 +26,7 @@ #define HSMP_DEVNODE_NAME "hsmp" #define ACPI_HSMP_DEVICE_HID "AMDI0097" -#define DRIVER_VERSION "2.4" +#define DRIVER_VERSION "2.5" struct hsmp_mbaddr_info { u32 base_addr; @@ -63,4 +64,9 @@ int hsmp_misc_register(struct device *dev); int hsmp_get_tbl_dram_base(u16 sock_ind); ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size); struct hsmp_plat_device *get_hsmp_pdev(void); +#if IS_REACHABLE(CONFIG_HWMON) +int hsmp_create_sensor(struct device *dev, u16 sock_ind); +#else +static inline int hsmp_create_sensor(struct device *dev, u16 sock_ind) { return 0; } +#endif #endif /* HSMP_H */ diff --git a/drivers/platform/x86/amd/hsmp/hwmon.c b/drivers/platform/x86/amd/hsmp/hwmon.c new file mode 100644 index 000000000000..7ffb61e0ef62 --- /dev/null +++ b/drivers/platform/x86/amd/hsmp/hwmon.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * AMD HSMP hwmon support + * Copyright (c) 2025, AMD. + * All Rights Reserved. + * + * This file provides hwmon implementation for HSMP interface. + */ + +#include + +#include +#include +#include +#include +#include + +#include "hsmp.h" + +#define HSMP_HWMON_NAME "amd_hsmp_hwmon" + +static int hsmp_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + u16 sock_ind = (uintptr_t)dev_get_drvdata(dev); + struct hsmp_message msg = {}; + + if (type != hwmon_power) + return -EOPNOTSUPP; + + if (attr != hwmon_power_cap) + return -EOPNOTSUPP; + + msg.num_args = 1; + msg.args[0] = val / MICROWATT_PER_MILLIWATT; + msg.msg_id = HSMP_SET_SOCKET_POWER_LIMIT; + msg.sock_ind = sock_ind; + return hsmp_send_message(&msg); +} + +static int hsmp_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + u16 sock_ind = (uintptr_t)dev_get_drvdata(dev); + struct hsmp_message msg = {}; + int ret; + + if (type != hwmon_power) + return -EOPNOTSUPP; + + msg.sock_ind = sock_ind; + msg.response_sz = 1; + + switch (attr) { + case hwmon_power_input: + msg.msg_id = HSMP_GET_SOCKET_POWER; + break; + case hwmon_power_cap: + msg.msg_id = HSMP_GET_SOCKET_POWER_LIMIT; + break; + case hwmon_power_cap_max: + msg.msg_id = HSMP_GET_SOCKET_POWER_LIMIT_MAX; + break; + default: + return -EOPNOTSUPP; + } + + ret = hsmp_send_message(&msg); + if (!ret) + *val = msg.args[0] * MICROWATT_PER_MILLIWATT; + + return ret; +} + +static umode_t hsmp_hwmon_is_visble(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_power) + return 0; + + switch (attr) { + case hwmon_power_input: + return 0444; + case hwmon_power_cap: + return 0644; + case hwmon_power_cap_max: + return 0444; + default: + return 0; + } +} + +static const struct hwmon_ops hsmp_hwmon_ops = { + .read = hsmp_hwmon_read, + .is_visible = hsmp_hwmon_is_visble, + .write = hsmp_hwmon_write, +}; + +static const struct hwmon_channel_info * const hsmp_info[] = { + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), + NULL +}; + +static const struct hwmon_chip_info hsmp_chip_info = { + .ops = &hsmp_hwmon_ops, + .info = hsmp_info, +}; + +int hsmp_create_sensor(struct device *dev, u16 sock_ind) +{ + struct device *hwmon_dev; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, HSMP_HWMON_NAME, + (void *)(uintptr_t)sock_ind, + &hsmp_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} +EXPORT_SYMBOL_NS(hsmp_create_sensor, "AMD_HSMP"); diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c index 4f03fdf988c1..0881d7e01936 100644 --- a/drivers/platform/x86/amd/hsmp/plat.c +++ b/drivers/platform/x86/amd/hsmp/plat.c @@ -189,6 +189,11 @@ static int init_platform_device(struct device *dev) if (ret) dev_err(dev, "Failed to init metric table\n"); } + + /* Register with hwmon interface for reporting power */ + ret = hsmp_create_sensor(dev, i); + if (ret) + dev_err(dev, "Failed to register HSMP sensors with hwmon\n"); } return 0; -- 2.51.0