From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 31C4625E45A; Sun, 11 May 2025 20:53:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996807; cv=none; b=CT8k3JuWd6a9zinLmt21goWJPVH68FT79wmBh+Z1nvbnyKOw0LH4SawW6BDQu5/Tmb13bxEPVljOf+FCyT+lv9eVyT1utSvZPvb4uP8nL6f+Y9Aee51XxFemjlOWXVMvZEfG0ni22sWrRgNR81ysVKbz1CARNFr4jS13O6uc2T0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996807; c=relaxed/simple; bh=lxVqT6fVrxp3st8VNJBf2MocKXWigeCZ+B0AprVLg14=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RNGTw7S9ibQAbnj5R7Sfk3Hal78VTaY9V5g9tw3mhwY5SgtnisdLp18DV98yQq/hhtJQO0p4Z6EjITzdw4sJEQnXNTZ3ZCm9LFu401PYWrGV6cuQ3QatrekdOrloIyvcJTesMBvlTYu1DKu7Iw6tCiUuf7wX0oqMDRrOtjn7A6M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=h+zh1jxP; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="h+zh1jxP" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id 08D492E0A47A; Sun, 11 May 2025 23:44:37 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996280; bh=Prktr/wUoxhrqc+n5hi2mCrepwPtH3enZVLiFIXWbZY=; h=From:To:Subject; b=h+zh1jxPppLqx413m1/fZcTRfHtLRa15nOZl7yktDPqzijyb4u8YUSAe/ItlPf345 1PvR/ndmkAx9irwWtvC/6lC3hbTe426u6d0PitFKgQNzTcQdzM5OdhoFMQ6/S5ChI4 ODcroasdL5mv8uj3V/ih7hZjo4f0d6ftZPQqqMcg= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 01/10] platform/x86: msi-wmi-platform: Use input buffer for returning result Date: Sun, 11 May 2025 22:44:18 +0200 Message-ID: <20250511204427.327558-2-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699627977.27565.11929832264375726108@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" From: Armin Wolf Modify msi_wmi_platform_query() to reuse the input buffer for returning the result of a WMI method call. Using a separate output buffer to return the result is unnecessary because the WMI interface requires both buffers to have the same length anyway. Co-developed-by: Antheas Kapenekakis Signed-off-by: Antheas Kapenekakis Signed-off-by: Armin Wolf --- drivers/platform/x86/msi-wmi-platform.c | 53 ++++++++++++------------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86= /msi-wmi-platform.c index dc5e9878cb682..41218a9d6e35d 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include =20 @@ -140,19 +141,19 @@ static int msi_wmi_platform_parse_buffer(union acpi_o= bject *obj, u8 *output, siz } =20 static int msi_wmi_platform_query(struct msi_wmi_platform_data *data, - enum msi_wmi_platform_method method, u8 *input, - size_t input_length, u8 *output, size_t output_length) + enum msi_wmi_platform_method method, u8 *buffer, + size_t length) { struct acpi_buffer out =3D { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer in =3D { - .length =3D input_length, - .pointer =3D input + .length =3D length, + .pointer =3D buffer }; union acpi_object *obj; acpi_status status; int ret; =20 - if (!input_length || !output_length) + if (!length) return -EINVAL; =20 /* @@ -169,7 +170,7 @@ static int msi_wmi_platform_query(struct msi_wmi_platfo= rm_data *data, if (!obj) return -ENODATA; =20 - ret =3D msi_wmi_platform_parse_buffer(obj, output, output_length); + ret =3D msi_wmi_platform_parse_buffer(obj, buffer, length); kfree(obj); =20 return ret; @@ -185,17 +186,15 @@ static int msi_wmi_platform_read(struct device *dev, = enum hwmon_sensor_types typ int channel, long *val) { struct msi_wmi_platform_data *data =3D dev_get_drvdata(dev); - u8 input[32] =3D { 0 }; - u8 output[32]; + u8 buffer[32] =3D { 0 }; u16 value; int ret; =20 - ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_FAN, input, sizeof(= input), output, - sizeof(output)); + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_FAN, buf, sizeof(bu= f)); if (ret < 0) return ret; =20 - value =3D get_unaligned_be16(&output[channel * 2 + 1]); + value =3D get_unaligned_be16(&buffer[channel * 2 + 1]); if (!value) *val =3D 0; else @@ -245,13 +244,17 @@ static ssize_t msi_wmi_platform_write(struct file *fp= , const char __user *input, return ret; =20 down_write(&data->buffer_lock); - ret =3D msi_wmi_platform_query(data->data, data->method, payload, data->l= ength, data->buffer, + ret =3D msi_wmi_platform_query(data->data, data->method, data->buffer, data->length); up_write(&data->buffer_lock); =20 if (ret < 0) return ret; =20 + down_write(&data->buffer_lock); + memcpy(data->buffer, payload, data->length); + up_write(&data->buffer_lock); + return length; } =20 @@ -348,23 +351,21 @@ static int msi_wmi_platform_hwmon_init(struct msi_wmi= _platform_data *data) =20 static int msi_wmi_platform_ec_init(struct msi_wmi_platform_data *data) { - u8 input[32] =3D { 0 }; - u8 output[32]; + u8 buffer[32] =3D { 0 }; u8 flags; int ret; =20 - ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_EC, input, sizeof(i= nput), output, - sizeof(output)); + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_EC, buffer, sizeof(= buffer)); if (ret < 0) return ret; =20 - flags =3D output[MSI_PLATFORM_EC_FLAGS_OFFSET]; + flags =3D buffer[MSI_PLATFORM_EC_FLAGS_OFFSET]; =20 dev_dbg(&data->wdev->dev, "EC RAM version %lu.%lu\n", FIELD_GET(MSI_PLATFORM_EC_MAJOR_MASK, flags), FIELD_GET(MSI_PLATFORM_EC_MINOR_MASK, flags)); dev_dbg(&data->wdev->dev, "EC firmware version %.28s\n", - &output[MSI_PLATFORM_EC_VERSION_OFFSET]); + &buffer[MSI_PLATFORM_EC_VERSION_OFFSET]); =20 if (!(flags & MSI_PLATFORM_EC_IS_TIGERLAKE)) { if (!force) @@ -378,27 +379,25 @@ static int msi_wmi_platform_ec_init(struct msi_wmi_pl= atform_data *data) =20 static int msi_wmi_platform_init(struct msi_wmi_platform_data *data) { - u8 input[32] =3D { 0 }; - u8 output[32]; + u8 buffer[32] =3D { 0 }; int ret; =20 - ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_WMI, input, sizeof(= input), output, - sizeof(output)); + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_WMI, buffer, sizeof= (buffer)); if (ret < 0) return ret; =20 dev_dbg(&data->wdev->dev, "WMI interface version %u.%u\n", - output[MSI_PLATFORM_WMI_MAJOR_OFFSET], - output[MSI_PLATFORM_WMI_MINOR_OFFSET]); + buffer[MSI_PLATFORM_WMI_MAJOR_OFFSET], + buffer[MSI_PLATFORM_WMI_MINOR_OFFSET]); =20 - if (output[MSI_PLATFORM_WMI_MAJOR_OFFSET] !=3D MSI_WMI_PLATFORM_INTERFACE= _VERSION) { + if (buffer[MSI_PLATFORM_WMI_MAJOR_OFFSET] !=3D MSI_WMI_PLATFORM_INTERFACE= _VERSION) { if (!force) return -ENODEV; =20 dev_warn(&data->wdev->dev, "Loading despite unsupported WMI interface version (%u.%u)\n", - output[MSI_PLATFORM_WMI_MAJOR_OFFSET], - output[MSI_PLATFORM_WMI_MINOR_OFFSET]); + buffer[MSI_PLATFORM_WMI_MAJOR_OFFSET], + buffer[MSI_PLATFORM_WMI_MINOR_OFFSET]); } =20 return 0; --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2FB7C25DD10; Sun, 11 May 2025 20:53:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996806; cv=none; b=drsR8jsZtrOUAB9VAlBPR78NCg/eowUyX1ijkF9BQyEtMNUQblVopZnMoDAUu4++gJfH62SnmUf3ygFWVyqy+XSHO8Z6TCc2Z60i93nbhQTklYStSrP5Gf79eVvOe+zPj0qhqM6DYH4rhndDcEPhGg0q2qTQNPm20SMF8kX62WI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996806; c=relaxed/simple; bh=LIFeAHb+nqLq+xefQoSh++C1ZLkF/KDUEKZLmwKOmlI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=eG93KGTrILpLsFkI9Bvy3Du0OqfH92di+RxCW3R2NtLVC71uBisUaxfGCdbs0nj8mfd4ArOO2B+R2XcigaSLH8t8Tt4ITRKJ5jZWlojuz7YAWH7WQzn54MFCCydaTXXJ/6FsTEMqww+cIZh+obYD7gMXe+4e1gsW7lxg3XPtioA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=bZe4STWq; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="bZe4STWq" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id 7A1C32E0A474; Sun, 11 May 2025 23:44:40 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996282; bh=rgXIXL0LET4rRdcbB8YpYDd7SOLKugo5R8SBkYijYKU=; h=From:To:Subject; b=bZe4STWqq/hnhSxdOI4peaHCfrNVRcvW4ysI3SUPmMUSHMr71enegUQZs9vVq+QR1 wNY38919fARSgmJlnefQhKp0VBKb1J/nWD+KqeZoyJ8xvMT2x+PTneOkhssKE5Ldbs kWAk+EBQjIY4lMst4Y+FCUX+rthHUiV2XCN1pZwo= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 02/10] platform/x86: msi-wmi-platform: Add unlocked msi_wmi_platform_query Date: Sun, 11 May 2025 22:44:19 +0200 Message-ID: <20250511204427.327558-3-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699628198.27634.6236568785205799371@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" This driver requires to be able to handle transactions that perform multiple WMI actions at a time. Therefore, it needs to be able to lock the wmi_lock mutex for multiple operations. Add msi_wmi_platform_query_unlocked() to allow the caller to perform the WMI query without locking the wmi_lock mutex, by renaming the existing function and adding a new one that only locks the mutex. Signed-off-by: Antheas Kapenekakis --- drivers/platform/x86/msi-wmi-platform.c | 27 ++++++++++++++++--------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86= /msi-wmi-platform.c index 41218a9d6e35d..f0d1b8e1a2fec 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -140,7 +140,7 @@ static int msi_wmi_platform_parse_buffer(union acpi_obj= ect *obj, u8 *output, siz return 0; } =20 -static int msi_wmi_platform_query(struct msi_wmi_platform_data *data, +static int msi_wmi_platform_query_unlocked(struct msi_wmi_platform_data *d= ata, enum msi_wmi_platform_method method, u8 *buffer, size_t length) { @@ -156,15 +156,9 @@ static int msi_wmi_platform_query(struct msi_wmi_platf= orm_data *data, if (!length) return -EINVAL; =20 - /* - * The ACPI control method responsible for handling the WMI method calls - * is not thread-safe. Because of this we have to do the locking ourself. - */ - scoped_guard(mutex, &data->wmi_lock) { - status =3D wmidev_evaluate_method(data->wdev, 0x0, method, &in, &out); - if (ACPI_FAILURE(status)) - return -EIO; - } + status =3D wmidev_evaluate_method(data->wdev, 0x0, method, &in, &out); + if (ACPI_FAILURE(status)) + return -EIO; =20 obj =3D out.pointer; if (!obj) @@ -176,6 +170,19 @@ static int msi_wmi_platform_query(struct msi_wmi_platf= orm_data *data, return ret; } =20 +static int msi_wmi_platform_query(struct msi_wmi_platform_data *data, + enum msi_wmi_platform_method method, u8 *buffer, + size_t length) +{ + /* + * The ACPI control method responsible for handling the WMI method calls + * is not thread-safe. Because of this we have to do the locking ourself. + */ + scoped_guard(mutex, &data->wmi_lock) { + return msi_wmi_platform_query_unlocked(data, method, buffer, length); + } +} + static umode_t msi_wmi_platform_is_visible(const void *drvdata, enum hwmon= _sensor_types type, u32 attr, int channel) { --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2FBE425E441; Sun, 11 May 2025 20:53:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996806; cv=none; b=JWzePHvrVc+JKTZL+1xYMlwwhxncpkfwWwJME4OOrFQ6F2VAxZbou9i4o8W9JoQzj7IhyhnJvw1/4ALPgQwmigxVnZkHUnLP4oBfyCbqIr9Lb9PFZmaj5O+xSZFc6lx4zZ8grQ68eDHGesBvucXqXIAtA4DDUwmReqmIdv4442k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996806; c=relaxed/simple; bh=BpRiUEkykFFxmO6Nb5gIYaR0NfbGv/J3jo+sqA5HoLU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=nCrIbDnUaDoAwwJy+4k5K1+TMpkIhYKg+Nq86DkQMOs222omkr7aTXSd1vre70kIggUA43XAE11zP4QbULsbUiaAlEpEv5B55Hcf8Dgnz6ekjeWdI1I2u7NSQhIaiEDn4JykCPzuao+WlVQAAh+Kc7MpNPQvzNt3XWl/ptZWjAA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=r67q5xHp; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="r67q5xHp" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id 81F812E0A48C; Sun, 11 May 2025 23:44:42 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996284; bh=MHrWlgs+N01hBSgFhNm7P6i9ul6uIvdp2jaUCTHGVik=; h=From:To:Subject; b=r67q5xHpVpa9JrQt7aSGQEnrkuTUTZnNFF/ZzxrNyXMK591efwnYo+eILIR3+PuYp euy7QaCmxizoF+sL1WNrkfS6Cjcae+mwTDuuZXj4bAv/TAfqy2FJZnGVebuNjZrPRT QoYCWtEmdYm4+R264udqO7B8exF+h9qVh1GGANFw= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 03/10] platform/x86: msi-wmi-platform: Add quirk system Date: Sun, 11 May 2025 22:44:20 +0200 Message-ID: <20250511204427.327558-4-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699628445.27701.9153414834377873410@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" MSI uses the WMI interface as a passthrough for writes to the EC and uses a board name match and a quirk table from userspace on Windows. Therefore, there is no auto-detection functionality and we have to fallback to a quirk table. Introduce it here, prior to starting to add features to it. Signed-off-by: Antheas Kapenekakis --- drivers/platform/x86/msi-wmi-platform.c | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86= /msi-wmi-platform.c index f0d1b8e1a2fec..408d42ab19e20 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -79,8 +80,12 @@ enum msi_wmi_platform_method { MSI_PLATFORM_GET_WMI =3D 0x1d, }; =20 +struct msi_wmi_platform_quirk { +}; + struct msi_wmi_platform_data { struct wmi_device *wdev; + struct msi_wmi_platform_quirk *quirks; struct mutex wmi_lock; /* Necessary when calling WMI methods */ }; =20 @@ -124,6 +129,39 @@ static const char * const msi_wmi_platform_debugfs_nam= es[] =3D { "get_wmi" }; =20 +static struct msi_wmi_platform_quirk quirk_default =3D {}; +static struct msi_wmi_platform_quirk quirk_gen1 =3D { +}; +static struct msi_wmi_platform_quirk quirk_gen2 =3D { +}; + +static const struct dmi_system_id msi_quirks[] =3D { + { + .ident =3D "MSI Claw (gen 1)", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "MS-1T41"), + }, + .driver_data =3D &quirk_gen1, + }, + { + .ident =3D "MSI Claw AI+ 7", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "MS-1T42"), + }, + .driver_data =3D &quirk_gen2, + }, + { + .ident =3D "MSI Claw AI+ 8", + .matches =3D { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_BOARD_NAME, "MS-1T52"), + }, + .driver_data =3D &quirk_gen2, + }, +}; + static int msi_wmi_platform_parse_buffer(union acpi_object *obj, u8 *outpu= t, size_t length) { if (obj->type !=3D ACPI_TYPE_BUFFER) @@ -413,6 +451,7 @@ static int msi_wmi_platform_init(struct msi_wmi_platfor= m_data *data) static int msi_wmi_platform_probe(struct wmi_device *wdev, const void *con= text) { struct msi_wmi_platform_data *data; + const struct dmi_system_id *dmi_id; int ret; =20 data =3D devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL); @@ -422,6 +461,12 @@ static int msi_wmi_platform_probe(struct wmi_device *w= dev, const void *context) data->wdev =3D wdev; dev_set_drvdata(&wdev->dev, data); =20 + dmi_id =3D dmi_first_match(msi_quirks); + if (dmi_id) + data->quirks =3D dmi_id->driver_data; + else + data->quirks =3D &quirk_default; + ret =3D devm_mutex_init(&wdev->dev, &data->wmi_lock); if (ret < 0) return ret; --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A30D919CD1B; Sun, 11 May 2025 20:44:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996294; cv=none; b=J01r29TeDtKWLX+OUii4HaaK27oYj13o7kxLGqLkUu9XRefQTJvRRkDYLWqOQwkxta23EMc8GWvu5M7XxmlbPsKcerKROdWSrzLLugaw6bATo5Lp01RDFTy1B5Q9h0hVgaZCvEi02/5dp2KdUZUb0n8mFnh+eC/seLloGRihGIo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996294; c=relaxed/simple; bh=yod0QYTUWXu4beihq/PFbHrXHSx9LymHN6BEblhLqMg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=h129NMzWmvHVYzGXrsuHGuCIVxcmamlOu4N6JM/VcXLDGgnURmNcvWc25ZHEN7yvKqpr+7UOcTCMKyhZabfY3MYOjwhcWsY1wPbSd/ylKho0cmkyS/UCYN2aq96qR7Ed43mcaerx8vgwJIn6hEHcPO1fJTrIVJEy6JWs5+pLKoA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=CU86VxmU; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="CU86VxmU" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id F1FB12E0A497; Sun, 11 May 2025 23:44:44 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996287; bh=vn6mkpGqtjeko6MG3AwpTqUYV+4tOle1Wn/dLcrIDPo=; h=From:To:Subject; b=CU86VxmU/cSKepiOJ8+DFMCHdRSiDtOwhFldAL2+j0QQ8xebRc2KIMBPWG6BOM0/4 zOnj48znK2TjfBZ1U4AJ9KXHblONm8OuT5yK+fA900oqMBjPerkeSXQp4/WNZcUsqV x2XAyZ5Gy7XgCKLKyvAsPKBkoG1yObxE9Jonul/A= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 04/10] platform/x86: msi-wmi-platform: Add support for fan control Date: Sun, 11 May 2025 22:44:21 +0200 Message-ID: <20250511204427.327558-5-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699628685.27760.15530964568722829701@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" From: Armin Wolf Adds fan curve support for the MSI platform. These devices contain support for two fans, where they are named CPU and GPU but in the case of the Claw series just map to left and right fan. Co-developed-by: Antheas Kapenekakis Signed-off-by: Antheas Kapenekakis Signed-off-by: Armin Wolf --- .../wmi/devices/msi-wmi-platform.rst | 26 ++ drivers/platform/x86/msi-wmi-platform.c | 328 +++++++++++++++++- 2 files changed, 337 insertions(+), 17 deletions(-) diff --git a/Documentation/wmi/devices/msi-wmi-platform.rst b/Documentation= /wmi/devices/msi-wmi-platform.rst index 73197b31926a5..704bfdac5203e 100644 --- a/Documentation/wmi/devices/msi-wmi-platform.rst +++ b/Documentation/wmi/devices/msi-wmi-platform.rst @@ -169,6 +169,32 @@ The fan RPM readings can be calculated with the follow= ing formula: =20 If the fan speed reading is zero, then the fan RPM is zero too. =20 +The subfeature ``0x01`` is used to retrieve the fan speed table for the CP= U fan. The output +data contains the fan speed table and two bytes with unknown data. The fan= speed table +consists of six 8-bit entries, each containing a fan speed value in percen= t. + +The subfeature ``0x02`` is used tho retrieve the same data for the GPU fan. + +WMI method Set_Fan() +-------------------- + +The fan speed tables can be accessed using subfeature ``0x01`` (CPU fan) a= nd subfeature ``0x02`` +(GPU fan). The input data has the same format as the output data of the ``= Get_Fan`` WMI method. + +WMI method Get_AP() +------------------- + +The current fan mode can be accessed using subfeature ``0x01``. The output= data contains a flag +byte and two bytes of unknown data. If the 7th bit inside the flag byte is= cleared then all fans +are operating in automatic mode, otherwise the fans operate based on the f= an speed tables +accessible thru the ``Get_Fan``/``Set_Fan`` WMI methods. + +WMI method Set_AP() +------------------- + +The current fan mode can be changed using subfeature ``0x01``. The input d= ata has the same format +as the output data of the ``Get_AP`` WMI method. + WMI method Get_WMI() -------------------- =20 diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86= /msi-wmi-platform.c index 408d42ab19e20..9ac3c6f1b3f1d 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -16,13 +16,18 @@ #include #include #include +#include #include +#include #include +#include +#include #include #include #include #include #include +#include #include #include =20 @@ -34,9 +39,11 @@ =20 #define MSI_WMI_PLATFORM_INTERFACE_VERSION 2 =20 +/* Get_WMI() WMI method */ #define MSI_PLATFORM_WMI_MAJOR_OFFSET 1 #define MSI_PLATFORM_WMI_MINOR_OFFSET 2 =20 +/* Get_EC() and Set_EC() WMI methods */ #define MSI_PLATFORM_EC_FLAGS_OFFSET 1 #define MSI_PLATFORM_EC_MINOR_MASK GENMASK(3, 0) #define MSI_PLATFORM_EC_MAJOR_MASK GENMASK(5, 4) @@ -44,6 +51,18 @@ #define MSI_PLATFORM_EC_IS_TIGERLAKE BIT(7) #define MSI_PLATFORM_EC_VERSION_OFFSET 2 =20 +/* Get_Fan() and Set_Fan() WMI methods */ +#define MSI_PLATFORM_FAN_SUBFEATURE_FAN_SPEED 0x0 +#define MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE 0x1 +#define MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE 0x2 +#define MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE 0x1 +#define MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE 0x2 + +/* Get_AP() and Set_AP() WMI methods */ +#define MSI_PLATFORM_AP_SUBFEATURE_FAN_MODE 0x1 +#define MSI_PLATFORM_AP_FAN_FLAGS_OFFSET 1 +#define MSI_PLATFORM_AP_ENABLE_FAN_TABLES BIT(7) + static bool force; module_param_unsafe(force, bool, 0); MODULE_PARM_DESC(force, "Force loading without checking for supported WMI = interface versions"); @@ -221,9 +240,201 @@ static int msi_wmi_platform_query(struct msi_wmi_plat= form_data *data, } } =20 +static ssize_t msi_wmi_platform_fan_table_show(struct device *dev, struct = device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr =3D to_sensor_dev_attr_2(attr); + struct msi_wmi_platform_data *data =3D dev_get_drvdata(dev); + u8 buffer[32] =3D { sattr->nr }; + u8 fan_percent; + int ret; + + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_FAN, buffer, sizeof= (buffer)); + if (ret < 0) + return ret; + + fan_percent =3D buffer[sattr->index + 1]; + if (fan_percent > 100) + return -EIO; + + return sysfs_emit(buf, "%d\n", fixp_linear_interpolate(0, 0, 100, 255, fa= n_percent)); +} + +static ssize_t msi_wmi_platform_fan_table_store(struct device *dev, struct= device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr =3D to_sensor_dev_attr_2(attr); + struct msi_wmi_platform_data *data =3D dev_get_drvdata(dev); + u8 buffer[32] =3D { sattr->nr }; + long speed; + int ret; + + ret =3D kstrtol(buf, 10, &speed); + if (ret < 0) + return ret; + + speed =3D clamp_val(speed, 0, 255); + + guard(mutex)(&data->wmi_lock); + + ret =3D msi_wmi_platform_query_unlocked(data, MSI_PLATFORM_GET_FAN, + buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + buffer[0] =3D sattr->nr; + buffer[sattr->index + 1] =3D fixp_linear_interpolate(0, 0, 255, 100, spee= d); + + ret =3D msi_wmi_platform_query_unlocked(data, MSI_PLATFORM_SET_FAN, + buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t msi_wmi_platform_temp_table_show(struct device *dev, struct= device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr =3D to_sensor_dev_attr_2(attr); + struct msi_wmi_platform_data *data =3D dev_get_drvdata(dev); + u8 buffer[32] =3D { sattr->nr }; + u8 temp_c; + int ret; + + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_TEMPERATURE, + buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + temp_c =3D buffer[sattr->index + 1]; + + return sysfs_emit(buf, "%d\n", temp_c); +} + +static ssize_t msi_wmi_platform_temp_table_store(struct device *dev, struc= t device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr =3D to_sensor_dev_attr_2(attr); + struct msi_wmi_platform_data *data =3D dev_get_drvdata(dev); + u8 buffer[32] =3D { sattr->nr }; + long temp_c; + int ret; + + ret =3D kstrtol(buf, 10, &temp_c); + if (ret < 0) + return ret; + + temp_c =3D clamp_val(temp_c, 0, 255); + + guard(mutex)(&data->wmi_lock); + + ret =3D msi_wmi_platform_query_unlocked(data, MSI_PLATFORM_GET_TEMPERATUR= E, + buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + buffer[0] =3D sattr->nr; + buffer[sattr->index + 1] =3D temp_c; + + ret =3D msi_wmi_platform_query_unlocked(data, MSI_PLATFORM_SET_TEMPERATUR= E, + buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + return count; +} + +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE, 0x0); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE, 0x3); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE, 0x4); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE, 0x5); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE, 0x6); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point6_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE, 0x7); + +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point1_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE, 0x1); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point2_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE, 0x2); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point3_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE, 0x3); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point4_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE, 0x4); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point5_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE, 0x5); +static SENSOR_DEVICE_ATTR_2_RW(pwm1_auto_point6_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE, 0x6); + +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE, 0x0); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE, 0x3); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE, 0x4); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE, 0x5); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE, 0x6); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point6_temp, msi_wmi_platform_tem= p_table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE, 0x7); + +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point1_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE, 0x1); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point2_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE, 0x2); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point3_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE, 0x3); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point4_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE, 0x4); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point5_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE, 0x5); +static SENSOR_DEVICE_ATTR_2_RW(pwm2_auto_point6_pwm, msi_wmi_platform_fan_= table, + MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE, 0x6); + +static struct attribute *msi_wmi_platform_hwmon_attrs[] =3D { + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, + + &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point5_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point6_temp.dev_attr.attr, + + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point5_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point6_pwm.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(msi_wmi_platform_hwmon); + static umode_t msi_wmi_platform_is_visible(const void *drvdata, enum hwmon= _sensor_types type, u32 attr, int channel) { + if (type =3D=3D hwmon_pwm && attr =3D=3D hwmon_pwm_enable) + return 0644; + return 0444; } =20 @@ -233,24 +444,102 @@ static int msi_wmi_platform_read(struct device *dev,= enum hwmon_sensor_types typ struct msi_wmi_platform_data *data =3D dev_get_drvdata(dev); u8 buffer[32] =3D { 0 }; u16 value; + u8 flags; int ret; =20 - ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_FAN, buf, sizeof(bu= f)); - if (ret < 0) - return ret; + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + buffer[0] =3D MSI_PLATFORM_FAN_SUBFEATURE_FAN_SPEED; + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_FAN, buffer, + sizeof(buffer)); + if (ret < 0) + return ret; + + value =3D get_unaligned_be16(&buffer[channel * 2 + 1]); + if (!value) + *val =3D 0; + else + *val =3D 480000 / value; + + return 0; + default: + return -EOPNOTSUPP; + } + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + buffer[0] =3D MSI_PLATFORM_AP_SUBFEATURE_FAN_MODE; + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_AP, buffer, + sizeof(buffer)); + if (ret < 0) + return ret; + + flags =3D buffer[MSI_PLATFORM_AP_FAN_FLAGS_OFFSET]; + if (flags & MSI_PLATFORM_AP_ENABLE_FAN_TABLES) + *val =3D 1; + else + *val =3D 2; + + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} =20 - value =3D get_unaligned_be16(&buffer[channel * 2 + 1]); - if (!value) - *val =3D 0; - else - *val =3D 480000 / value; +static int msi_wmi_platform_write(struct device *dev, enum hwmon_sensor_ty= pes type, u32 attr, + int channel, long val) +{ + struct msi_wmi_platform_data *data =3D dev_get_drvdata(dev); + u8 buffer[32] =3D { }; + int ret; =20 - return 0; + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + guard(mutex)(&data->wmi_lock); + + buffer[0] =3D MSI_PLATFORM_AP_SUBFEATURE_FAN_MODE; + ret =3D msi_wmi_platform_query_unlocked( + data, MSI_PLATFORM_GET_AP, buffer, + sizeof(buffer)); + if (ret < 0) + return ret; + + buffer[0] =3D MSI_PLATFORM_AP_SUBFEATURE_FAN_MODE; + switch (val) { + case 1: + buffer[MSI_PLATFORM_AP_FAN_FLAGS_OFFSET] |=3D + MSI_PLATFORM_AP_ENABLE_FAN_TABLES; + break; + case 2: + buffer[MSI_PLATFORM_AP_FAN_FLAGS_OFFSET] &=3D + ~MSI_PLATFORM_AP_ENABLE_FAN_TABLES; + break; + default: + return -EINVAL; + } + + return msi_wmi_platform_query_unlocked( + data, MSI_PLATFORM_SET_AP, buffer, + sizeof(buffer)); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } } =20 static const struct hwmon_ops msi_wmi_platform_ops =3D { .is_visible =3D msi_wmi_platform_is_visible, .read =3D msi_wmi_platform_read, + .write =3D msi_wmi_platform_write, }; =20 static const struct hwmon_channel_info * const msi_wmi_platform_info[] =3D= { @@ -260,6 +549,10 @@ static const struct hwmon_channel_info * const msi_wmi= _platform_info[] =3D { HWMON_F_INPUT, HWMON_F_INPUT ), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_ENABLE, + HWMON_PWM_ENABLE + ), NULL }; =20 @@ -268,8 +561,8 @@ static const struct hwmon_chip_info msi_wmi_platform_ch= ip_info =3D { .info =3D msi_wmi_platform_info, }; =20 -static ssize_t msi_wmi_platform_write(struct file *fp, const char __user *= input, size_t length, - loff_t *offset) +static ssize_t msi_wmi_platform_debugfs_write(struct file *fp, const char = __user *input, + size_t length, loff_t *offset) { struct seq_file *seq =3D fp->private_data; struct msi_wmi_platform_debugfs_data *data =3D seq->private; @@ -303,7 +596,7 @@ static ssize_t msi_wmi_platform_write(struct file *fp, = const char __user *input, return length; } =20 -static int msi_wmi_platform_show(struct seq_file *seq, void *p) +static int msi_wmi_platform_debugfs_show(struct seq_file *seq, void *p) { struct msi_wmi_platform_debugfs_data *data =3D seq->private; int ret; @@ -315,19 +608,19 @@ static int msi_wmi_platform_show(struct seq_file *seq= , void *p) return ret; } =20 -static int msi_wmi_platform_open(struct inode *inode, struct file *fp) +static int msi_wmi_platform_debugfs_open(struct inode *inode, struct file = *fp) { struct msi_wmi_platform_debugfs_data *data =3D inode->i_private; =20 /* The seq_file uses the last byte of the buffer for detecting buffer ove= rflows */ - return single_open_size(fp, msi_wmi_platform_show, data, data->length + 1= ); + return single_open_size(fp, msi_wmi_platform_debugfs_show, data, data->le= ngth + 1); } =20 static const struct file_operations msi_wmi_platform_debugfs_fops =3D { .owner =3D THIS_MODULE, - .open =3D msi_wmi_platform_open, + .open =3D msi_wmi_platform_debugfs_open, .read =3D seq_read, - .write =3D msi_wmi_platform_write, + .write =3D msi_wmi_platform_debugfs_write, .llseek =3D seq_lseek, .release =3D single_release, }; @@ -389,7 +682,8 @@ static int msi_wmi_platform_hwmon_init(struct msi_wmi_p= latform_data *data) struct device *hdev; =20 hdev =3D devm_hwmon_device_register_with_info(&data->wdev->dev, "msi_wmi_= platform", data, - &msi_wmi_platform_chip_info, NULL); + &msi_wmi_platform_chip_info, + msi_wmi_platform_hwmon_groups); =20 return PTR_ERR_OR_ZERO(hdev); } --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A5A661E2613; Sun, 11 May 2025 20:44:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996295; cv=none; b=Ypma3kixX1rGCkmfSrDdubF0l6ByzMwNWwRu9R7qYhrnTQSnt8ziCm/NjWRenEp5ZXc6GAd0SQGRrix5eGXzN0LsWb+tidT3GtxXzg7nSzyYDdC/Ve5OUsv05OoNAzIbnZFYWtxAx+4CIZfi2XWgI7CowmfhSed9bsZ9JbPsoJY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996295; c=relaxed/simple; bh=gDFJ9+K1kVVChodFDOpA1BknylLr1pIcjGCP+rcoI2E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GwCa2wprQ8uSgMtbEk+Q9GLWLwwBsMXUlN9jAtI1EDpIE2dtMDh334zS+0xPISVyKLG02epYENBb9gQXEF+84L7wUIgPespcNaP/KmT0fpHAJpBXjlzrq+oweOvXKo2FrFNqsIie/53fvByUEc8FMVC9a1Et01A1FicXMMXef3k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=x+d/d9Bc; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="x+d/d9Bc" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id 814282E0A4D3; Sun, 11 May 2025 23:44:47 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996289; bh=HWQUNkPUDvHGDiPDP1/+nuZxOazjs5/3q5rXh/7y6Zo=; h=From:To:Subject; b=x+d/d9BcYedOvht1aGHDslE652va8KpIODQX0VO34pQuFdSyC8vhzvxaG8WCXfQYm pZrEfwyvhKkZVNg0qcURliqpcv0NMfv31+PzvdKLBUcouLClT1HF6yAJiDwX7FUGsw TE1z4XlRRO0kenMJWP074fSvnnHIpRAStl3PxvMI= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 05/10] platform/x86: msi-wmi-platform: Add platform profile through shift mode Date: Sun, 11 May 2025 22:44:22 +0200 Message-ID: <20250511204427.327558-6-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699628919.27833.13898849900911197689@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" 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 --- 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 #include #include +#include #include #include #include @@ -63,6 +64,16 @@ #define MSI_PLATFORM_AP_FAN_FLAGS_OFFSET 1 #define MSI_PLATFORM_AP_ENABLE_FAN_TABLES BIT(7) =20 +/* Get_Data() and Set_Data() Shift Mode Register */ +#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) + 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 { }; =20 struct msi_wmi_platform_quirk { + bool shift_mode; /* Shift mode is supported */ }; =20 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; }; =20 struct msi_wmi_platform_debugfs_data { @@ -150,8 +163,10 @@ static const char * const msi_wmi_platform_debugfs_nam= es[] =3D { =20 static struct msi_wmi_platform_quirk quirk_default =3D {}; static struct msi_wmi_platform_quirk quirk_gen1 =3D { + .shift_mode =3D true }; static struct msi_wmi_platform_quirk quirk_gen2 =3D { + .shift_mode =3D true }; =20 static const struct dmi_system_id msi_quirks[] =3D { @@ -561,6 +576,90 @@ static const struct hwmon_chip_info msi_wmi_platform_c= hip_info =3D { .info =3D msi_wmi_platform_info, }; =20 +static int msi_wmi_platform_profile_probe(void *drvdata, unsigned long *ch= oices) +{ + 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); + return 0; +} + +static int msi_wmi_platform_profile_get(struct device *dev, + enum platform_profile_option *profile) +{ + struct msi_wmi_platform_data *data =3D dev_get_drvdata(dev); + int ret; + + u8 buffer[32] =3D { }; + + buffer[0] =3D MSI_PLATFORM_SHIFT_ADDR; + + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_DATA, buffer, sizeo= f(buffer)); + if (ret < 0) + return ret; + + if (buffer[0] !=3D 1) + return -EINVAL; + + switch (buffer[1]) { + case MSI_PLATFORM_SHIFT_SPORT: + *profile =3D PLATFORM_PROFILE_PERFORMANCE; + return 0; + case MSI_PLATFORM_SHIFT_COMFORT: + *profile =3D PLATFORM_PROFILE_BALANCED_PERFORMANCE; + return 0; + case MSI_PLATFORM_SHIFT_GREEN: + *profile =3D PLATFORM_PROFILE_BALANCED; + return 0; + case MSI_PLATFORM_SHIFT_ECO: + *profile =3D PLATFORM_PROFILE_LOW_POWER; + return 0; + case MSI_PLATFORM_SHIFT_USER: + *profile =3D 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 =3D dev_get_drvdata(dev); + u8 buffer[32] =3D { }; + + buffer[0] =3D MSI_PLATFORM_SHIFT_ADDR; + + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + buffer[1] =3D MSI_PLATFORM_SHIFT_SPORT; + break; + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: + buffer[1] =3D MSI_PLATFORM_SHIFT_COMFORT; + break; + case PLATFORM_PROFILE_BALANCED: + buffer[1] =3D MSI_PLATFORM_SHIFT_GREEN; + break; + case PLATFORM_PROFILE_LOW_POWER: + buffer[1] =3D MSI_PLATFORM_SHIFT_ECO; + break; + case PLATFORM_PROFILE_CUSTOM: + buffer[1] =3D 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 =3D { + .probe =3D msi_wmi_platform_profile_probe, + .profile_get =3D msi_wmi_platform_profile_get, + .profile_set =3D 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_platfo= rm_data *data) return 0; } =20 +static int msi_wmi_platform_profile_setup(struct msi_wmi_platform_data *da= ta) +{ + int err; + + if (!data->quirks->shift_mode) + return 0; + + data->ppdev =3D devm_platform_profile_register( + &data->wdev->dev, "msi-wmi-platform", data, + &msi_wmi_platform_profile_ops); + if (err) + return err; + + return PTR_ERR_OR_ZERO(data->ppdev); +} + static int msi_wmi_platform_probe(struct wmi_device *wdev, const void *con= text) { struct msi_wmi_platform_data *data; @@ -775,6 +890,8 @@ static int msi_wmi_platform_probe(struct wmi_device *wd= ev, const void *context) =20 msi_wmi_platform_debugfs_init(data); =20 + msi_wmi_platform_profile_setup(data); + return msi_wmi_platform_hwmon_init(data); } =20 --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3913625F97C; Sun, 11 May 2025 20:44:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996298; cv=none; b=Bo2Ne5QMoYEXpDnyyBR/cI6W8+07cRfWrSOE/U2UXhziA+0++UA5zjkhZWB+Z5o72AaMy/b6YhOa0dbWj2tzkEHvGdLG0N03eoPLFryVp7WlLLYtEtf/RaSDtfkQr3y2mgihCbDXDHuDiyH52+wWAQUNJqqJASHw615+oMgqG/E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996298; c=relaxed/simple; bh=u/pnYZxcbcw4sGrkm/SKQDDQ5LmdLfxsLKeJjfkaGl0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aHpqui3QGXeFiJ4FmZuATK9hB3w3gcXWbRiPTXjGSbc+Kk4b/gXbLbPploJ2DksQv5McmRtG7eDLjRgioEifrdlCwaac2odPaCCL/H2w8WsLevs57Mc0kF6ZcLzh7MYVdnZ2eqtR+TH1SHjPrnLdHAmILliwIV3gVM/YXQBXET0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=OK854rBZ; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="OK854rBZ" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id CD9C92E0A48E; Sun, 11 May 2025 23:44:49 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996291; bh=1vf0uADkwVLhVqOD+YVjSxJ8zT+FEcBB8KuDdLtEsSc=; h=From:To:Subject; b=OK854rBZNgBM1hKqDfeOoKYm+tDR1qoCZUOuZtHvuMt19IZuc4QcXsb6SPvH+iYmw AWEL+7u+4dWE3xWhf9XAkvOt7nXinLMG6HNrUXvUOSf+gNMGNJwzeM2AI7LnvH3I38 4+HwLGCT0Oce8Py2qJnzVvGBWsjRUHG5EfD+fxPA= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 06/10] platform/x86: msi-wmi-platform: Add PL1/PL2 support via firmware attributes Date: Sun, 11 May 2025 22:44:23 +0200 Message-ID: <20250511204427.327558-7-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699629160.27911.3593585289138935135@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" Adds PL1, and PL2 support through the firmware attributes interface. The min and max values are quirked, and the attributes are only defined if they are set to a non-zero value. These values are meant to be set in conjunction with shift mode, where shift mode automatically sets an upper bound on PL1/PL2 (e.g., low-power would be used with 8W). Signed-off-by: Antheas Kapenekakis --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/msi-wmi-platform.c | 361 +++++++++++++++++++++++- 2 files changed, 360 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 57a48910c8fd4..fd3da718731e7 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -747,6 +747,7 @@ config MSI_WMI_PLATFORM depends on ACPI_WMI depends on HWMON select ACPI_PLATFORM_PROFILE + select FW_ATTR_CLASS 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 c0b577c95c079..6498f4b44fe53 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -34,6 +34,8 @@ =20 #include =20 +#include "firmware_attributes_class.h" + #define DRIVER_NAME "msi-wmi-platform" =20 #define MSI_PLATFORM_GUID "ABBC0F6E-8EA1-11d1-00A0-C90629100000" @@ -74,6 +76,10 @@ #define MSI_PLATFORM_SHIFT_ECO (MSI_PLATFORM_SHIFT_ENABLE + 2) #define MSI_PLATFORM_SHIFT_USER (MSI_PLATFORM_SHIFT_ENABLE + 3) =20 +/* Get_Data() and Set_Data() Params */ +#define MSI_PLATFORM_PL1_ADDR 0x50 +#define MSI_PLATFORM_PL2_ADDR 0x51 + static bool force; module_param_unsafe(force, bool, 0); MODULE_PARM_DESC(force, "Force loading without checking for supported WMI = interface versions"); @@ -112,6 +118,9 @@ enum msi_wmi_platform_method { =20 struct msi_wmi_platform_quirk { bool shift_mode; /* Shift mode is supported */ + int pl_min; /* Minimum PLx value */ + int pl1_max; /* Maximum PL1 value */ + int pl2_max; /* Maximum PL2 value */ }; =20 struct msi_wmi_platform_data { @@ -119,6 +128,44 @@ struct msi_wmi_platform_data { struct msi_wmi_platform_quirk *quirks; struct mutex wmi_lock; /* Necessary when calling WMI methods */ struct device *ppdev; + struct device *fw_attrs_dev; + struct kset *fw_attrs_kset; +}; + +enum msi_fw_attr_id { + MSI_ATTR_PPT_PL1_SPL, + MSI_ATTR_PPT_PL2_SPPT, +}; + +static const char *const msi_fw_attr_name[] =3D { + [MSI_ATTR_PPT_PL1_SPL] =3D "ppt_pl1_spl", + [MSI_ATTR_PPT_PL2_SPPT] =3D "ppt_pl2_sppt", +}; + +static const char *const msi_fw_attr_desc[] =3D { + [MSI_ATTR_PPT_PL1_SPL] =3D "CPU Steady package limit (PL1/SPL)", + [MSI_ATTR_PPT_PL2_SPPT] =3D "CPU Boost slow package limit (PL2/SPPT)", +}; + +#define MSI_ATTR_LANGUAGE_CODE "en_US.UTF-8" + +struct msi_fw_attr { + struct msi_wmi_platform_data *data; + enum msi_fw_attr_id fw_attr_id; + struct attribute_group attr_group; + struct kobj_attribute display_name; + struct kobj_attribute current_value; + struct kobj_attribute min_value; + struct kobj_attribute max_value; + + u32 min; + u32 max; + + int (*get_value)(struct msi_wmi_platform_data *data, + struct msi_fw_attr *fw_attr, char *buf); + ssize_t (*set_value)(struct msi_wmi_platform_data *data, + struct msi_fw_attr *fw_attr, const char *buf, + size_t count); }; =20 struct msi_wmi_platform_debugfs_data { @@ -163,10 +210,16 @@ static const char * const msi_wmi_platform_debugfs_na= mes[] =3D { =20 static struct msi_wmi_platform_quirk quirk_default =3D {}; static struct msi_wmi_platform_quirk quirk_gen1 =3D { - .shift_mode =3D true + .shift_mode =3D true, + .pl_min =3D 8, + .pl1_max =3D 43, + .pl2_max =3D 45 }; static struct msi_wmi_platform_quirk quirk_gen2 =3D { - .shift_mode =3D true + .shift_mode =3D true, + .pl_min =3D 8, + .pl1_max =3D 30, + .pl2_max =3D 37 }; =20 static const struct dmi_system_id msi_quirks[] =3D { @@ -660,6 +713,306 @@ static const struct platform_profile_ops msi_wmi_plat= form_profile_ops =3D { .profile_set =3D msi_wmi_platform_profile_set, }; =20 +/* Firmware Attributes setup */ +static int data_get_addr(struct msi_wmi_platform_data *data, + const enum msi_fw_attr_id id) +{ + switch (id) { + case MSI_ATTR_PPT_PL1_SPL: + return MSI_PLATFORM_PL1_ADDR; + case MSI_ATTR_PPT_PL2_SPPT: + return MSI_PLATFORM_PL2_ADDR; + default: + pr_warn("Invalid attribute id %d\n", id); + return -EINVAL; + } +} + +static ssize_t data_set_value(struct msi_wmi_platform_data *data, + struct msi_fw_attr *fw_attr, const char *buf, + size_t count) +{ + u8 buffer[32] =3D { 0 }; + int ret, fwid; + u32 value; + + fwid =3D data_get_addr(data, fw_attr->fw_attr_id); + if (fwid < 0) + return fwid; + + ret =3D kstrtou32(buf, 10, &value); + if (ret) + return ret; + + if (fw_attr->min >=3D 0 && value < fw_attr->min) + return -EINVAL; + if (fw_attr->max >=3D 0 && value > fw_attr->max) + return -EINVAL; + + buffer[0] =3D fwid; + put_unaligned_le32(value, &buffer[1]); + + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_SET_DATA, buffer, sizeo= f(buffer)); + if (ret) { + pr_warn("Failed to set_data with id %d: %d\n", + fw_attr->fw_attr_id, ret); + return ret; + } + + return count; +} + +static int data_get_value(struct msi_wmi_platform_data *data, + struct msi_fw_attr *fw_attr, char *buf) +{ + u8 buffer[32] =3D { 0 }; + u32 value; + int ret, addr; + + addr =3D data_get_addr(data, fw_attr->fw_attr_id); + if (addr < 0) + return addr; + + buffer[0] =3D addr; + + ret =3D msi_wmi_platform_query(data, MSI_PLATFORM_GET_DATA, buffer, sizeo= f(buffer)); + if (ret) { + pr_warn("Failed to show set_data for id %d: %d\n", + fw_attr->fw_attr_id, ret); + return ret; + } + + value =3D get_unaligned_le32(&buffer[1]); + + return sysfs_emit(buf, "%d\n", value); +} + +static ssize_t display_name_language_code_show(struct kobject *kobj, struc= t kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%s\n", MSI_ATTR_LANGUAGE_CODE); +} + +static struct kobj_attribute fw_attr_display_name_language_code =3D + __ATTR_RO(display_name_language_code); + +static ssize_t scalar_increment_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "1\n"); +} + +static struct kobj_attribute fw_attr_scalar_increment =3D + __ATTR_RO(scalar_increment); + +static ssize_t pending_reboot_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "0\n"); +} + +static struct kobj_attribute fw_attr_pending_reboot =3D __ATTR_RO(pending_= reboot); + +static ssize_t display_name_show(struct kobject *kobj, struct kobj_attribu= te *attr, char *buf) +{ + struct msi_fw_attr *fw_attr =3D + container_of(attr, struct msi_fw_attr, display_name); + + return sysfs_emit(buf, "%s\n", msi_fw_attr_desc[fw_attr->fw_attr_id]); +} + +static ssize_t current_value_show(struct kobject *kobj, struct kobj_attrib= ute *attr, char *buf) +{ + struct msi_fw_attr *fw_attr =3D + container_of(attr, struct msi_fw_attr, current_value); + + return fw_attr->get_value(fw_attr->data, fw_attr, buf); +} + +static ssize_t current_value_store(struct kobject *kobj, struct kobj_attri= bute *attr, + const char *buf, size_t count) +{ + struct msi_fw_attr *fw_attr =3D + container_of(attr, struct msi_fw_attr, current_value); + + return fw_attr->set_value(fw_attr->data, fw_attr, buf, count); +} + +static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "integer\n"); +} + +static struct kobj_attribute fw_attr_type_int =3D { + .attr =3D { .name =3D "type", .mode =3D 0444 }, + .show =3D type_show, +}; + +static ssize_t min_value_show(struct kobject *kobj, struct kobj_attribute = *attr, + char *buf) +{ + struct msi_fw_attr *fw_attr =3D + container_of(attr, struct msi_fw_attr, min_value); + + return sysfs_emit(buf, "%d\n", fw_attr->min); +} + +static ssize_t max_value_show(struct kobject *kobj, struct kobj_attribute = *attr, + char *buf) +{ + struct msi_fw_attr *fw_attr =3D + container_of(attr, struct msi_fw_attr, max_value); + + return sysfs_emit(buf, "%d\n", fw_attr->max); +} + +#define FW_ATTR_ENUM_MAX_ATTRS 7 + +static int +msi_fw_attr_init(struct msi_wmi_platform_data *data, + const enum msi_fw_attr_id fw_attr_id, + struct kobj_attribute *fw_attr_type, const s32 min, + const s32 max, + int (*get_value)(struct msi_wmi_platform_data *data, + struct msi_fw_attr *fw_attr, char *buf), + ssize_t (*set_value)(struct msi_wmi_platform_data *data, + struct msi_fw_attr *fw_attr, + const char *buf, size_t count)) +{ + struct msi_fw_attr *fw_attr; + struct attribute **attrs; + int idx =3D 0; + + fw_attr =3D devm_kzalloc(&data->wdev->dev, sizeof(*fw_attr), GFP_KERNEL); + if (!fw_attr) + return -ENOMEM; + + attrs =3D devm_kcalloc(&data->wdev->dev, FW_ATTR_ENUM_MAX_ATTRS + 1, + sizeof(*attrs), GFP_KERNEL); + if (!attrs) + return -ENOMEM; + + fw_attr->data =3D data; + fw_attr->fw_attr_id =3D fw_attr_id; + fw_attr->attr_group.name =3D msi_fw_attr_name[fw_attr_id]; + fw_attr->attr_group.attrs =3D attrs; + fw_attr->get_value =3D get_value; + fw_attr->set_value =3D set_value; + + attrs[idx++] =3D &fw_attr_type->attr; + if (fw_attr_type =3D=3D &fw_attr_type_int) + attrs[idx++] =3D &fw_attr_scalar_increment.attr; + attrs[idx++] =3D &fw_attr_display_name_language_code.attr; + + sysfs_attr_init(&fw_attr->display_name.attr); + fw_attr->display_name.attr.name =3D "display_name"; + fw_attr->display_name.attr.mode =3D 0444; + fw_attr->display_name.show =3D display_name_show; + attrs[idx++] =3D &fw_attr->display_name.attr; + + sysfs_attr_init(&fw_attr->current_value.attr); + fw_attr->current_value.attr.name =3D "current_value"; + fw_attr->current_value.attr.mode =3D 0644; + fw_attr->current_value.show =3D current_value_show; + fw_attr->current_value.store =3D current_value_store; + attrs[idx++] =3D &fw_attr->current_value.attr; + + if (min >=3D 0) { + fw_attr->min =3D min; + sysfs_attr_init(&fw_attr->min_value.attr); + fw_attr->min_value.attr.name =3D "min_value"; + fw_attr->min_value.attr.mode =3D 0444; + fw_attr->min_value.show =3D min_value_show; + attrs[idx++] =3D &fw_attr->min_value.attr; + } else { + fw_attr->min =3D -1; + } + + if (max >=3D 0) { + fw_attr->max =3D max; + sysfs_attr_init(&fw_attr->max_value.attr); + fw_attr->max_value.attr.name =3D "max_value"; + fw_attr->max_value.attr.mode =3D 0444; + fw_attr->max_value.show =3D max_value_show; + attrs[idx++] =3D &fw_attr->max_value.attr; + } else { + fw_attr->max =3D -1; + } + + attrs[idx] =3D NULL; + return sysfs_create_group(&data->fw_attrs_kset->kobj, &fw_attr->attr_grou= p); +} + +static void msi_kset_unregister(void *data) +{ + struct kset *kset =3D data; + + sysfs_remove_file(&kset->kobj, &fw_attr_pending_reboot.attr); + kset_unregister(kset); +} + +static void msi_fw_attrs_dev_unregister(void *data) +{ + struct device *fw_attrs_dev =3D data; + + device_unregister(fw_attrs_dev); +} + +static int msi_wmi_fw_attrs_init(struct msi_wmi_platform_data *data) +{ + int err; + + data->fw_attrs_dev =3D device_create(&firmware_attributes_class, NULL, MK= DEV(0, 0), + NULL, "%s", DRIVER_NAME); + if (IS_ERR(data->fw_attrs_dev)) + return PTR_ERR(data->fw_attrs_dev); + + err =3D devm_add_action_or_reset(&data->wdev->dev, + msi_fw_attrs_dev_unregister, + data->fw_attrs_dev); + if (err) + return err; + + data->fw_attrs_kset =3D kset_create_and_add("attributes", NULL, + &data->fw_attrs_dev->kobj); + if (!data->fw_attrs_kset) + return -ENOMEM; + + err =3D sysfs_create_file(&data->fw_attrs_kset->kobj, + &fw_attr_pending_reboot.attr); + if (err) { + kset_unregister(data->fw_attrs_kset); + return err; + } + + err =3D devm_add_action_or_reset(&data->wdev->dev, msi_kset_unregister, + data->fw_attrs_kset); + if (err) + return err; + + if (data->quirks->pl1_max) { + err =3D msi_fw_attr_init(data, MSI_ATTR_PPT_PL1_SPL, + &fw_attr_type_int, data->quirks->pl_min, + data->quirks->pl1_max, &data_get_value, + &data_set_value); + if (err) + return err; + } + + if (data->quirks->pl2_max) { + err =3D msi_fw_attr_init(data, MSI_ATTR_PPT_PL2_SPPT, + &fw_attr_type_int, data->quirks->pl_min, + data->quirks->pl2_max, &data_get_value, + &data_set_value); + if (err) + return err; + } + + return 0; +} + static ssize_t msi_wmi_platform_debugfs_write(struct file *fp, const char = __user *input, size_t length, loff_t *offset) { @@ -888,6 +1241,10 @@ static int msi_wmi_platform_probe(struct wmi_device *= wdev, const void *context) if (ret < 0) return ret; =20 + ret =3D msi_wmi_fw_attrs_init(data); + if (ret < 0) + return ret; + msi_wmi_platform_debugfs_init(data); =20 msi_wmi_platform_profile_setup(data); --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A2652609FD; Sun, 11 May 2025 20:44:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996298; cv=none; b=aQ2a2EQCqBiYD7LTjgjfn4hBgC0qcc6n7N6gIqQN53+hJQUz0nkd9JcC7/2gpO1clZTrDbFQs+RzpPC4ZEPFoCGFLp8ssE7Bn5YBwEFJjJQyYVChY4MEOO7+aC/h4uvXQ513og9TPqfSmYIjSFgURnV1RXE8QhPCPJ3zcWAlBOg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996298; c=relaxed/simple; bh=kpBnwgNwczqcxbMFE6cot4862GY10n56IFvMiPvIr0Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MMbhUbzMsNl6ZJNsUbv/xTr8RwnhmASYzUZpUyn794wBkDXoSZi+yJxot+Ai5wKB7kz4kPIIAdb09jlV13HdtzY4znPWwv0dwDTpPrVdbptG73ShvKF8SbhnUAxmBSSAR6kKDZ7yX3dZxY1CL5umdYhdyxJsTGGD/remv6w8sY8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=ZesEwHQ9; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="ZesEwHQ9" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id 1CD982E0A4DC; Sun, 11 May 2025 23:44:52 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996294; bh=WopzrnfLWFUOpLXJ+4tce4MrUTHEiUC4E4KKMWifksY=; h=From:To:Subject; b=ZesEwHQ9a65YY7n11HqOHny55dcX+0oiNB/E53vY0MSEgdlCcS85h5qBi7HdxhcnB 97eyXjuqY8XUYbomVb+IV1yC4VysvIDYFvUoZtYgeuChjLOmkoL1wj7Y2HTHEPEsug Lc6SqqJ6fKGqLgdw/NOtNuZoShtUw4n9NWYM4phE= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 07/10] platform/x86: msi-wmi-platform: Add charge_threshold support Date: Sun, 11 May 2025 22:44:24 +0200 Message-ID: <20250511204427.327558-8-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699629404.27973.5787412059925961155@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" The battery of MSI laptops supports charge threshold. Add support for it. Signed-off-by: Antheas Kapenekakis --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/msi-wmi-platform.c | 110 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index fd3da718731e7..51a34ab476ffc 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -744,6 +744,7 @@ config MSI_WMI =20 config MSI_WMI_PLATFORM tristate "MSI WMI Platform features" + depends on ACPI_BATTERY depends on ACPI_WMI depends on HWMON select ACPI_PLATFORM_PROFILE diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86= /msi-wmi-platform.c index 6498f4b44fe53..46928fb4da8a6 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -31,6 +31,7 @@ #include #include #include +#include =20 #include =20 @@ -79,6 +80,7 @@ /* Get_Data() and Set_Data() Params */ #define MSI_PLATFORM_PL1_ADDR 0x50 #define MSI_PLATFORM_PL2_ADDR 0x51 +#define MSI_PLATFORM_BAT_ADDR 0xd7 =20 static bool force; module_param_unsafe(force, bool, 0); @@ -118,6 +120,7 @@ enum msi_wmi_platform_method { =20 struct msi_wmi_platform_quirk { bool shift_mode; /* Shift mode is supported */ + bool charge_threshold; /* Charge threshold is supported */ int pl_min; /* Minimum PLx value */ int pl1_max; /* Maximum PL1 value */ int pl2_max; /* Maximum PL2 value */ @@ -128,6 +131,7 @@ struct msi_wmi_platform_data { struct msi_wmi_platform_quirk *quirks; struct mutex wmi_lock; /* Necessary when calling WMI methods */ struct device *ppdev; + struct acpi_battery_hook battery_hook; struct device *fw_attrs_dev; struct kset *fw_attrs_kset; }; @@ -211,12 +215,14 @@ static const char * const msi_wmi_platform_debugfs_na= mes[] =3D { static struct msi_wmi_platform_quirk quirk_default =3D {}; static struct msi_wmi_platform_quirk quirk_gen1 =3D { .shift_mode =3D true, + .charge_threshold =3D true, .pl_min =3D 8, .pl1_max =3D 43, .pl2_max =3D 45 }; static struct msi_wmi_platform_quirk quirk_gen2 =3D { .shift_mode =3D true, + .charge_threshold =3D true, .pl_min =3D 8, .pl1_max =3D 30, .pl2_max =3D 37 @@ -1013,6 +1019,94 @@ static int msi_wmi_fw_attrs_init(struct msi_wmi_plat= form_data *data) return 0; } =20 +static int msi_platform_psy_ext_get_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct msi_wmi_platform_data *msi =3D data; + u8 buffer[32] =3D { 0 }; + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + buffer[0] =3D MSI_PLATFORM_BAT_ADDR; + ret =3D msi_wmi_platform_query(msi, MSI_PLATFORM_GET_DATA, + buffer, sizeof(buffer)); + if (ret) + return ret; + + val->intval =3D buffer[1] & ~BIT(7); + if (val->intval > 100) + return -EINVAL; + + return 0; + default: + return -EINVAL; + } +} + +static int msi_platform_psy_ext_set_prop(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct msi_wmi_platform_data *msi =3D data; + u8 buffer[32] =3D { 0 }; + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: + if (val->intval > 100) + return -EINVAL; + buffer[0] =3D MSI_PLATFORM_BAT_ADDR; + buffer[1] =3D val->intval | BIT(7); + return msi_wmi_platform_query(msi, MSI_PLATFORM_SET_DATA, + buffer, sizeof(buffer)); + default: + return -EINVAL; + } +} + +static int +msi_platform_psy_prop_is_writeable(struct power_supply *psy, + const struct power_supply_ext *ext, + void *data, enum power_supply_property psp) +{ + return true; +} + +static const enum power_supply_property oxp_psy_ext_props[] =3D { + POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, +}; + +static const struct power_supply_ext msi_platform_psy_ext =3D { + .name =3D "msi-platform-charge-control", + .properties =3D oxp_psy_ext_props, + .num_properties =3D ARRAY_SIZE(oxp_psy_ext_props), + .get_property =3D msi_platform_psy_ext_get_prop, + .set_property =3D msi_platform_psy_ext_set_prop, + .property_is_writeable =3D msi_platform_psy_prop_is_writeable, +}; + +static int msi_wmi_platform_battery_add(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + struct msi_wmi_platform_data *data =3D + container_of(hook, struct msi_wmi_platform_data, battery_hook); + + return power_supply_register_extension(battery, &msi_platform_psy_ext, + &data->wdev->dev, data); +} + +static int msi_wmi_platform_battery_remove(struct power_supply *battery, + struct acpi_battery_hook *hook) +{ + power_supply_unregister_extension(battery, &msi_platform_psy_ext); + return 0; +} + static ssize_t msi_wmi_platform_debugfs_write(struct file *fp, const char = __user *input, size_t length, loff_t *offset) { @@ -1245,6 +1339,13 @@ static int msi_wmi_platform_probe(struct wmi_device = *wdev, const void *context) if (ret < 0) return ret; =20 + if (data->quirks->charge_threshold) { + data->battery_hook.name =3D "MSI Battery"; + data->battery_hook.add_battery =3D msi_wmi_platform_battery_add; + data->battery_hook.remove_battery =3D msi_wmi_platform_battery_remove; + battery_hook_register(&data->battery_hook); + } + msi_wmi_platform_debugfs_init(data); =20 msi_wmi_platform_profile_setup(data); @@ -1252,6 +1353,14 @@ static int msi_wmi_platform_probe(struct wmi_device = *wdev, const void *context) return msi_wmi_platform_hwmon_init(data); } =20 +static void msi_wmi_platform_remove(struct wmi_device *wdev) +{ + struct msi_wmi_platform_data *data =3D dev_get_drvdata(&wdev->dev); + + if (data->quirks->charge_threshold) + battery_hook_unregister(&data->battery_hook); +} + static const struct wmi_device_id msi_wmi_platform_id_table[] =3D { { MSI_PLATFORM_GUID, NULL }, { } @@ -1265,6 +1374,7 @@ static struct wmi_driver msi_wmi_platform_driver =3D { }, .id_table =3D msi_wmi_platform_id_table, .probe =3D msi_wmi_platform_probe, + .remove =3D msi_wmi_platform_remove, .no_singleton =3D true, }; module_wmi_driver(msi_wmi_platform_driver); --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 213E4262FF6; Sun, 11 May 2025 20:44:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996301; cv=none; b=SaUs6XMCS8rPovScO4QlD3UMoVv9qfIJdH9rB+GoCgSwCuNdKD6Q8CXRvmZHTLOzSSstWzG23LiyEG1DcRLlySV+vWqC46ixVoEd5nAWqFxqB5nSdpp59yqUg8ju9VQyhvOq2UawsHy23aTK4oAlCfE6dr26YWnBPktwCIpRqGg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996301; c=relaxed/simple; bh=7W2ynPb+GWUHYkq+13QBa7AhkJJC8fkL2TK8e3N3oH0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RIHs7J36HeENFiPWZnCcS6gDdfImHIfCJ/JQgX39tufgiLyies/wvFNSpUOT89hbxoYi/A6XBZ74uYR7vaNHElC6c2lKLdlnkASpI7fUvHjk7RssgRkJlh8aVJEuuqm28i43qu5worJE67WxYwmryO1GzbQezVEnMWEFUfL8cGc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=Vc4Bci/5; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="Vc4Bci/5" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id BF2F22E0A4FD; Sun, 11 May 2025 23:44:54 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996297; bh=SiKkfgsqPmiEofXzj87y/JV1KOZHBaOsXJ78Qapas3g=; h=From:To:Subject; b=Vc4Bci/5ibaVRZS1k5OnPN1ritLDzjfuRmTu+81EW3R4U/I/QMu1kBes3rOmt2mc3 L9Wp4Cf7PMrfTztqMLAwuO8QjEjsxhpYDgitWxBvK7iOcnnaLgAm7BBPjRjfVbFoNz 1Mw545tvDCchgt8eGSxYftDYzda5o8a/xoo+7Pgs= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 08/10] platform/x86: msi-wmi-platform: Drop excess fans in dual fan devices Date: Sun, 11 May 2025 22:44:25 +0200 Message-ID: <20250511204427.327558-9-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699629656.28048.2062799627441253450@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" Currently, the platform driver always exposes 4 fans, since the underlying WMI interface reads 4 values from the EC. However, most devices only have two fans. Therefore, at least in the case of the Claw series, quirk the driver to only show two hwmon fans. Signed-off-by: Antheas Kapenekakis --- drivers/platform/x86/msi-wmi-platform.c | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86= /msi-wmi-platform.c index 46928fb4da8a6..eaf0eb25e349b 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -121,6 +121,7 @@ enum msi_wmi_platform_method { struct msi_wmi_platform_quirk { bool shift_mode; /* Shift mode is supported */ bool charge_threshold; /* Charge threshold is supported */ + bool dual_fans; /* For devices with two hwmon fans */ int pl_min; /* Minimum PLx value */ int pl1_max; /* Maximum PL1 value */ int pl2_max; /* Maximum PL2 value */ @@ -216,6 +217,7 @@ static struct msi_wmi_platform_quirk quirk_default =3D = {}; static struct msi_wmi_platform_quirk quirk_gen1 =3D { .shift_mode =3D true, .charge_threshold =3D true, + .dual_fans =3D true, .pl_min =3D 8, .pl1_max =3D 43, .pl2_max =3D 45 @@ -223,6 +225,7 @@ static struct msi_wmi_platform_quirk quirk_gen1 =3D { static struct msi_wmi_platform_quirk quirk_gen2 =3D { .shift_mode =3D true, .charge_threshold =3D true, + .dual_fans =3D true, .pl_min =3D 8, .pl1_max =3D 30, .pl2_max =3D 37 @@ -635,6 +638,23 @@ static const struct hwmon_chip_info msi_wmi_platform_c= hip_info =3D { .info =3D msi_wmi_platform_info, }; =20 +static const struct hwmon_channel_info * const msi_wmi_platform_info_dual[= ] =3D { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT, + HWMON_F_INPUT + ), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_ENABLE, + HWMON_PWM_ENABLE + ), + NULL +}; + +static const struct hwmon_chip_info msi_wmi_platform_chip_info_dual =3D { + .ops =3D &msi_wmi_platform_ops, + .info =3D msi_wmi_platform_info_dual, +}; + static int msi_wmi_platform_profile_probe(void *drvdata, unsigned long *ch= oices) { set_bit(PLATFORM_PROFILE_LOW_POWER, choices); @@ -1227,9 +1247,11 @@ static int msi_wmi_platform_hwmon_init(struct msi_wm= i_platform_data *data) { struct device *hdev; =20 - hdev =3D devm_hwmon_device_register_with_info(&data->wdev->dev, "msi_wmi_= platform", data, - &msi_wmi_platform_chip_info, - msi_wmi_platform_hwmon_groups); + hdev =3D devm_hwmon_device_register_with_info( + &data->wdev->dev, "msi_wmi_platform", data, + data->quirks->dual_fans ? &msi_wmi_platform_chip_info_dual : + &msi_wmi_platform_chip_info, + msi_wmi_platform_hwmon_groups); =20 return PTR_ERR_OR_ZERO(hdev); } --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 379302641F8; Sun, 11 May 2025 20:45:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996302; cv=none; b=DfEMmwmPbZUuXUQ1Be7SrbiZu8G/TMQvAuQfJbnQasyUb+LwyIf+cX+bNqAZX8Kn07uQWrixb5PKRqGzG+pHPl/7bXA51sG63Llf4/8liGH1cEG5jxWB7Q17m7DItNe0GS0JCGnMPBfERKXcyT4uB87oznQGqYCqjpi6y9R03Yo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996302; c=relaxed/simple; bh=XEG6Ru/scCkzDTSQfXsnO7HPtZzCYk3A2FZ1sw1GjsI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=t5aG7Q6N1oQpJ8gyCsjERyW50rbY0HYVCoPEyYp1DQ2oSScS3MO96nBPzeEkRljMulFmXA2G1DQVDuOnKDemvr9xPnFiYF0XRym7v9yaUJ89WsRlRwvQSjwfmd6/gbrge5sTUekILCQo1Y39+7zvr3WkQeqg5xK99Kh0EyIphuY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=UF7LM+wW; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="UF7LM+wW" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id 6079F2E0A4F9; Sun, 11 May 2025 23:44:57 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996299; bh=Z+4RxwDbg216aG3i/WsnN12fdFv3dUON+3NDpYGhgB0=; h=From:To:Subject; b=UF7LM+wW6HIgq0dvPKRC3Jlq17x2YOtmp9D42db0KRMQfRTK7v96+KjJ+lhiV8gF4 oOUKPpqk9Q/7uSEP1CEB/UEcRGOMkz7H2mLTf1XhD185yWDgEVPhNKH58A51ibxJuL uA06iO2MzQitEJlYN2Cd+ody3Z2E/xQ7tqyuM6MM= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 09/10] platform/x86: msi-wmi-platform: Update header text Date: Sun, 11 May 2025 22:44:26 +0200 Message-ID: <20250511204427.327558-10-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699629921.28113.4240116989424490780@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" Update copyright information in the header and specify that this driver also applies to handhelds. Signed-off-by: Antheas Kapenekakis Reviewed-by: Armin Wolf --- drivers/platform/x86/msi-wmi-platform.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86= /msi-wmi-platform.c index eaf0eb25e349b..7dafe17d4d6be 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -1,8 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * Linux driver for WMI platform features on MSI notebooks. + * Linux driver for WMI platform features on MSI notebooks and handhelds. * - * Copyright (C) 2024 Armin Wolf + * Copyright (C) 2024-2025 Armin Wolf + * Copyright (C) 2025 Antheas Kapenekakis */ =20 #define pr_format(fmt) KBUILD_MODNAME ": " fmt --=20 2.49.0 From nobody Wed Dec 17 08:53:08 2025 Received: from linux1587.grserver.gr (linux1587.grserver.gr [185.138.42.100]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id B2958264FBB; Sun, 11 May 2025 20:45:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.138.42.100 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996305; cv=none; b=pd9Welb5iZdp0wPbJdI4zRxUK7zo4/g8mJo3tIBGZdKOhFNAS2l1KmYCF/revfkdUWrofTTT+xgk2T/e4T1I63tY0eNZLwKFmJw6lW3BgnndauqtffauXO7v43UxH2Vg8xPi95UYriYZ4BGeexzqbPte5oc7doj/MIG48o90tkA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1746996305; c=relaxed/simple; bh=orJ8j/3ajnfYd3uDV2+SFkRISKEac5ehE16CTuEw1eI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NORRHoczhuuyPRQCGxtdScdlq+LBb+D22sCrrbay+t84k/fuCRMwlhjbnyemGsHu61zWVAnYWnk++704pNsvDRdJyozXZlMTFdaLxpRLwFACR7fKBe1r5dzBokdDuYihUNHaImvP3meBQyP37xERy03bDVJE19L/ek1SYEaZLnM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev; spf=pass smtp.mailfrom=antheas.dev; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b=tmQC8lgf; arc=none smtp.client-ip=185.138.42.100 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=antheas.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=antheas.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=antheas.dev header.i=@antheas.dev header.b="tmQC8lgf" Received: from localhost.localdomain (x5996a826.customers.hiper-net.dk [89.150.168.38]) by linux1587.grserver.gr (Postfix) with ESMTPSA id AB49A2E0A4EA; Sun, 11 May 2025 23:44:59 +0300 (EEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=antheas.dev; s=default; t=1746996301; bh=Enb2hpri1htzmtmoOfLTI9UZyxly8NwM8OPV3n/jH3Q=; h=From:To:Subject; b=tmQC8lgfWNLsbtuszcxNtW+/jFiB/zKXw4HpKzaKbSQR3duV1mNKmN/0b0OEIELtI pv4vKxaN77nnP2QKM+Pf2gj54uJWnzof2S97WqDB5NjeqGF5LeGluSWgF2SFqJh+1z JencDpHdmlq+O/oEvKLwxX4ZJW6Tw27noCTT9tIo= Authentication-Results: linux1587.grserver.gr; spf=pass (sender IP is 89.150.168.38) smtp.mailfrom=lkml@antheas.dev smtp.helo=localhost.localdomain Received-SPF: pass (linux1587.grserver.gr: connection is authenticated) From: Antheas Kapenekakis To: platform-driver-x86@vger.kernel.org Cc: Armin Wolf , Jonathan Corbet , Hans de Goede , =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= , Jean Delvare , Guenter Roeck , Kurt Borja , linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org, linux-hwmon@vger.kernel.org, Antheas Kapenekakis Subject: [PATCH v1 10/10] platform/x86: msi-wmi-platform: Restore fan curves on PWM disable and unload Date: Sun, 11 May 2025 22:44:27 +0200 Message-ID: <20250511204427.327558-11-lkml@antheas.dev> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250511204427.327558-1-lkml@antheas.dev> References: <20250511204427.327558-1-lkml@antheas.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-PPP-Message-ID: <174699630136.28218.12400765318638031012@linux1587.grserver.gr> X-PPP-Vhost: antheas.dev X-Virus-Scanned: clamav-milter 0.103.11 at linux1587.grserver.gr X-Virus-Status: Clean Content-Type: text/plain; charset="utf-8" MSI software is a bit weird in that even when the manual fan curve is disabled, the fan speed is still somewhat affected by the curve. So we have to restore the fan curves on unload and PWM disable, as it is done in Windows. Suggested-by: Armin Wolf Signed-off-by: Antheas Kapenekakis --- drivers/platform/x86/msi-wmi-platform.c | 123 +++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/msi-wmi-platform.c b/drivers/platform/x86= /msi-wmi-platform.c index 7dafe17d4d6be..a917db0300c06 100644 --- a/drivers/platform/x86/msi-wmi-platform.c +++ b/drivers/platform/x86/msi-wmi-platform.c @@ -123,16 +123,25 @@ struct msi_wmi_platform_quirk { bool shift_mode; /* Shift mode is supported */ bool charge_threshold; /* Charge threshold is supported */ bool dual_fans; /* For devices with two hwmon fans */ + bool restore_curves; /* Restore factory curves on unload */ int pl_min; /* Minimum PLx value */ int pl1_max; /* Maximum PL1 value */ int pl2_max; /* Maximum PL2 value */ }; =20 +struct msi_wmi_platform_factory_curves { + u8 cpu_fan_table[32]; + u8 gpu_fan_table[32]; + u8 cpu_temp_table[32]; + u8 gpu_temp_table[32]; +}; + 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_factory_curves factory_curves; struct acpi_battery_hook battery_hook; struct device *fw_attrs_dev; struct kset *fw_attrs_kset; @@ -219,6 +228,7 @@ static struct msi_wmi_platform_quirk quirk_gen1 =3D { .shift_mode =3D true, .charge_threshold =3D true, .dual_fans =3D true, + .restore_curves =3D true, .pl_min =3D 8, .pl1_max =3D 43, .pl2_max =3D 45 @@ -227,6 +237,7 @@ static struct msi_wmi_platform_quirk quirk_gen2 =3D { .shift_mode =3D true, .charge_threshold =3D true, .dual_fans =3D true, + .restore_curves =3D true, .pl_min =3D 8, .pl1_max =3D 30, .pl2_max =3D 37 @@ -507,6 +518,94 @@ static struct attribute *msi_wmi_platform_hwmon_attrs[= ] =3D { }; ATTRIBUTE_GROUPS(msi_wmi_platform_hwmon); =20 +static int msi_wmi_platform_curves_save(struct msi_wmi_platform_data *data) +{ + int ret; + + data->factory_curves.cpu_fan_table[0] =3D + MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE; + ret =3D msi_wmi_platform_query_unlocked( + data, MSI_PLATFORM_GET_FAN, + data->factory_curves.cpu_fan_table, + sizeof(data->factory_curves.cpu_fan_table)); + if (ret < 0) + return ret; + data->factory_curves.cpu_fan_table[0] =3D + MSI_PLATFORM_FAN_SUBFEATURE_CPU_FAN_TABLE; + + data->factory_curves.gpu_fan_table[0] =3D + MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE; + ret =3D msi_wmi_platform_query_unlocked( + data, MSI_PLATFORM_GET_FAN, + data->factory_curves.gpu_fan_table, + sizeof(data->factory_curves.gpu_fan_table)); + if (ret < 0) + return ret; + data->factory_curves.gpu_fan_table[0] =3D + MSI_PLATFORM_FAN_SUBFEATURE_GPU_FAN_TABLE; + + data->factory_curves.cpu_temp_table[0] =3D + MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE; + ret =3D msi_wmi_platform_query_unlocked( + data, MSI_PLATFORM_GET_TEMPERATURE, + data->factory_curves.cpu_temp_table, + sizeof(data->factory_curves.cpu_temp_table)); + if (ret < 0) + return ret; + data->factory_curves.cpu_temp_table[0] =3D + MSI_PLATFORM_FAN_SUBFEATURE_CPU_TEMP_TABLE; + + data->factory_curves.gpu_temp_table[0] =3D + MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE; + ret =3D msi_wmi_platform_query_unlocked( + data, MSI_PLATFORM_GET_TEMPERATURE, + data->factory_curves.gpu_temp_table, + sizeof(data->factory_curves.gpu_temp_table)); + if (ret < 0) + return ret; + data->factory_curves.gpu_temp_table[0] =3D + MSI_PLATFORM_FAN_SUBFEATURE_GPU_TEMP_TABLE; + + return 0; +} + +static int msi_wmi_platform_curves_load(struct msi_wmi_platform_data *data) +{ + u8 buffer[32] =3D { }; + int ret; + + memcpy(buffer, data->factory_curves.cpu_fan_table, + sizeof(data->factory_curves.cpu_fan_table)); + ret =3D msi_wmi_platform_query_unlocked(data, MSI_PLATFORM_SET_FAN, + buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + memcpy(buffer, data->factory_curves.gpu_fan_table, + sizeof(data->factory_curves.gpu_fan_table)); + ret =3D msi_wmi_platform_query_unlocked(data, MSI_PLATFORM_SET_FAN, + buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + memcpy(buffer, data->factory_curves.cpu_temp_table, + sizeof(data->factory_curves.cpu_temp_table)); + ret =3D msi_wmi_platform_query_unlocked( + data, MSI_PLATFORM_SET_TEMPERATURE, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + memcpy(buffer, data->factory_curves.gpu_temp_table, + sizeof(data->factory_curves.gpu_temp_table)); + ret =3D msi_wmi_platform_query_unlocked( + data, MSI_PLATFORM_SET_TEMPERATURE, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + return 0; +} + + static umode_t msi_wmi_platform_is_visible(const void *drvdata, enum hwmon= _sensor_types type, u32 attr, int channel) { @@ -603,9 +702,19 @@ static int msi_wmi_platform_write(struct device *dev, = enum hwmon_sensor_types ty return -EINVAL; } =20 - return msi_wmi_platform_query_unlocked( + ret =3D msi_wmi_platform_query_unlocked( data, MSI_PLATFORM_SET_AP, buffer, sizeof(buffer)); + if (ret < 0) + return ret; + + if (val =3D=3D 2 && data->quirks->restore_curves) { + ret =3D msi_wmi_platform_curves_load(data); + if (ret < 0) + return ret; + } + + return 0; default: return -EOPNOTSUPP; } @@ -1373,6 +1482,13 @@ static int msi_wmi_platform_probe(struct wmi_device = *wdev, const void *context) =20 msi_wmi_platform_profile_setup(data); =20 + if (data->quirks->restore_curves) { + guard(mutex)(&data->wmi_lock); + ret =3D msi_wmi_platform_curves_save(data); + if (ret < 0) + return ret; + } + return msi_wmi_platform_hwmon_init(data); } =20 @@ -1382,6 +1498,11 @@ static void msi_wmi_platform_remove(struct wmi_devic= e *wdev) =20 if (data->quirks->charge_threshold) battery_hook_unregister(&data->battery_hook); + + if (data->quirks->restore_curves) { + guard(mutex)(&data->wmi_lock); + msi_wmi_platform_curves_load(data); + } } =20 static const struct wmi_device_id msi_wmi_platform_id_table[] =3D { --=20 2.49.0