On Sun May 11, 2025 at 5:44 PM -03, Antheas Kapenekakis wrote: > MSI's version of platform profile in Windows is called shift mode. > Introduce it here, and add a profile handler to it. > > It has 5 modes: sport, comfort, green, eco, and user. > Confusingly, for the Claw, MSI only uses sport, green, and eco, > where they correspond to performance, balanced, and low-power. > Therefore, comfort is mapped to balanced-performance, and user to > custom. > > Signed-off-by: Antheas Kapenekakis <lkml@xxxxxxxxxxx> > --- > drivers/platform/x86/Kconfig | 1 + > drivers/platform/x86/msi-wmi-platform.c | 117 ++++++++++++++++++++++++ > 2 files changed, 118 insertions(+) > > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig > index bee98251b8f0b..57a48910c8fd4 100644 > --- a/drivers/platform/x86/Kconfig > +++ b/drivers/platform/x86/Kconfig > @@ -746,6 +746,7 @@ config MSI_WMI_PLATFORM > tristate "MSI WMI Platform features" > depends on ACPI_WMI > depends on HWMON > + select ACPI_PLATFORM_PROFILE > help > Say Y here if you want to have support for WMI-based platform features > like fan sensor access on MSI machines. > diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86/msi-wmi-platform.c > index 9ac3c6f1b3f1d..c0b577c95c079 100644 > --- a/drivers/platform/x86/msi-wmi-platform.c > +++ b/drivers/platform/x86/msi-wmi-platform.c > @@ -17,6 +17,7 @@ > #include <linux/dmi.h> > #include <linux/errno.h> > #include <linux/fixp-arith.h> > +#include <linux/platform_profile.h> > #include <linux/hwmon.h> > #include <linux/hwmon-sysfs.h> > #include <linux/kernel.h> > @@ -63,6 +64,16 @@ > #define MSI_PLATFORM_AP_FAN_FLAGS_OFFSET 1 > #define MSI_PLATFORM_AP_ENABLE_FAN_TABLES BIT(7) > > +/* Get_Data() and Set_Data() Shift Mode Register */ Maybe you can write short documentation for these methods? > +#define MSI_PLATFORM_SHIFT_ADDR 0xd2 > +#define MSI_PLATFORM_SHIFT_DISABLE BIT(7) > +#define MSI_PLATFORM_SHIFT_ENABLE (BIT(7) | BIT(6)) > +#define MSI_PLATFORM_SHIFT_SPORT (MSI_PLATFORM_SHIFT_ENABLE + 4) > +#define MSI_PLATFORM_SHIFT_COMFORT (MSI_PLATFORM_SHIFT_ENABLE + 0) > +#define MSI_PLATFORM_SHIFT_GREEN (MSI_PLATFORM_SHIFT_ENABLE + 1) > +#define MSI_PLATFORM_SHIFT_ECO (MSI_PLATFORM_SHIFT_ENABLE + 2) > +#define MSI_PLATFORM_SHIFT_USER (MSI_PLATFORM_SHIFT_ENABLE + 3) Instead of summing the profiles I suggest something like: enum MSI_PLATFORM_PROFILES { MSI_PROFILE_COMFORT, MSI_PROFILE_GREEN, MSI_PROFILE_ECO, MSI_PROFILE_USER, MSI_PROFILE_SPORT, } And you can prepare your commands like command = MSI_PLATFORM_SHIT_ENABLE; command |= FIELD_PREP(GENMASK(1,0), MSI_PROFILE_{profile}); I feel that it's cleaner this way. This is only a suggestion though. > + > static bool force; > module_param_unsafe(force, bool, 0); > MODULE_PARM_DESC(force, "Force loading without checking for supported WMI interface versions"); > @@ -100,12 +111,14 @@ enum msi_wmi_platform_method { > }; > > struct msi_wmi_platform_quirk { > + bool shift_mode; /* Shift mode is supported */ > }; > > struct msi_wmi_platform_data { > struct wmi_device *wdev; > struct msi_wmi_platform_quirk *quirks; > struct mutex wmi_lock; /* Necessary when calling WMI methods */ > + struct device *ppdev; > }; > > struct msi_wmi_platform_debugfs_data { > @@ -150,8 +163,10 @@ static const char * const msi_wmi_platform_debugfs_names[] = { > > static struct msi_wmi_platform_quirk quirk_default = {}; > static struct msi_wmi_platform_quirk quirk_gen1 = { > + .shift_mode = true > }; > static struct msi_wmi_platform_quirk quirk_gen2 = { > + .shift_mode = true > }; > > static const struct dmi_system_id msi_quirks[] = { > @@ -561,6 +576,90 @@ static const struct hwmon_chip_info msi_wmi_platform_chip_info = { > .info = msi_wmi_platform_info, > }; > > +static int msi_wmi_platform_profile_probe(void *drvdata, unsigned long *choices) > +{ > + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); > + set_bit(PLATFORM_PROFILE_BALANCED, choices); > + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); > + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); Please, use the non-atomic __set_bit(). `choices` is not shared between threads. > + return 0; > +} > + > +static int msi_wmi_platform_profile_get(struct device *dev, > + enum platform_profile_option *profile) > +{ > + struct msi_wmi_platform_data *data = dev_get_drvdata(dev); > + int ret; > + > + u8 buffer[32] = { }; Move this to the top. > + > + buffer[0] = MSI_PLATFORM_SHIFT_ADDR; > + > + ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_DATA, buffer, sizeof(buffer)); > + if (ret < 0) > + return ret; > + > + if (buffer[0] != 1) > + return -EINVAL; > + > + switch (buffer[1]) { > + case MSI_PLATFORM_SHIFT_SPORT: > + *profile = PLATFORM_PROFILE_PERFORMANCE; > + return 0; > + case MSI_PLATFORM_SHIFT_COMFORT: > + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; Maybe comfort can be mapped to balanced and green to cool. What do you think? > + return 0; > + case MSI_PLATFORM_SHIFT_GREEN: > + *profile = PLATFORM_PROFILE_BALANCED; > + return 0; > + case MSI_PLATFORM_SHIFT_ECO: > + *profile = PLATFORM_PROFILE_LOW_POWER; > + return 0; > + case MSI_PLATFORM_SHIFT_USER: > + *profile = PLATFORM_PROFILE_CUSTOM; > + return 0; > + default: > + return -EINVAL; > + } > +} > + > +static int msi_wmi_platform_profile_set(struct device *dev, > + enum platform_profile_option profile) > +{ > + struct msi_wmi_platform_data *data = dev_get_drvdata(dev); > + u8 buffer[32] = { }; > + > + buffer[0] = MSI_PLATFORM_SHIFT_ADDR; > + > + switch (profile) { > + case PLATFORM_PROFILE_PERFORMANCE: > + buffer[1] = MSI_PLATFORM_SHIFT_SPORT; > + break; > + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: > + buffer[1] = MSI_PLATFORM_SHIFT_COMFORT; > + break; > + case PLATFORM_PROFILE_BALANCED: > + buffer[1] = MSI_PLATFORM_SHIFT_GREEN; > + break; > + case PLATFORM_PROFILE_LOW_POWER: > + buffer[1] = MSI_PLATFORM_SHIFT_ECO; > + break; > + case PLATFORM_PROFILE_CUSTOM: > + buffer[1] = MSI_PLATFORM_SHIFT_USER; > + break; > + default: > + return -EINVAL; > + } > + > + return msi_wmi_platform_query(data, MSI_PLATFORM_SET_DATA, buffer, sizeof(buffer)); > +} > + > +static const struct platform_profile_ops msi_wmi_platform_profile_ops = { > + .probe = msi_wmi_platform_profile_probe, > + .profile_get = msi_wmi_platform_profile_get, > + .profile_set = msi_wmi_platform_profile_set, > +}; > + > static ssize_t msi_wmi_platform_debugfs_write(struct file *fp, const char __user *input, > size_t length, loff_t *offset) > { > @@ -742,6 +841,22 @@ static int msi_wmi_platform_init(struct msi_wmi_platform_data *data) > return 0; > } > > +static int msi_wmi_platform_profile_setup(struct msi_wmi_platform_data *data) > +{ > + int err; > + > + if (!data->quirks->shift_mode) > + return 0; > + > + data->ppdev = devm_platform_profile_register( > + &data->wdev->dev, "msi-wmi-platform", data, > + &msi_wmi_platform_profile_ops); Broken format. > + if (err) > + return err; `err` is not initialized. Is it a leftover? > + > + return PTR_ERR_OR_ZERO(data->ppdev); > +} > + > static int msi_wmi_platform_probe(struct wmi_device *wdev, const void *context) > { > struct msi_wmi_platform_data *data; > @@ -775,6 +890,8 @@ static int msi_wmi_platform_probe(struct wmi_device *wdev, const void *context) > > msi_wmi_platform_debugfs_init(data); > > + msi_wmi_platform_profile_setup(data); Check return value. -- ~ Kurt > + > return msi_wmi_platform_hwmon_init(data); > } >
Attachment:
signature.asc
Description: PGP signature