* [PATCH v5 0/2] hwmon: add GPD devices sensor driver
@ 2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
0 siblings, 0 replies; 12+ messages in thread
From: Cryolitia PukNgae @ 2025-02-11 7:01 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, Cryolitia PukNgae, Jonathan Corbet
Cc: linux-kernel, linux-hwmon, linux-doc, Celeste Liu, Yao Zi,
Derek John Clark, Marcin Strągowski, someone5678,
Justin Weiss
Sensors driver for GPD Handhelds that expose fan reading and control via
hwmon sysfs.
Shenzhen GPD Technology Co., Ltd. manufactures a series of handheld
devices. This driver implements these functions through x86 port-mapped IO.
Tested-by: Marcin Strągowski <marcin@stragowski.com>
Tested-by: someone5678 <someone5678.dev@gmail.com>
Tested-by: Justin Weiss <justin@justinweiss.com>
Signed-off-by: Cryolitia PukNgae <Cryolitia@gmail.com>
---
Changes in v5:
- Rebase on kernel 6.13
- Remove all value-cache related code
- Clean up code
- Link to v4: https://lore.kernel.org/r/20240718-gpd_fan-v4-0-116e5431a9fe@gmail.com
Changes in v4:
- Apply suggest by Krzysztof Kozlowski, thanks!
- Link to v3: https://lore.kernel.org/r/20240717-gpd_fan-v3-0-8d7efb1263b7@gmail.com
Changes in v3:
- Re-arrange code, thanks to Krzysztof Kozlowski, Guenter Roeck, Yao Zi!
- Link to v2: https://lore.kernel.org/r/20240717-gpd_fan-v2-0-f7b7e6b9f21b@gmail.com
Changes in v2:
- Improved documentation, thanks to Randy Dunlap!
- Link to v1: https://lore.kernel.org/r/20240716-gpd_fan-v1-0-34051dd71a06@gmail.com
---
Cryolitia PukNgae (2):
hwmon: add GPD devices sensor driver
hwmon: document: add gpd-fan
Documentation/hwmon/gpd-fan.rst | 63 +++++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 7 +
drivers/hwmon/Kconfig | 10 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/gpd-fan.c | 611 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 693 insertions(+)
---
base-commit: ffd294d346d185b70e28b1a28abe367bbfe53c04
change-id: 20240716-gpd_fan-57f30923c884
Best regards,
--
Cryolitia PukNgae <Cryolitia@gmail.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v5 0/2] hwmon: add GPD devices sensor driver
@ 2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
0 siblings, 0 replies; 12+ messages in thread
From: Cryolitia PukNgae via B4 Relay @ 2025-02-11 7:01 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, Cryolitia PukNgae, Jonathan Corbet
Cc: linux-kernel, linux-hwmon, linux-doc, Celeste Liu, Yao Zi,
Derek John Clark, Marcin Strągowski, someone5678,
Justin Weiss
Sensors driver for GPD Handhelds that expose fan reading and control via
hwmon sysfs.
Shenzhen GPD Technology Co., Ltd. manufactures a series of handheld
devices. This driver implements these functions through x86 port-mapped IO.
Tested-by: Marcin Strągowski <marcin@stragowski.com>
Tested-by: someone5678 <someone5678.dev@gmail.com>
Tested-by: Justin Weiss <justin@justinweiss.com>
Signed-off-by: Cryolitia PukNgae <Cryolitia@gmail.com>
---
Changes in v5:
- Rebase on kernel 6.13
- Remove all value-cache related code
- Clean up code
- Link to v4: https://lore.kernel.org/r/20240718-gpd_fan-v4-0-116e5431a9fe@gmail.com
Changes in v4:
- Apply suggest by Krzysztof Kozlowski, thanks!
- Link to v3: https://lore.kernel.org/r/20240717-gpd_fan-v3-0-8d7efb1263b7@gmail.com
Changes in v3:
- Re-arrange code, thanks to Krzysztof Kozlowski, Guenter Roeck, Yao Zi!
- Link to v2: https://lore.kernel.org/r/20240717-gpd_fan-v2-0-f7b7e6b9f21b@gmail.com
Changes in v2:
- Improved documentation, thanks to Randy Dunlap!
- Link to v1: https://lore.kernel.org/r/20240716-gpd_fan-v1-0-34051dd71a06@gmail.com
---
Cryolitia PukNgae (2):
hwmon: add GPD devices sensor driver
hwmon: document: add gpd-fan
Documentation/hwmon/gpd-fan.rst | 63 +++++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 7 +
drivers/hwmon/Kconfig | 10 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/gpd-fan.c | 611 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 693 insertions(+)
---
base-commit: ffd294d346d185b70e28b1a28abe367bbfe53c04
change-id: 20240716-gpd_fan-57f30923c884
Best regards,
--
Cryolitia PukNgae <Cryolitia@gmail.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v5 1/2] hwmon: add GPD devices sensor driver
2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
@ 2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
-1 siblings, 0 replies; 12+ messages in thread
From: Cryolitia PukNgae @ 2025-02-11 7:01 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, Cryolitia PukNgae, Jonathan Corbet
Cc: linux-kernel, linux-hwmon, linux-doc, Celeste Liu, Yao Zi,
Derek John Clark, Marcin Strągowski, someone5678,
Justin Weiss
Sensors driver for GPD Handhelds that expose fan reading and control via
hwmon sysfs.
Shenzhen GPD Technology Co., Ltd. manufactures a series of handheld
devices. This driver implements these functions through x86 port-mapped IO.
Signed-off-by: Cryolitia PukNgae <Cryolitia@gmail.com>
---
MAINTAINERS | 6 +
drivers/hwmon/Kconfig | 10 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/gpd-fan.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 628 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0fa7c5728f1e64d031f4a47b6fce1db484ce0fc2..777ba74ccb07ccc0840c3cd34e7b4d98d726f964 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9762,6 +9762,12 @@ F: drivers/phy/samsung/phy-gs101-ufs.c
F: include/dt-bindings/clock/google,gs101.h
K: [gG]oogle.?[tT]ensor
+GPD FAN DRIVER
+M: Cryolitia PukNgae <Cryolitia@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/gpd-fan.c
+
GPD POCKET FAN DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: platform-driver-x86@vger.kernel.org
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index dd376602f3f19c6f258651afeffbe1bb5d9b6b72..974b341c0bdaba147370de59f510140c0c937913 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -729,6 +729,16 @@ config SENSORS_GL520SM
This driver can also be built as a module. If so, the module
will be called gl520sm.
+config SENSORS_GPD
+ tristate "GPD handhelds"
+ depends on X86
+ help
+ If you say yes here you get support for fan readings and
+ control over GPD handheld devices.
+
+ Can also be built as a module. In that case it will be
+ called gpd-fan.
+
config SENSORS_G760A
tristate "GMT G760A"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b827b92f2a7844418f3f3b6434a63b744b52c33d..cd512c19caa9737a2926a3d4860f65b65cd013c3 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_SENSORS_GIGABYTE_WATERFORCE) += gigabyte_waterforce.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o
+obj-$(CONFIG_SENSORS_GPD) += gpd-fan.o
obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_GXP_FAN_CTRL) += gxp-fan-ctrl.o
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c
new file mode 100644
index 0000000000000000000000000000000000000000..8d54ebf5defa45f4d01c0dd7786b1908bca55ec0
--- /dev/null
+++ b/drivers/hwmon/gpd-fan.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/* Platform driver for GPD devices that expose fan control via hwmon sysfs.
+ *
+ * Fan control is provided via pwm interface in the range [0-255].
+ * Each model has a different range in the EC, the written value is scaled to accommodate for that.
+ *
+ * Based on this repo:
+ * https://github.com/Cryolitia/gpd-fan-driver
+ *
+ * Copyright (c) 2024 Cryolitia PukNgae
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/hwmon.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "gpdfan"
+
+#define GPD_PWM_CTR_OFFSET 0x1841
+
+// model param, see document
+static char *gpd_fan_board = "";
+module_param(gpd_fan_board, charp, 0444);
+
+// EC read/write locker
+// Should never access EC at the same time, otherwise system down.
+static DEFINE_MUTEX(gpd_fan_lock);
+
+enum gpd_board {
+ win_mini,
+ win4_6800u,
+ win_max_2,
+};
+
+enum FAN_PWM_ENABLE {
+ DISABLE = 0,
+ MANUAL = 1,
+ AUTOMATIC = 2,
+};
+
+static struct {
+ enum FAN_PWM_ENABLE pwm_enable;
+ u8 pwm_value;
+
+ const struct gpd_board_drvdata *drvdata;
+} gpd_driver_priv;
+
+struct gpd_board_drvdata {
+ const char *board_name; /* Board name for module param comparison */
+ const enum gpd_board board;
+
+ const u8 addr_port;
+ const u8 data_port;
+ const u16 manual_control_enable;
+ const u16 rpm_read;
+ const u16 pwm_write;
+ const u16 pwm_max;
+};
+
+static struct gpd_board_drvdata gpd_win_mini_drvdata = {
+ .board_name = "win_mini",
+ .board = win_mini,
+
+ .addr_port = 0x4E,
+ .data_port = 0x4F,
+ .manual_control_enable = 0x047A,
+ .rpm_read = 0x0478,
+ .pwm_write = 0x047A,
+ .pwm_max = 244,
+};
+
+static struct gpd_board_drvdata gpd_win4_drvdata = {
+ .board_name = "win4",
+ .board = win4_6800u,
+
+ .addr_port = 0x2E,
+ .data_port = 0x2F,
+ .manual_control_enable = 0xC311,
+ .rpm_read = 0xC880,
+ .pwm_write = 0xC311,
+ .pwm_max = 127,
+};
+
+static struct gpd_board_drvdata gpd_wm2_drvdata = {
+ .board_name = "wm2",
+ .board = win_max_2,
+
+ .addr_port = 0x4E,
+ .data_port = 0x4F,
+ .manual_control_enable = 0x0275,
+ .rpm_read = 0x0218,
+ .pwm_write = 0x1809,
+ .pwm_max = 184,
+};
+
+static const struct dmi_system_id dmi_table[] = {
+ {
+ // GPD Win Mini
+ // GPD Win Mini with AMD Ryzen 8840U
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1617-01")
+ },
+ .driver_data = &gpd_win_mini_drvdata,
+ },
+ {
+ // GPD Win 4 with AMD Ryzen 6800U
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
+ DMI_MATCH(DMI_BOARD_VERSION, "Default string"),
+ },
+ .driver_data = &gpd_win4_drvdata,
+ },
+ {
+ // GPD Win 4 with Ryzen 7840U
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
+ DMI_MATCH(DMI_BOARD_VERSION, "Ver. 1.0"),
+ },
+ // Since 7840U, win4 uses the same drvdata as wm2
+ .driver_data = &gpd_wm2_drvdata,
+ },
+ {
+ // GPD Win Max 2 with Ryzen 6800U
+ // GPD Win Max 2 2023 with Ryzen 7840U
+ // GPD Win Max 2 2024 with Ryzen 8840U
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"),
+ },
+ .driver_data = &gpd_wm2_drvdata,
+ },
+ {}
+};
+
+static const struct gpd_board_drvdata *gpd_module_drvdata[] = {
+ &gpd_win_mini_drvdata, &gpd_win4_drvdata, &gpd_wm2_drvdata, NULL
+};
+
+/* Helper functions to handle EC read/write */
+static int gpd_ecram_read(const struct gpd_board_drvdata *drvdata, u16 offset,
+ u8 *val)
+{
+ int ret;
+ u16 addr_port = drvdata->addr_port;
+ u16 data_port = drvdata->data_port;
+
+ ret = mutex_lock_interruptible(&gpd_fan_lock);
+
+ if (ret)
+ return ret;
+
+ outb(0x2E, addr_port);
+ outb(0x11, data_port);
+ outb(0x2F, addr_port);
+ outb((u8)((offset >> 8) & 0xFF), data_port);
+
+ outb(0x2E, addr_port);
+ outb(0x10, data_port);
+ outb(0x2F, addr_port);
+ outb((u8)(offset & 0xFF), data_port);
+
+ outb(0x2E, addr_port);
+ outb(0x12, data_port);
+ outb(0x2F, addr_port);
+ *val = inb(data_port);
+
+ mutex_unlock(&gpd_fan_lock);
+ return 0;
+}
+
+static int gpd_ecram_write(const struct gpd_board_drvdata *drvdata, u16 offset,
+ u8 value)
+{
+ int ret;
+ u16 addr_port = drvdata->addr_port;
+ u16 data_port = drvdata->data_port;
+
+ ret = mutex_lock_interruptible(&gpd_fan_lock);
+
+ if (ret)
+ return ret;
+
+ outb(0x2E, addr_port);
+ outb(0x11, data_port);
+ outb(0x2F, addr_port);
+ outb((u8)((offset >> 8) & 0xFF), data_port);
+
+ outb(0x2E, addr_port);
+ outb(0x10, data_port);
+ outb(0x2F, addr_port);
+ outb((u8)(offset & 0xFF), data_port);
+
+ outb(0x2E, addr_port);
+ outb(0x12, data_port);
+ outb(0x2F, addr_port);
+ outb(value, data_port);
+
+ mutex_unlock(&gpd_fan_lock);
+ return 0;
+}
+
+static int gpd_generic_read_rpm_uncached(void)
+{
+ u8 high, low;
+ int ret;
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+
+ ret = gpd_ecram_read(drvdata, drvdata->rpm_read, &high);
+ if (ret)
+ return ret;
+ ret = gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low);
+ if (ret)
+ return ret;
+
+ return (u16)high << 8 | low;
+}
+
+static int gpd_win4_read_rpm_uncached(void)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+ u8 PWMCTR;
+ int ret;
+
+ gpd_ecram_read(drvdata, GPD_PWM_CTR_OFFSET, &PWMCTR);
+ if (PWMCTR != 0x7F)
+ gpd_ecram_write(drvdata, GPD_PWM_CTR_OFFSET, 0x7F);
+
+ ret = gpd_generic_read_rpm_uncached();
+
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0) {
+ //re-init EC
+ u8 chip_id;
+
+ gpd_ecram_read(drvdata, 0x2000, &chip_id);
+ if (chip_id == 0x55) {
+ u8 chip_ver;
+
+ if (gpd_ecram_read(drvdata, 0x1060, &chip_ver))
+ gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80);
+ }
+ }
+ return ret;
+}
+
+static int gpd_wm2_read_rpm_uncached(void)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+
+ for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2;
+ pwm_ctr_offset++) {
+ u8 PWMCTR;
+
+ gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR);
+ if (PWMCTR != 0xB8)
+ gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8);
+ }
+ return gpd_generic_read_rpm_uncached();
+}
+
+// Read value for fan1_input
+static int gpd_read_rpm(void)
+{
+ switch (gpd_driver_priv.drvdata->board) {
+ case win_mini: {
+ return gpd_generic_read_rpm_uncached();
+ }
+ case win4_6800u: {
+ return gpd_win4_read_rpm_uncached();
+ }
+ case win_max_2: {
+ return gpd_wm2_read_rpm_uncached();
+ }
+ }
+ return 0;
+}
+
+static int gpd_wm2_read_pwm_uncached(void)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+ u8 var;
+ int ret = gpd_ecram_read(drvdata, drvdata->pwm_write, &var);
+
+ if (ret < 0)
+ return ret;
+
+ return var * 255 / drvdata->pwm_max;
+}
+
+// Read value for pwm1
+static int gpd_read_pwm(void)
+{
+ switch (gpd_driver_priv.drvdata->board) {
+ case win_mini:
+ case win4_6800u:
+ return gpd_driver_priv.pwm_value;
+ case win_max_2:
+ return gpd_wm2_read_pwm_uncached();
+ }
+ return 0;
+}
+
+static int gpd_generic_write_pwm(u8 val)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+ u8 actual;
+
+ // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it.
+ actual = val * (drvdata->pwm_max - 1) / 255 + 1;
+ return gpd_ecram_write(drvdata, drvdata->pwm_write, actual);
+}
+
+static int gpd_win_mini_write_pwm(u8 val)
+{
+ if (gpd_driver_priv.pwm_enable == MANUAL)
+ return gpd_generic_write_pwm(val);
+ else
+ return -EPERM;
+}
+
+static int gpd_wm2_write_pwm(u8 val)
+{
+ if (gpd_driver_priv.pwm_enable != DISABLE)
+ return gpd_generic_write_pwm(val);
+ else
+ return -EPERM;
+}
+
+// Write value for pwm1
+static int gpd_write_pwm(u8 val)
+{
+ switch (gpd_driver_priv.drvdata->board) {
+ case win_mini:
+ return gpd_win_mini_write_pwm(val);
+ case win4_6800u:
+ return gpd_generic_write_pwm(val);
+ case win_max_2:
+ return gpd_wm2_write_pwm(val);
+ }
+ return 0;
+}
+
+static int gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
+{
+ switch (pwm_enable) {
+ case DISABLE:
+ return gpd_generic_write_pwm(255);
+ case MANUAL:
+ return gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
+ case AUTOMATIC:
+ const struct gpd_board_drvdata *drvdata = gpd_driver_priv.drvdata;
+
+ return gpd_ecram_write(drvdata, drvdata->pwm_write, 0);
+ }
+ return 0;
+}
+
+static int gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+ int ret;
+
+ switch (enable) {
+ case DISABLE: {
+ ret = gpd_generic_write_pwm(255);
+
+ if (ret)
+ return ret;
+
+ return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
+ }
+ case MANUAL: {
+ ret = gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
+
+ if (ret)
+ return ret;
+
+ return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
+ }
+ case AUTOMATIC: {
+ ret = gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0);
+
+ return ret;
+ }
+ }
+ return 0;
+}
+
+// Write value for pwm1_enable
+static int gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable)
+{
+ switch (gpd_driver_priv.drvdata->board) {
+ case win_mini:
+ case win4_6800u:
+ return gpd_win_mini_set_pwm_enable(enable);
+ case win_max_2:
+ return gpd_wm2_set_pwm_enable(enable);
+ }
+ return 0;
+}
+
+static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata,
+ enum hwmon_sensor_types type, u32 attr,
+ __always_unused int channel)
+{
+ if (type == hwmon_fan && attr == hwmon_fan_input) {
+ return 0444;
+ } else if (type == hwmon_pwm) {
+ switch (attr) {
+ case hwmon_pwm_enable:
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int gpd_fan_hwmon_read(__always_unused struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ __always_unused int channel, long *val)
+{
+ if (type == hwmon_fan) {
+ if (attr == hwmon_fan_input) {
+ int ret = gpd_read_rpm();
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return 0;
+ }
+ return -EOPNOTSUPP;
+ }
+ if (type == hwmon_pwm) {
+ switch (attr) {
+ case hwmon_pwm_enable:
+ *val = gpd_driver_priv.pwm_enable;
+ return 0;
+ case hwmon_pwm_input:
+ int ret = gpd_read_pwm();
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+ return -EOPNOTSUPP;
+}
+
+static int gpd_fan_hwmon_write(__always_unused struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ __always_unused int channel, long val)
+{
+ if (type == hwmon_pwm) {
+ switch (attr) {
+ case hwmon_pwm_enable:
+ if (!in_range(val, 0, 3))
+ return -EINVAL;
+ gpd_driver_priv.pwm_enable = val;
+ return gpd_set_pwm_enable(gpd_driver_priv.pwm_enable);
+ case hwmon_pwm_input:
+ u8 var = clamp_val(val, 0, 255);
+
+ gpd_driver_priv.pwm_value = var;
+ return gpd_write_pwm(var);
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+ return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops gpd_fan_ops = {
+ .is_visible = gpd_fan_hwmon_is_visible,
+ .read = gpd_fan_hwmon_read,
+ .write = gpd_fan_hwmon_write,
+};
+
+static const struct hwmon_channel_info *gpd_fan_hwmon_channel_info[] = {
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), NULL
+};
+
+static struct hwmon_chip_info gpd_fan_chip_info = {
+ .ops = &gpd_fan_ops,
+ .info = gpd_fan_hwmon_channel_info
+};
+
+static int gpd_fan_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpd_driver_priv *data;
+ const struct resource *plat_res;
+ const struct device *dev_reg;
+ const struct resource *region_res;
+
+ data = dev_get_platdata(&pdev->dev);
+ if (IS_ERR(data))
+ return -ENODEV;
+
+ plat_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (IS_ERR(plat_res))
+ return dev_err_probe(dev, PTR_ERR(plat_res),
+ "Failed to get platform resource\n");
+
+ region_res = devm_request_region(dev, plat_res->start,
+ resource_size(plat_res), DRIVER_NAME);
+ if (IS_ERR(region_res))
+ return dev_err_probe(dev, PTR_ERR(region_res),
+ "Failed to request region\n");
+
+ dev_reg = devm_hwmon_device_register_with_info(dev,
+ DRIVER_NAME,
+ data,
+ &gpd_fan_chip_info,
+ NULL);
+ if (IS_ERR(dev_reg))
+ return dev_err_probe(dev, PTR_ERR(region_res),
+ "Failed to register hwmon device\n");
+
+ return 0;
+}
+
+static void gpd_fan_remove(__always_unused struct platform_device *pdev)
+{
+ gpd_driver_priv.pwm_enable = AUTOMATIC;
+ gpd_set_pwm_enable(AUTOMATIC);
+}
+
+static struct platform_driver gpd_fan_driver = {
+ .probe = gpd_fan_probe,
+ .remove = gpd_fan_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+};
+
+static struct platform_device *gpd_fan_platform_device;
+
+static int __init gpd_fan_init(void)
+{
+ const struct gpd_board_drvdata *match = NULL;
+
+ for (const struct gpd_board_drvdata **p = gpd_module_drvdata; *p; p++) {
+ if (strcmp(gpd_fan_board, (*p)->board_name) == 0) {
+ match = *p;
+ break;
+ }
+ }
+
+ if (!match)
+ match = dmi_first_match(dmi_table)->driver_data;
+
+ if (!match)
+ return -ENODEV;
+
+ gpd_driver_priv.pwm_enable = AUTOMATIC;
+ gpd_driver_priv.pwm_value = 255;
+ gpd_driver_priv.drvdata = match;
+
+ struct resource gpd_fan_resources[] = {
+ {
+ .start = match->addr_port,
+ .end = match->data_port,
+ .flags = IORESOURCE_IO,
+ },
+ };
+
+ gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver,
+ gpd_fan_probe,
+ gpd_fan_resources,
+ 1, NULL, 0);
+
+ if (IS_ERR(gpd_fan_platform_device)) {
+ pr_warn("Failed to create platform device\n");
+ return PTR_ERR(gpd_fan_platform_device);
+ }
+
+ return 0;
+}
+
+static void __exit gpd_fan_exit(void)
+{
+ platform_device_unregister(gpd_fan_platform_device);
+ platform_driver_unregister(&gpd_fan_driver);
+}
+
+MODULE_DEVICE_TABLE(dmi, dmi_table);
+
+module_init(gpd_fan_init)
+module_exit(gpd_fan_exit)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cryolitia <Cryolitia@gmail.com>");
+MODULE_DESCRIPTION("GPD Devices fan control driver");
--
2.47.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v5 1/2] hwmon: add GPD devices sensor driver
@ 2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
0 siblings, 0 replies; 12+ messages in thread
From: Cryolitia PukNgae via B4 Relay @ 2025-02-11 7:01 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, Cryolitia PukNgae, Jonathan Corbet
Cc: linux-kernel, linux-hwmon, linux-doc, Celeste Liu, Yao Zi,
Derek John Clark, Marcin Strągowski, someone5678,
Justin Weiss
From: Cryolitia PukNgae <Cryolitia@gmail.com>
Sensors driver for GPD Handhelds that expose fan reading and control via
hwmon sysfs.
Shenzhen GPD Technology Co., Ltd. manufactures a series of handheld
devices. This driver implements these functions through x86 port-mapped IO.
Signed-off-by: Cryolitia PukNgae <Cryolitia@gmail.com>
---
MAINTAINERS | 6 +
drivers/hwmon/Kconfig | 10 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/gpd-fan.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 628 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0fa7c5728f1e64d031f4a47b6fce1db484ce0fc2..777ba74ccb07ccc0840c3cd34e7b4d98d726f964 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9762,6 +9762,12 @@ F: drivers/phy/samsung/phy-gs101-ufs.c
F: include/dt-bindings/clock/google,gs101.h
K: [gG]oogle.?[tT]ensor
+GPD FAN DRIVER
+M: Cryolitia PukNgae <Cryolitia@gmail.com>
+L: linux-hwmon@vger.kernel.org
+S: Maintained
+F: drivers/hwmon/gpd-fan.c
+
GPD POCKET FAN DRIVER
M: Hans de Goede <hdegoede@redhat.com>
L: platform-driver-x86@vger.kernel.org
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index dd376602f3f19c6f258651afeffbe1bb5d9b6b72..974b341c0bdaba147370de59f510140c0c937913 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -729,6 +729,16 @@ config SENSORS_GL520SM
This driver can also be built as a module. If so, the module
will be called gl520sm.
+config SENSORS_GPD
+ tristate "GPD handhelds"
+ depends on X86
+ help
+ If you say yes here you get support for fan readings and
+ control over GPD handheld devices.
+
+ Can also be built as a module. In that case it will be
+ called gpd-fan.
+
config SENSORS_G760A
tristate "GMT G760A"
depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b827b92f2a7844418f3f3b6434a63b744b52c33d..cd512c19caa9737a2926a3d4860f65b65cd013c3 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -87,6 +87,7 @@ obj-$(CONFIG_SENSORS_GIGABYTE_WATERFORCE) += gigabyte_waterforce.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o
+obj-$(CONFIG_SENSORS_GPD) += gpd-fan.o
obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_GXP_FAN_CTRL) += gxp-fan-ctrl.o
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c
new file mode 100644
index 0000000000000000000000000000000000000000..8d54ebf5defa45f4d01c0dd7786b1908bca55ec0
--- /dev/null
+++ b/drivers/hwmon/gpd-fan.c
@@ -0,0 +1,611 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/* Platform driver for GPD devices that expose fan control via hwmon sysfs.
+ *
+ * Fan control is provided via pwm interface in the range [0-255].
+ * Each model has a different range in the EC, the written value is scaled to accommodate for that.
+ *
+ * Based on this repo:
+ * https://github.com/Cryolitia/gpd-fan-driver
+ *
+ * Copyright (c) 2024 Cryolitia PukNgae
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/hwmon.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "gpdfan"
+
+#define GPD_PWM_CTR_OFFSET 0x1841
+
+// model param, see document
+static char *gpd_fan_board = "";
+module_param(gpd_fan_board, charp, 0444);
+
+// EC read/write locker
+// Should never access EC at the same time, otherwise system down.
+static DEFINE_MUTEX(gpd_fan_lock);
+
+enum gpd_board {
+ win_mini,
+ win4_6800u,
+ win_max_2,
+};
+
+enum FAN_PWM_ENABLE {
+ DISABLE = 0,
+ MANUAL = 1,
+ AUTOMATIC = 2,
+};
+
+static struct {
+ enum FAN_PWM_ENABLE pwm_enable;
+ u8 pwm_value;
+
+ const struct gpd_board_drvdata *drvdata;
+} gpd_driver_priv;
+
+struct gpd_board_drvdata {
+ const char *board_name; /* Board name for module param comparison */
+ const enum gpd_board board;
+
+ const u8 addr_port;
+ const u8 data_port;
+ const u16 manual_control_enable;
+ const u16 rpm_read;
+ const u16 pwm_write;
+ const u16 pwm_max;
+};
+
+static struct gpd_board_drvdata gpd_win_mini_drvdata = {
+ .board_name = "win_mini",
+ .board = win_mini,
+
+ .addr_port = 0x4E,
+ .data_port = 0x4F,
+ .manual_control_enable = 0x047A,
+ .rpm_read = 0x0478,
+ .pwm_write = 0x047A,
+ .pwm_max = 244,
+};
+
+static struct gpd_board_drvdata gpd_win4_drvdata = {
+ .board_name = "win4",
+ .board = win4_6800u,
+
+ .addr_port = 0x2E,
+ .data_port = 0x2F,
+ .manual_control_enable = 0xC311,
+ .rpm_read = 0xC880,
+ .pwm_write = 0xC311,
+ .pwm_max = 127,
+};
+
+static struct gpd_board_drvdata gpd_wm2_drvdata = {
+ .board_name = "wm2",
+ .board = win_max_2,
+
+ .addr_port = 0x4E,
+ .data_port = 0x4F,
+ .manual_control_enable = 0x0275,
+ .rpm_read = 0x0218,
+ .pwm_write = 0x1809,
+ .pwm_max = 184,
+};
+
+static const struct dmi_system_id dmi_table[] = {
+ {
+ // GPD Win Mini
+ // GPD Win Mini with AMD Ryzen 8840U
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1617-01")
+ },
+ .driver_data = &gpd_win_mini_drvdata,
+ },
+ {
+ // GPD Win 4 with AMD Ryzen 6800U
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
+ DMI_MATCH(DMI_BOARD_VERSION, "Default string"),
+ },
+ .driver_data = &gpd_win4_drvdata,
+ },
+ {
+ // GPD Win 4 with Ryzen 7840U
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
+ DMI_MATCH(DMI_BOARD_VERSION, "Ver. 1.0"),
+ },
+ // Since 7840U, win4 uses the same drvdata as wm2
+ .driver_data = &gpd_wm2_drvdata,
+ },
+ {
+ // GPD Win Max 2 with Ryzen 6800U
+ // GPD Win Max 2 2023 with Ryzen 7840U
+ // GPD Win Max 2 2024 with Ryzen 8840U
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"),
+ },
+ .driver_data = &gpd_wm2_drvdata,
+ },
+ {}
+};
+
+static const struct gpd_board_drvdata *gpd_module_drvdata[] = {
+ &gpd_win_mini_drvdata, &gpd_win4_drvdata, &gpd_wm2_drvdata, NULL
+};
+
+/* Helper functions to handle EC read/write */
+static int gpd_ecram_read(const struct gpd_board_drvdata *drvdata, u16 offset,
+ u8 *val)
+{
+ int ret;
+ u16 addr_port = drvdata->addr_port;
+ u16 data_port = drvdata->data_port;
+
+ ret = mutex_lock_interruptible(&gpd_fan_lock);
+
+ if (ret)
+ return ret;
+
+ outb(0x2E, addr_port);
+ outb(0x11, data_port);
+ outb(0x2F, addr_port);
+ outb((u8)((offset >> 8) & 0xFF), data_port);
+
+ outb(0x2E, addr_port);
+ outb(0x10, data_port);
+ outb(0x2F, addr_port);
+ outb((u8)(offset & 0xFF), data_port);
+
+ outb(0x2E, addr_port);
+ outb(0x12, data_port);
+ outb(0x2F, addr_port);
+ *val = inb(data_port);
+
+ mutex_unlock(&gpd_fan_lock);
+ return 0;
+}
+
+static int gpd_ecram_write(const struct gpd_board_drvdata *drvdata, u16 offset,
+ u8 value)
+{
+ int ret;
+ u16 addr_port = drvdata->addr_port;
+ u16 data_port = drvdata->data_port;
+
+ ret = mutex_lock_interruptible(&gpd_fan_lock);
+
+ if (ret)
+ return ret;
+
+ outb(0x2E, addr_port);
+ outb(0x11, data_port);
+ outb(0x2F, addr_port);
+ outb((u8)((offset >> 8) & 0xFF), data_port);
+
+ outb(0x2E, addr_port);
+ outb(0x10, data_port);
+ outb(0x2F, addr_port);
+ outb((u8)(offset & 0xFF), data_port);
+
+ outb(0x2E, addr_port);
+ outb(0x12, data_port);
+ outb(0x2F, addr_port);
+ outb(value, data_port);
+
+ mutex_unlock(&gpd_fan_lock);
+ return 0;
+}
+
+static int gpd_generic_read_rpm_uncached(void)
+{
+ u8 high, low;
+ int ret;
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+
+ ret = gpd_ecram_read(drvdata, drvdata->rpm_read, &high);
+ if (ret)
+ return ret;
+ ret = gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low);
+ if (ret)
+ return ret;
+
+ return (u16)high << 8 | low;
+}
+
+static int gpd_win4_read_rpm_uncached(void)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+ u8 PWMCTR;
+ int ret;
+
+ gpd_ecram_read(drvdata, GPD_PWM_CTR_OFFSET, &PWMCTR);
+ if (PWMCTR != 0x7F)
+ gpd_ecram_write(drvdata, GPD_PWM_CTR_OFFSET, 0x7F);
+
+ ret = gpd_generic_read_rpm_uncached();
+
+ if (ret < 0)
+ return ret;
+
+ if (ret == 0) {
+ //re-init EC
+ u8 chip_id;
+
+ gpd_ecram_read(drvdata, 0x2000, &chip_id);
+ if (chip_id == 0x55) {
+ u8 chip_ver;
+
+ if (gpd_ecram_read(drvdata, 0x1060, &chip_ver))
+ gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80);
+ }
+ }
+ return ret;
+}
+
+static int gpd_wm2_read_rpm_uncached(void)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+
+ for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2;
+ pwm_ctr_offset++) {
+ u8 PWMCTR;
+
+ gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR);
+ if (PWMCTR != 0xB8)
+ gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8);
+ }
+ return gpd_generic_read_rpm_uncached();
+}
+
+// Read value for fan1_input
+static int gpd_read_rpm(void)
+{
+ switch (gpd_driver_priv.drvdata->board) {
+ case win_mini: {
+ return gpd_generic_read_rpm_uncached();
+ }
+ case win4_6800u: {
+ return gpd_win4_read_rpm_uncached();
+ }
+ case win_max_2: {
+ return gpd_wm2_read_rpm_uncached();
+ }
+ }
+ return 0;
+}
+
+static int gpd_wm2_read_pwm_uncached(void)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+ u8 var;
+ int ret = gpd_ecram_read(drvdata, drvdata->pwm_write, &var);
+
+ if (ret < 0)
+ return ret;
+
+ return var * 255 / drvdata->pwm_max;
+}
+
+// Read value for pwm1
+static int gpd_read_pwm(void)
+{
+ switch (gpd_driver_priv.drvdata->board) {
+ case win_mini:
+ case win4_6800u:
+ return gpd_driver_priv.pwm_value;
+ case win_max_2:
+ return gpd_wm2_read_pwm_uncached();
+ }
+ return 0;
+}
+
+static int gpd_generic_write_pwm(u8 val)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+ u8 actual;
+
+ // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it.
+ actual = val * (drvdata->pwm_max - 1) / 255 + 1;
+ return gpd_ecram_write(drvdata, drvdata->pwm_write, actual);
+}
+
+static int gpd_win_mini_write_pwm(u8 val)
+{
+ if (gpd_driver_priv.pwm_enable == MANUAL)
+ return gpd_generic_write_pwm(val);
+ else
+ return -EPERM;
+}
+
+static int gpd_wm2_write_pwm(u8 val)
+{
+ if (gpd_driver_priv.pwm_enable != DISABLE)
+ return gpd_generic_write_pwm(val);
+ else
+ return -EPERM;
+}
+
+// Write value for pwm1
+static int gpd_write_pwm(u8 val)
+{
+ switch (gpd_driver_priv.drvdata->board) {
+ case win_mini:
+ return gpd_win_mini_write_pwm(val);
+ case win4_6800u:
+ return gpd_generic_write_pwm(val);
+ case win_max_2:
+ return gpd_wm2_write_pwm(val);
+ }
+ return 0;
+}
+
+static int gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
+{
+ switch (pwm_enable) {
+ case DISABLE:
+ return gpd_generic_write_pwm(255);
+ case MANUAL:
+ return gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
+ case AUTOMATIC:
+ const struct gpd_board_drvdata *drvdata = gpd_driver_priv.drvdata;
+
+ return gpd_ecram_write(drvdata, drvdata->pwm_write, 0);
+ }
+ return 0;
+}
+
+static int gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable)
+{
+ const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
+ int ret;
+
+ switch (enable) {
+ case DISABLE: {
+ ret = gpd_generic_write_pwm(255);
+
+ if (ret)
+ return ret;
+
+ return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
+ }
+ case MANUAL: {
+ ret = gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
+
+ if (ret)
+ return ret;
+
+ return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
+ }
+ case AUTOMATIC: {
+ ret = gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0);
+
+ return ret;
+ }
+ }
+ return 0;
+}
+
+// Write value for pwm1_enable
+static int gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable)
+{
+ switch (gpd_driver_priv.drvdata->board) {
+ case win_mini:
+ case win4_6800u:
+ return gpd_win_mini_set_pwm_enable(enable);
+ case win_max_2:
+ return gpd_wm2_set_pwm_enable(enable);
+ }
+ return 0;
+}
+
+static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata,
+ enum hwmon_sensor_types type, u32 attr,
+ __always_unused int channel)
+{
+ if (type == hwmon_fan && attr == hwmon_fan_input) {
+ return 0444;
+ } else if (type == hwmon_pwm) {
+ switch (attr) {
+ case hwmon_pwm_enable:
+ case hwmon_pwm_input:
+ return 0644;
+ default:
+ return 0;
+ }
+ }
+ return 0;
+}
+
+static int gpd_fan_hwmon_read(__always_unused struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ __always_unused int channel, long *val)
+{
+ if (type == hwmon_fan) {
+ if (attr == hwmon_fan_input) {
+ int ret = gpd_read_rpm();
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return 0;
+ }
+ return -EOPNOTSUPP;
+ }
+ if (type == hwmon_pwm) {
+ switch (attr) {
+ case hwmon_pwm_enable:
+ *val = gpd_driver_priv.pwm_enable;
+ return 0;
+ case hwmon_pwm_input:
+ int ret = gpd_read_pwm();
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+ return -EOPNOTSUPP;
+}
+
+static int gpd_fan_hwmon_write(__always_unused struct device *dev,
+ enum hwmon_sensor_types type, u32 attr,
+ __always_unused int channel, long val)
+{
+ if (type == hwmon_pwm) {
+ switch (attr) {
+ case hwmon_pwm_enable:
+ if (!in_range(val, 0, 3))
+ return -EINVAL;
+ gpd_driver_priv.pwm_enable = val;
+ return gpd_set_pwm_enable(gpd_driver_priv.pwm_enable);
+ case hwmon_pwm_input:
+ u8 var = clamp_val(val, 0, 255);
+
+ gpd_driver_priv.pwm_value = var;
+ return gpd_write_pwm(var);
+ default:
+ return -EOPNOTSUPP;
+ }
+ }
+ return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops gpd_fan_ops = {
+ .is_visible = gpd_fan_hwmon_is_visible,
+ .read = gpd_fan_hwmon_read,
+ .write = gpd_fan_hwmon_write,
+};
+
+static const struct hwmon_channel_info *gpd_fan_hwmon_channel_info[] = {
+ HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
+ HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), NULL
+};
+
+static struct hwmon_chip_info gpd_fan_chip_info = {
+ .ops = &gpd_fan_ops,
+ .info = gpd_fan_hwmon_channel_info
+};
+
+static int gpd_fan_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct gpd_driver_priv *data;
+ const struct resource *plat_res;
+ const struct device *dev_reg;
+ const struct resource *region_res;
+
+ data = dev_get_platdata(&pdev->dev);
+ if (IS_ERR(data))
+ return -ENODEV;
+
+ plat_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (IS_ERR(plat_res))
+ return dev_err_probe(dev, PTR_ERR(plat_res),
+ "Failed to get platform resource\n");
+
+ region_res = devm_request_region(dev, plat_res->start,
+ resource_size(plat_res), DRIVER_NAME);
+ if (IS_ERR(region_res))
+ return dev_err_probe(dev, PTR_ERR(region_res),
+ "Failed to request region\n");
+
+ dev_reg = devm_hwmon_device_register_with_info(dev,
+ DRIVER_NAME,
+ data,
+ &gpd_fan_chip_info,
+ NULL);
+ if (IS_ERR(dev_reg))
+ return dev_err_probe(dev, PTR_ERR(region_res),
+ "Failed to register hwmon device\n");
+
+ return 0;
+}
+
+static void gpd_fan_remove(__always_unused struct platform_device *pdev)
+{
+ gpd_driver_priv.pwm_enable = AUTOMATIC;
+ gpd_set_pwm_enable(AUTOMATIC);
+}
+
+static struct platform_driver gpd_fan_driver = {
+ .probe = gpd_fan_probe,
+ .remove = gpd_fan_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ },
+};
+
+static struct platform_device *gpd_fan_platform_device;
+
+static int __init gpd_fan_init(void)
+{
+ const struct gpd_board_drvdata *match = NULL;
+
+ for (const struct gpd_board_drvdata **p = gpd_module_drvdata; *p; p++) {
+ if (strcmp(gpd_fan_board, (*p)->board_name) == 0) {
+ match = *p;
+ break;
+ }
+ }
+
+ if (!match)
+ match = dmi_first_match(dmi_table)->driver_data;
+
+ if (!match)
+ return -ENODEV;
+
+ gpd_driver_priv.pwm_enable = AUTOMATIC;
+ gpd_driver_priv.pwm_value = 255;
+ gpd_driver_priv.drvdata = match;
+
+ struct resource gpd_fan_resources[] = {
+ {
+ .start = match->addr_port,
+ .end = match->data_port,
+ .flags = IORESOURCE_IO,
+ },
+ };
+
+ gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver,
+ gpd_fan_probe,
+ gpd_fan_resources,
+ 1, NULL, 0);
+
+ if (IS_ERR(gpd_fan_platform_device)) {
+ pr_warn("Failed to create platform device\n");
+ return PTR_ERR(gpd_fan_platform_device);
+ }
+
+ return 0;
+}
+
+static void __exit gpd_fan_exit(void)
+{
+ platform_device_unregister(gpd_fan_platform_device);
+ platform_driver_unregister(&gpd_fan_driver);
+}
+
+MODULE_DEVICE_TABLE(dmi, dmi_table);
+
+module_init(gpd_fan_init)
+module_exit(gpd_fan_exit)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cryolitia <Cryolitia@gmail.com>");
+MODULE_DESCRIPTION("GPD Devices fan control driver");
--
2.47.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v5 2/2] hwmon: document: add gpd-fan
2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
@ 2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
-1 siblings, 0 replies; 12+ messages in thread
From: Cryolitia PukNgae @ 2025-02-11 7:01 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, Cryolitia PukNgae, Jonathan Corbet
Cc: linux-kernel, linux-hwmon, linux-doc, Celeste Liu, Yao Zi,
Derek John Clark, Marcin Strągowski, someone5678,
Justin Weiss
Add GPD fan driver document
Signed-off-by: Cryolitia PukNgae <Cryolitia@gmail.com>
---
Documentation/hwmon/gpd-fan.rst | 63 +++++++++++++++++++++++++++++++++++++++++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 1 +
3 files changed, 65 insertions(+)
diff --git a/Documentation/hwmon/gpd-fan.rst b/Documentation/hwmon/gpd-fan.rst
new file mode 100644
index 0000000000000000000000000000000000000000..9d478c7350b5fb88e43e54407503dac441328142
--- /dev/null
+++ b/Documentation/hwmon/gpd-fan.rst
@@ -0,0 +1,63 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver gpd-fan
+=========================
+
+Author:
+ - Cryolitia PukNgae <Cryolitia@gmail.com>
+
+Description
+------------
+
+Handheld devices from Shenzhen GPD Technology Co., Ltd. provide fan readings and fan control through
+their embedded controllers.
+
+Supported devices
+-----------------
+
+Currently the driver supports the following handhelds:
+
+ - GPD Win Mini (7840U)
+ - GPD Win Mini (8840U)
+ - GPD Win Max 2
+ - GPD Win Max 2 2023 (7840U)
+ - GPD Win Max 2 2024 (8840U)
+ - GPD Win 4 (6800U)
+ - GPD Win 4 (7840U)
+
+Module parameters
+-----------------
+
+gpd_fan_board
+ Force specific which module quirk should be used.
+ Use it like "gpd_fan_board=wm2".
+
+ - wm2
+ - GPD Win 4 (7840U)
+ - GPD Win Max 2 (6800U)
+ - GPD Win Max 2 2023 (7840U)
+ - GPD Win Max 2 2024 (8840U)
+ - win4
+ - GPD Win 4 (6800U)
+ - win_mini
+ - GPD Win Mini (7840U)
+ - GPD Win Mini (8840U)
+
+Sysfs entries
+-------------
+
+The following attributes are supported:
+
+fan1_input
+ Read Only. Reads current fan RPM.
+
+pwm1_enable
+ Read/Write. Enable manual fan control. Write "0" to disable control and run at
+ full speed. Write "1" to set to manual, write "2" to let the EC control decide
+ fan speed. Read this attribute to see current status.
+
+pwm1
+ Read/Write. Read this attribute to see current duty cycle in the range [0-255].
+ When pwm1_enable is set to "1" (manual) write any value in the range [0-255]
+ to set fan speed.
+
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 55f1111594b2e9ada4a881e5d4d8884f33256d1f..d5c7cd0cfdeb7059b6cd83050ae98aa7cb1334e6 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -80,6 +80,7 @@ Hardware Monitoring Kernel Drivers
gigabyte_waterforce
gsc-hwmon
gl518sm
+ gpd-fan
gxp-fan-ctrl
hih6130
hp-wmi-sensors
diff --git a/MAINTAINERS b/MAINTAINERS
index 777ba74ccb07ccc0840c3cd34e7b4d98d726f964..20faebeae981e4b7619fb10331c50525d98db944 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9766,6 +9766,7 @@ GPD FAN DRIVER
M: Cryolitia PukNgae <Cryolitia@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
+F: Documentation/hwmon/gpd-fan.rst
F: drivers/hwmon/gpd-fan.c
GPD POCKET FAN DRIVER
--
2.47.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v5 2/2] hwmon: document: add gpd-fan
@ 2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
0 siblings, 0 replies; 12+ messages in thread
From: Cryolitia PukNgae via B4 Relay @ 2025-02-11 7:01 UTC (permalink / raw)
To: Jean Delvare, Guenter Roeck, Cryolitia PukNgae, Jonathan Corbet
Cc: linux-kernel, linux-hwmon, linux-doc, Celeste Liu, Yao Zi,
Derek John Clark, Marcin Strągowski, someone5678,
Justin Weiss
From: Cryolitia PukNgae <Cryolitia@gmail.com>
Add GPD fan driver document
Signed-off-by: Cryolitia PukNgae <Cryolitia@gmail.com>
---
Documentation/hwmon/gpd-fan.rst | 63 +++++++++++++++++++++++++++++++++++++++++
Documentation/hwmon/index.rst | 1 +
MAINTAINERS | 1 +
3 files changed, 65 insertions(+)
diff --git a/Documentation/hwmon/gpd-fan.rst b/Documentation/hwmon/gpd-fan.rst
new file mode 100644
index 0000000000000000000000000000000000000000..9d478c7350b5fb88e43e54407503dac441328142
--- /dev/null
+++ b/Documentation/hwmon/gpd-fan.rst
@@ -0,0 +1,63 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver gpd-fan
+=========================
+
+Author:
+ - Cryolitia PukNgae <Cryolitia@gmail.com>
+
+Description
+------------
+
+Handheld devices from Shenzhen GPD Technology Co., Ltd. provide fan readings and fan control through
+their embedded controllers.
+
+Supported devices
+-----------------
+
+Currently the driver supports the following handhelds:
+
+ - GPD Win Mini (7840U)
+ - GPD Win Mini (8840U)
+ - GPD Win Max 2
+ - GPD Win Max 2 2023 (7840U)
+ - GPD Win Max 2 2024 (8840U)
+ - GPD Win 4 (6800U)
+ - GPD Win 4 (7840U)
+
+Module parameters
+-----------------
+
+gpd_fan_board
+ Force specific which module quirk should be used.
+ Use it like "gpd_fan_board=wm2".
+
+ - wm2
+ - GPD Win 4 (7840U)
+ - GPD Win Max 2 (6800U)
+ - GPD Win Max 2 2023 (7840U)
+ - GPD Win Max 2 2024 (8840U)
+ - win4
+ - GPD Win 4 (6800U)
+ - win_mini
+ - GPD Win Mini (7840U)
+ - GPD Win Mini (8840U)
+
+Sysfs entries
+-------------
+
+The following attributes are supported:
+
+fan1_input
+ Read Only. Reads current fan RPM.
+
+pwm1_enable
+ Read/Write. Enable manual fan control. Write "0" to disable control and run at
+ full speed. Write "1" to set to manual, write "2" to let the EC control decide
+ fan speed. Read this attribute to see current status.
+
+pwm1
+ Read/Write. Read this attribute to see current duty cycle in the range [0-255].
+ When pwm1_enable is set to "1" (manual) write any value in the range [0-255]
+ to set fan speed.
+
diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
index 55f1111594b2e9ada4a881e5d4d8884f33256d1f..d5c7cd0cfdeb7059b6cd83050ae98aa7cb1334e6 100644
--- a/Documentation/hwmon/index.rst
+++ b/Documentation/hwmon/index.rst
@@ -80,6 +80,7 @@ Hardware Monitoring Kernel Drivers
gigabyte_waterforce
gsc-hwmon
gl518sm
+ gpd-fan
gxp-fan-ctrl
hih6130
hp-wmi-sensors
diff --git a/MAINTAINERS b/MAINTAINERS
index 777ba74ccb07ccc0840c3cd34e7b4d98d726f964..20faebeae981e4b7619fb10331c50525d98db944 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9766,6 +9766,7 @@ GPD FAN DRIVER
M: Cryolitia PukNgae <Cryolitia@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
+F: Documentation/hwmon/gpd-fan.rst
F: drivers/hwmon/gpd-fan.c
GPD POCKET FAN DRIVER
--
2.47.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
(?)
@ 2025-02-13 19:32 ` kernel test robot
-1 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-02-13 19:32 UTC (permalink / raw)
To: Cryolitia PukNgae via B4 Relay, Jean Delvare, Guenter Roeck,
Cryolitia PukNgae, Jonathan Corbet
Cc: llvm, oe-kbuild-all, linux-kernel, linux-hwmon, linux-doc,
Celeste Liu, Yao Zi, Derek John Clark, Marcin Strągowski,
someone5678, Justin Weiss
Hi Cryolitia,
kernel test robot noticed the following build warnings:
[auto build test WARNING on ffd294d346d185b70e28b1a28abe367bbfe53c04]
url: https://github.com/intel-lab-lkp/linux/commits/Cryolitia-PukNgae-via-B4-Relay/hwmon-add-GPD-devices-sensor-driver/20250211-150418
base: ffd294d346d185b70e28b1a28abe367bbfe53c04
patch link: https://lore.kernel.org/r/20250211-gpd_fan-v5-1-608f4255f0e1%40gmail.com
patch subject: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
config: i386-randconfig-002-20250214 (https://download.01.org/0day-ci/archive/20250214/202502140302.IkW9UALU-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250214/202502140302.IkW9UALU-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502140302.IkW9UALU-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/hwmon/gpd-fan.c:361:3: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
361 | const struct gpd_board_drvdata *drvdata = gpd_driver_priv.drvdata;
| ^
drivers/hwmon/gpd-fan.c:452:4: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
452 | int ret = gpd_read_pwm();
| ^
drivers/hwmon/gpd-fan.c:478:4: warning: label followed by a declaration is a C23 extension [-Wc23-extensions]
478 | u8 var = clamp_val(val, 0, 255);
| ^
3 warnings generated.
vim +361 drivers/hwmon/gpd-fan.c
352
353 static int gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
354 {
355 switch (pwm_enable) {
356 case DISABLE:
357 return gpd_generic_write_pwm(255);
358 case MANUAL:
359 return gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
360 case AUTOMATIC:
> 361 const struct gpd_board_drvdata *drvdata = gpd_driver_priv.drvdata;
362
363 return gpd_ecram_write(drvdata, drvdata->pwm_write, 0);
364 }
365 return 0;
366 }
367
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
@ 2025-02-14 6:05 kernel test robot
0 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-02-14 6:05 UTC (permalink / raw)
To: oe-kbuild; +Cc: lkp
::::::
:::::: Manual check reason: "low confidence static check warning: drivers/hwmon/gpd-fan.c:482:17: sparse: sparse: not in switch scope"
::::::
BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <20250211-gpd_fan-v5-1-608f4255f0e1@gmail.com>
References: <20250211-gpd_fan-v5-1-608f4255f0e1@gmail.com>
TO: Cryolitia PukNgae via B4 Relay <devnull+Cryolitia.gmail.com@kernel.org>
TO: Jean Delvare <jdelvare@suse.com>
TO: Guenter Roeck <linux@roeck-us.net>
TO: Cryolitia PukNgae <Cryolitia@gmail.com>
TO: Jonathan Corbet <corbet@lwn.net>
CC: linux-kernel@vger.kernel.org
CC: linux-hwmon@vger.kernel.org
CC: linux-doc@vger.kernel.org
CC: Celeste Liu <CoelacanthusHex@gmail.com>
CC: Yao Zi <ziyao@disroot.org>
CC: Derek John Clark <derekjohn.clark@gmail.com>
CC: "Marcin Strągowski" <marcin@stragowski.com>
CC: someone5678 <someone5678.dev@gmail.com>
CC: Justin Weiss <justin@justinweiss.com>
Hi Cryolitia,
kernel test robot noticed the following build warnings:
[auto build test WARNING on ffd294d346d185b70e28b1a28abe367bbfe53c04]
url: https://github.com/intel-lab-lkp/linux/commits/Cryolitia-PukNgae-via-B4-Relay/hwmon-add-GPD-devices-sensor-driver/20250211-150418
base: ffd294d346d185b70e28b1a28abe367bbfe53c04
patch link: https://lore.kernel.org/r/20250211-gpd_fan-v5-1-608f4255f0e1%40gmail.com
patch subject: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
:::::: branch date: 3 days ago
:::::: commit date: 3 days ago
config: i386-randconfig-r111-20250214 (https://download.01.org/0day-ci/archive/20250214/202502141340.gv6WPQPq-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250214/202502141340.gv6WPQPq-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/r/202502141340.gv6WPQPq-lkp@intel.com/
sparse warnings: (new ones prefixed by >>)
drivers/hwmon/gpd-fan.c:361:17: sparse: sparse: typename in expression
drivers/hwmon/gpd-fan.c:361:23: sparse: sparse: Expected ; at end of statement
drivers/hwmon/gpd-fan.c:361:23: sparse: sparse: got struct
drivers/hwmon/gpd-fan.c:452:25: sparse: sparse: typename in expression
drivers/hwmon/gpd-fan.c:452:29: sparse: sparse: Expected ; at end of statement
drivers/hwmon/gpd-fan.c:452:29: sparse: sparse: got ret
drivers/hwmon/gpd-fan.c:478:25: sparse: sparse: typename in expression
drivers/hwmon/gpd-fan.c:478:28: sparse: sparse: Expected ; at end of statement
drivers/hwmon/gpd-fan.c:478:28: sparse: sparse: got var
drivers/hwmon/gpd-fan.c:478:34: sparse: sparse: Expected ; at end of statement
drivers/hwmon/gpd-fan.c:478:34: sparse: sparse: got )
>> drivers/hwmon/gpd-fan.c:482:17: sparse: sparse: not in switch scope
>> drivers/hwmon/gpd-fan.c:486:9: sparse: sparse: Trying to use reserved word 'return' as identifier
drivers/hwmon/gpd-fan.c:486:16: sparse: sparse: Expected ; at end of declaration
drivers/hwmon/gpd-fan.c:486:16: sparse: sparse: got -
drivers/hwmon/gpd-fan.c:487:1: sparse: sparse: Expected ; at the end of type declaration
drivers/hwmon/gpd-fan.c:487:1: sparse: sparse: got }
drivers/hwmon/gpd-fan.c:361:17: sparse: sparse: undefined identifier 'const'
drivers/hwmon/gpd-fan.c:363:40: sparse: sparse: undefined identifier 'drvdata'
drivers/hwmon/gpd-fan.c:452:25: sparse: sparse: undefined identifier 'int'
drivers/hwmon/gpd-fan.c:454:29: sparse: sparse: undefined identifier 'ret'
drivers/hwmon/gpd-fan.c:455:40: sparse: sparse: undefined identifier 'ret'
drivers/hwmon/gpd-fan.c:457:32: sparse: sparse: undefined identifier 'ret'
drivers/hwmon/gpd-fan.c:478:25: sparse: sparse: undefined identifier 'u8'
drivers/hwmon/gpd-fan.c:478:34: sparse: sparse: undefined identifier '__UNIQUE_ID_v_346'
drivers/hwmon/gpd-fan.c:478:34: sparse: sparse: undefined identifier '__UNIQUE_ID_v_346'
>> drivers/hwmon/gpd-fan.c:478:34: sparse: sparse: incompatible types for operation (<):
drivers/hwmon/gpd-fan.c:478:34: sparse: bad type
drivers/hwmon/gpd-fan.c:478:34: sparse: bad type
drivers/hwmon/gpd-fan.c:478:34: sparse: sparse: undefined identifier '__UNIQUE_ID_v_346'
drivers/hwmon/gpd-fan.c:480:53: sparse: sparse: undefined identifier 'var'
drivers/hwmon/gpd-fan.c:481:46: sparse: sparse: undefined identifier 'var'
drivers/hwmon/gpd-fan.c:501:17: sparse: sparse: undefined identifier 'gpd_fan_ops'
vim +482 drivers/hwmon/gpd-fan.c
143d683070d6ce Cryolitia PukNgae 2025-02-11 352
143d683070d6ce Cryolitia PukNgae 2025-02-11 353 static int gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
143d683070d6ce Cryolitia PukNgae 2025-02-11 354 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 355 switch (pwm_enable) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 356 case DISABLE:
143d683070d6ce Cryolitia PukNgae 2025-02-11 357 return gpd_generic_write_pwm(255);
143d683070d6ce Cryolitia PukNgae 2025-02-11 358 case MANUAL:
143d683070d6ce Cryolitia PukNgae 2025-02-11 359 return gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
143d683070d6ce Cryolitia PukNgae 2025-02-11 360 case AUTOMATIC:
143d683070d6ce Cryolitia PukNgae 2025-02-11 @361 const struct gpd_board_drvdata *drvdata = gpd_driver_priv.drvdata;
143d683070d6ce Cryolitia PukNgae 2025-02-11 362
143d683070d6ce Cryolitia PukNgae 2025-02-11 363 return gpd_ecram_write(drvdata, drvdata->pwm_write, 0);
143d683070d6ce Cryolitia PukNgae 2025-02-11 364 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 365 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 366 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 367
143d683070d6ce Cryolitia PukNgae 2025-02-11 368 static int gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable)
143d683070d6ce Cryolitia PukNgae 2025-02-11 369 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 370 const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
143d683070d6ce Cryolitia PukNgae 2025-02-11 371 int ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 372
143d683070d6ce Cryolitia PukNgae 2025-02-11 373 switch (enable) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 374 case DISABLE: {
143d683070d6ce Cryolitia PukNgae 2025-02-11 375 ret = gpd_generic_write_pwm(255);
143d683070d6ce Cryolitia PukNgae 2025-02-11 376
143d683070d6ce Cryolitia PukNgae 2025-02-11 377 if (ret)
143d683070d6ce Cryolitia PukNgae 2025-02-11 378 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 379
143d683070d6ce Cryolitia PukNgae 2025-02-11 380 return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
143d683070d6ce Cryolitia PukNgae 2025-02-11 381 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 382 case MANUAL: {
143d683070d6ce Cryolitia PukNgae 2025-02-11 383 ret = gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
143d683070d6ce Cryolitia PukNgae 2025-02-11 384
143d683070d6ce Cryolitia PukNgae 2025-02-11 385 if (ret)
143d683070d6ce Cryolitia PukNgae 2025-02-11 386 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 387
143d683070d6ce Cryolitia PukNgae 2025-02-11 388 return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
143d683070d6ce Cryolitia PukNgae 2025-02-11 389 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 390 case AUTOMATIC: {
143d683070d6ce Cryolitia PukNgae 2025-02-11 391 ret = gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0);
143d683070d6ce Cryolitia PukNgae 2025-02-11 392
143d683070d6ce Cryolitia PukNgae 2025-02-11 393 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 394 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 395 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 396 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 397 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 398
143d683070d6ce Cryolitia PukNgae 2025-02-11 399 // Write value for pwm1_enable
143d683070d6ce Cryolitia PukNgae 2025-02-11 400 static int gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable)
143d683070d6ce Cryolitia PukNgae 2025-02-11 401 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 402 switch (gpd_driver_priv.drvdata->board) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 403 case win_mini:
143d683070d6ce Cryolitia PukNgae 2025-02-11 404 case win4_6800u:
143d683070d6ce Cryolitia PukNgae 2025-02-11 405 return gpd_win_mini_set_pwm_enable(enable);
143d683070d6ce Cryolitia PukNgae 2025-02-11 406 case win_max_2:
143d683070d6ce Cryolitia PukNgae 2025-02-11 407 return gpd_wm2_set_pwm_enable(enable);
143d683070d6ce Cryolitia PukNgae 2025-02-11 408 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 409 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 410 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 411
143d683070d6ce Cryolitia PukNgae 2025-02-11 412 static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata,
143d683070d6ce Cryolitia PukNgae 2025-02-11 413 enum hwmon_sensor_types type, u32 attr,
143d683070d6ce Cryolitia PukNgae 2025-02-11 414 __always_unused int channel)
143d683070d6ce Cryolitia PukNgae 2025-02-11 415 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 416 if (type == hwmon_fan && attr == hwmon_fan_input) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 417 return 0444;
143d683070d6ce Cryolitia PukNgae 2025-02-11 418 } else if (type == hwmon_pwm) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 419 switch (attr) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 420 case hwmon_pwm_enable:
143d683070d6ce Cryolitia PukNgae 2025-02-11 421 case hwmon_pwm_input:
143d683070d6ce Cryolitia PukNgae 2025-02-11 422 return 0644;
143d683070d6ce Cryolitia PukNgae 2025-02-11 423 default:
143d683070d6ce Cryolitia PukNgae 2025-02-11 424 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 425 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 426 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 427 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 428 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 429
143d683070d6ce Cryolitia PukNgae 2025-02-11 430 static int gpd_fan_hwmon_read(__always_unused struct device *dev,
143d683070d6ce Cryolitia PukNgae 2025-02-11 431 enum hwmon_sensor_types type, u32 attr,
143d683070d6ce Cryolitia PukNgae 2025-02-11 432 __always_unused int channel, long *val)
143d683070d6ce Cryolitia PukNgae 2025-02-11 433 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 434 if (type == hwmon_fan) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 435 if (attr == hwmon_fan_input) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 436 int ret = gpd_read_rpm();
143d683070d6ce Cryolitia PukNgae 2025-02-11 437
143d683070d6ce Cryolitia PukNgae 2025-02-11 438 if (ret < 0)
143d683070d6ce Cryolitia PukNgae 2025-02-11 439 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 440
143d683070d6ce Cryolitia PukNgae 2025-02-11 441 *val = ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 442 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 443 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 444 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 445 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 446 if (type == hwmon_pwm) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 447 switch (attr) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 448 case hwmon_pwm_enable:
143d683070d6ce Cryolitia PukNgae 2025-02-11 449 *val = gpd_driver_priv.pwm_enable;
143d683070d6ce Cryolitia PukNgae 2025-02-11 450 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 451 case hwmon_pwm_input:
143d683070d6ce Cryolitia PukNgae 2025-02-11 @452 int ret = gpd_read_pwm();
143d683070d6ce Cryolitia PukNgae 2025-02-11 453
143d683070d6ce Cryolitia PukNgae 2025-02-11 454 if (ret < 0)
143d683070d6ce Cryolitia PukNgae 2025-02-11 455 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 456
143d683070d6ce Cryolitia PukNgae 2025-02-11 457 *val = ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 458 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 459 default:
143d683070d6ce Cryolitia PukNgae 2025-02-11 460 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 461 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 462 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 463 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 464 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 465
143d683070d6ce Cryolitia PukNgae 2025-02-11 466 static int gpd_fan_hwmon_write(__always_unused struct device *dev,
143d683070d6ce Cryolitia PukNgae 2025-02-11 467 enum hwmon_sensor_types type, u32 attr,
143d683070d6ce Cryolitia PukNgae 2025-02-11 468 __always_unused int channel, long val)
143d683070d6ce Cryolitia PukNgae 2025-02-11 469 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 470 if (type == hwmon_pwm) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 471 switch (attr) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 472 case hwmon_pwm_enable:
143d683070d6ce Cryolitia PukNgae 2025-02-11 473 if (!in_range(val, 0, 3))
143d683070d6ce Cryolitia PukNgae 2025-02-11 474 return -EINVAL;
143d683070d6ce Cryolitia PukNgae 2025-02-11 475 gpd_driver_priv.pwm_enable = val;
143d683070d6ce Cryolitia PukNgae 2025-02-11 476 return gpd_set_pwm_enable(gpd_driver_priv.pwm_enable);
143d683070d6ce Cryolitia PukNgae 2025-02-11 477 case hwmon_pwm_input:
143d683070d6ce Cryolitia PukNgae 2025-02-11 @478 u8 var = clamp_val(val, 0, 255);
143d683070d6ce Cryolitia PukNgae 2025-02-11 479
143d683070d6ce Cryolitia PukNgae 2025-02-11 480 gpd_driver_priv.pwm_value = var;
143d683070d6ce Cryolitia PukNgae 2025-02-11 481 return gpd_write_pwm(var);
143d683070d6ce Cryolitia PukNgae 2025-02-11 @482 default:
143d683070d6ce Cryolitia PukNgae 2025-02-11 483 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 484 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 485 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 @486 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 487 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 488
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
@ 2025-02-15 2:35 kernel test robot
0 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-02-15 2:35 UTC (permalink / raw)
To: oe-kbuild; +Cc: lkp, Dan Carpenter
BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <20250211-gpd_fan-v5-1-608f4255f0e1@gmail.com>
References: <20250211-gpd_fan-v5-1-608f4255f0e1@gmail.com>
TO: Cryolitia PukNgae via B4 Relay <devnull+Cryolitia.gmail.com@kernel.org>
TO: Jean Delvare <jdelvare@suse.com>
TO: Guenter Roeck <linux@roeck-us.net>
TO: Cryolitia PukNgae <Cryolitia@gmail.com>
TO: Jonathan Corbet <corbet@lwn.net>
CC: linux-kernel@vger.kernel.org
CC: linux-hwmon@vger.kernel.org
CC: linux-doc@vger.kernel.org
CC: Celeste Liu <CoelacanthusHex@gmail.com>
CC: Yao Zi <ziyao@disroot.org>
CC: Derek John Clark <derekjohn.clark@gmail.com>
CC: "Marcin Strągowski" <marcin@stragowski.com>
CC: someone5678 <someone5678.dev@gmail.com>
CC: Justin Weiss <justin@justinweiss.com>
Hi Cryolitia,
kernel test robot noticed the following build warnings:
[auto build test WARNING on ffd294d346d185b70e28b1a28abe367bbfe53c04]
url: https://github.com/intel-lab-lkp/linux/commits/Cryolitia-PukNgae-via-B4-Relay/hwmon-add-GPD-devices-sensor-driver/20250211-150418
base: ffd294d346d185b70e28b1a28abe367bbfe53c04
patch link: https://lore.kernel.org/r/20250211-gpd_fan-v5-1-608f4255f0e1%40gmail.com
patch subject: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
:::::: branch date: 4 days ago
:::::: commit date: 4 days ago
config: i386-randconfig-r073-20250215 (https://download.01.org/0day-ci/archive/20250215/202502151049.7BS9yEVk-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>
| Closes: https://lore.kernel.org/r/202502151049.7BS9yEVk-lkp@intel.com/
smatch warnings:
drivers/hwmon/gpd-fan.c:361 gpd_win_mini_set_pwm_enable() warn: statement has no effect 3
drivers/hwmon/gpd-fan.c:452 gpd_fan_hwmon_read() warn: statement has no effect 3
drivers/hwmon/gpd-fan.c:478 gpd_fan_hwmon_write() warn: statement has no effect 3
drivers/hwmon/gpd-fan.c:482 gpd_fan_hwmon_write() warn: inconsistent indenting
drivers/hwmon/gpd-fan.c:534 gpd_fan_probe() warn: passing zero to 'PTR_ERR'
vim +361 drivers/hwmon/gpd-fan.c
143d683070d6ce Cryolitia PukNgae 2025-02-11 352
143d683070d6ce Cryolitia PukNgae 2025-02-11 353 static int gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
143d683070d6ce Cryolitia PukNgae 2025-02-11 354 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 355 switch (pwm_enable) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 356 case DISABLE:
143d683070d6ce Cryolitia PukNgae 2025-02-11 357 return gpd_generic_write_pwm(255);
143d683070d6ce Cryolitia PukNgae 2025-02-11 358 case MANUAL:
143d683070d6ce Cryolitia PukNgae 2025-02-11 359 return gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
143d683070d6ce Cryolitia PukNgae 2025-02-11 360 case AUTOMATIC:
143d683070d6ce Cryolitia PukNgae 2025-02-11 @361 const struct gpd_board_drvdata *drvdata = gpd_driver_priv.drvdata;
143d683070d6ce Cryolitia PukNgae 2025-02-11 362
143d683070d6ce Cryolitia PukNgae 2025-02-11 363 return gpd_ecram_write(drvdata, drvdata->pwm_write, 0);
143d683070d6ce Cryolitia PukNgae 2025-02-11 364 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 365 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 366 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 367
143d683070d6ce Cryolitia PukNgae 2025-02-11 368 static int gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable)
143d683070d6ce Cryolitia PukNgae 2025-02-11 369 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 370 const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
143d683070d6ce Cryolitia PukNgae 2025-02-11 371 int ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 372
143d683070d6ce Cryolitia PukNgae 2025-02-11 373 switch (enable) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 374 case DISABLE: {
143d683070d6ce Cryolitia PukNgae 2025-02-11 375 ret = gpd_generic_write_pwm(255);
143d683070d6ce Cryolitia PukNgae 2025-02-11 376
143d683070d6ce Cryolitia PukNgae 2025-02-11 377 if (ret)
143d683070d6ce Cryolitia PukNgae 2025-02-11 378 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 379
143d683070d6ce Cryolitia PukNgae 2025-02-11 380 return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
143d683070d6ce Cryolitia PukNgae 2025-02-11 381 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 382 case MANUAL: {
143d683070d6ce Cryolitia PukNgae 2025-02-11 383 ret = gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
143d683070d6ce Cryolitia PukNgae 2025-02-11 384
143d683070d6ce Cryolitia PukNgae 2025-02-11 385 if (ret)
143d683070d6ce Cryolitia PukNgae 2025-02-11 386 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 387
143d683070d6ce Cryolitia PukNgae 2025-02-11 388 return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
143d683070d6ce Cryolitia PukNgae 2025-02-11 389 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 390 case AUTOMATIC: {
143d683070d6ce Cryolitia PukNgae 2025-02-11 391 ret = gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0);
143d683070d6ce Cryolitia PukNgae 2025-02-11 392
143d683070d6ce Cryolitia PukNgae 2025-02-11 393 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 394 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 395 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 396 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 397 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 398
143d683070d6ce Cryolitia PukNgae 2025-02-11 399 // Write value for pwm1_enable
143d683070d6ce Cryolitia PukNgae 2025-02-11 400 static int gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable)
143d683070d6ce Cryolitia PukNgae 2025-02-11 401 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 402 switch (gpd_driver_priv.drvdata->board) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 403 case win_mini:
143d683070d6ce Cryolitia PukNgae 2025-02-11 404 case win4_6800u:
143d683070d6ce Cryolitia PukNgae 2025-02-11 405 return gpd_win_mini_set_pwm_enable(enable);
143d683070d6ce Cryolitia PukNgae 2025-02-11 406 case win_max_2:
143d683070d6ce Cryolitia PukNgae 2025-02-11 407 return gpd_wm2_set_pwm_enable(enable);
143d683070d6ce Cryolitia PukNgae 2025-02-11 408 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 409 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 410 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 411
143d683070d6ce Cryolitia PukNgae 2025-02-11 412 static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata,
143d683070d6ce Cryolitia PukNgae 2025-02-11 413 enum hwmon_sensor_types type, u32 attr,
143d683070d6ce Cryolitia PukNgae 2025-02-11 414 __always_unused int channel)
143d683070d6ce Cryolitia PukNgae 2025-02-11 415 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 416 if (type == hwmon_fan && attr == hwmon_fan_input) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 417 return 0444;
143d683070d6ce Cryolitia PukNgae 2025-02-11 418 } else if (type == hwmon_pwm) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 419 switch (attr) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 420 case hwmon_pwm_enable:
143d683070d6ce Cryolitia PukNgae 2025-02-11 421 case hwmon_pwm_input:
143d683070d6ce Cryolitia PukNgae 2025-02-11 422 return 0644;
143d683070d6ce Cryolitia PukNgae 2025-02-11 423 default:
143d683070d6ce Cryolitia PukNgae 2025-02-11 424 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 425 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 426 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 427 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 428 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 429
143d683070d6ce Cryolitia PukNgae 2025-02-11 430 static int gpd_fan_hwmon_read(__always_unused struct device *dev,
143d683070d6ce Cryolitia PukNgae 2025-02-11 431 enum hwmon_sensor_types type, u32 attr,
143d683070d6ce Cryolitia PukNgae 2025-02-11 432 __always_unused int channel, long *val)
143d683070d6ce Cryolitia PukNgae 2025-02-11 433 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 434 if (type == hwmon_fan) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 435 if (attr == hwmon_fan_input) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 436 int ret = gpd_read_rpm();
143d683070d6ce Cryolitia PukNgae 2025-02-11 437
143d683070d6ce Cryolitia PukNgae 2025-02-11 438 if (ret < 0)
143d683070d6ce Cryolitia PukNgae 2025-02-11 439 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 440
143d683070d6ce Cryolitia PukNgae 2025-02-11 441 *val = ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 442 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 443 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 444 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 445 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 446 if (type == hwmon_pwm) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 447 switch (attr) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 448 case hwmon_pwm_enable:
143d683070d6ce Cryolitia PukNgae 2025-02-11 449 *val = gpd_driver_priv.pwm_enable;
143d683070d6ce Cryolitia PukNgae 2025-02-11 450 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 451 case hwmon_pwm_input:
143d683070d6ce Cryolitia PukNgae 2025-02-11 @452 int ret = gpd_read_pwm();
143d683070d6ce Cryolitia PukNgae 2025-02-11 453
143d683070d6ce Cryolitia PukNgae 2025-02-11 454 if (ret < 0)
143d683070d6ce Cryolitia PukNgae 2025-02-11 455 return ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 456
143d683070d6ce Cryolitia PukNgae 2025-02-11 457 *val = ret;
143d683070d6ce Cryolitia PukNgae 2025-02-11 458 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 459 default:
143d683070d6ce Cryolitia PukNgae 2025-02-11 460 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 461 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 462 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 463 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 464 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 465
143d683070d6ce Cryolitia PukNgae 2025-02-11 466 static int gpd_fan_hwmon_write(__always_unused struct device *dev,
143d683070d6ce Cryolitia PukNgae 2025-02-11 467 enum hwmon_sensor_types type, u32 attr,
143d683070d6ce Cryolitia PukNgae 2025-02-11 468 __always_unused int channel, long val)
143d683070d6ce Cryolitia PukNgae 2025-02-11 469 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 470 if (type == hwmon_pwm) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 471 switch (attr) {
143d683070d6ce Cryolitia PukNgae 2025-02-11 472 case hwmon_pwm_enable:
143d683070d6ce Cryolitia PukNgae 2025-02-11 473 if (!in_range(val, 0, 3))
143d683070d6ce Cryolitia PukNgae 2025-02-11 474 return -EINVAL;
143d683070d6ce Cryolitia PukNgae 2025-02-11 475 gpd_driver_priv.pwm_enable = val;
143d683070d6ce Cryolitia PukNgae 2025-02-11 476 return gpd_set_pwm_enable(gpd_driver_priv.pwm_enable);
143d683070d6ce Cryolitia PukNgae 2025-02-11 477 case hwmon_pwm_input:
143d683070d6ce Cryolitia PukNgae 2025-02-11 @478 u8 var = clamp_val(val, 0, 255);
143d683070d6ce Cryolitia PukNgae 2025-02-11 479
143d683070d6ce Cryolitia PukNgae 2025-02-11 480 gpd_driver_priv.pwm_value = var;
143d683070d6ce Cryolitia PukNgae 2025-02-11 481 return gpd_write_pwm(var);
143d683070d6ce Cryolitia PukNgae 2025-02-11 @482 default:
143d683070d6ce Cryolitia PukNgae 2025-02-11 483 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 484 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 485 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 486 return -EOPNOTSUPP;
143d683070d6ce Cryolitia PukNgae 2025-02-11 487 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 488
143d683070d6ce Cryolitia PukNgae 2025-02-11 489 static const struct hwmon_ops gpd_fan_ops = {
143d683070d6ce Cryolitia PukNgae 2025-02-11 490 .is_visible = gpd_fan_hwmon_is_visible,
143d683070d6ce Cryolitia PukNgae 2025-02-11 491 .read = gpd_fan_hwmon_read,
143d683070d6ce Cryolitia PukNgae 2025-02-11 492 .write = gpd_fan_hwmon_write,
143d683070d6ce Cryolitia PukNgae 2025-02-11 493 };
143d683070d6ce Cryolitia PukNgae 2025-02-11 494
143d683070d6ce Cryolitia PukNgae 2025-02-11 495 static const struct hwmon_channel_info *gpd_fan_hwmon_channel_info[] = {
143d683070d6ce Cryolitia PukNgae 2025-02-11 496 HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
143d683070d6ce Cryolitia PukNgae 2025-02-11 497 HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), NULL
143d683070d6ce Cryolitia PukNgae 2025-02-11 498 };
143d683070d6ce Cryolitia PukNgae 2025-02-11 499
143d683070d6ce Cryolitia PukNgae 2025-02-11 500 static struct hwmon_chip_info gpd_fan_chip_info = {
143d683070d6ce Cryolitia PukNgae 2025-02-11 501 .ops = &gpd_fan_ops,
143d683070d6ce Cryolitia PukNgae 2025-02-11 502 .info = gpd_fan_hwmon_channel_info
143d683070d6ce Cryolitia PukNgae 2025-02-11 503 };
143d683070d6ce Cryolitia PukNgae 2025-02-11 504
143d683070d6ce Cryolitia PukNgae 2025-02-11 505 static int gpd_fan_probe(struct platform_device *pdev)
143d683070d6ce Cryolitia PukNgae 2025-02-11 506 {
143d683070d6ce Cryolitia PukNgae 2025-02-11 507 struct device *dev = &pdev->dev;
143d683070d6ce Cryolitia PukNgae 2025-02-11 508 struct gpd_driver_priv *data;
143d683070d6ce Cryolitia PukNgae 2025-02-11 509 const struct resource *plat_res;
143d683070d6ce Cryolitia PukNgae 2025-02-11 510 const struct device *dev_reg;
143d683070d6ce Cryolitia PukNgae 2025-02-11 511 const struct resource *region_res;
143d683070d6ce Cryolitia PukNgae 2025-02-11 512
143d683070d6ce Cryolitia PukNgae 2025-02-11 513 data = dev_get_platdata(&pdev->dev);
143d683070d6ce Cryolitia PukNgae 2025-02-11 514 if (IS_ERR(data))
143d683070d6ce Cryolitia PukNgae 2025-02-11 515 return -ENODEV;
143d683070d6ce Cryolitia PukNgae 2025-02-11 516
143d683070d6ce Cryolitia PukNgae 2025-02-11 517 plat_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
143d683070d6ce Cryolitia PukNgae 2025-02-11 518 if (IS_ERR(plat_res))
143d683070d6ce Cryolitia PukNgae 2025-02-11 519 return dev_err_probe(dev, PTR_ERR(plat_res),
143d683070d6ce Cryolitia PukNgae 2025-02-11 520 "Failed to get platform resource\n");
143d683070d6ce Cryolitia PukNgae 2025-02-11 521
143d683070d6ce Cryolitia PukNgae 2025-02-11 522 region_res = devm_request_region(dev, plat_res->start,
143d683070d6ce Cryolitia PukNgae 2025-02-11 523 resource_size(plat_res), DRIVER_NAME);
143d683070d6ce Cryolitia PukNgae 2025-02-11 524 if (IS_ERR(region_res))
143d683070d6ce Cryolitia PukNgae 2025-02-11 525 return dev_err_probe(dev, PTR_ERR(region_res),
143d683070d6ce Cryolitia PukNgae 2025-02-11 526 "Failed to request region\n");
143d683070d6ce Cryolitia PukNgae 2025-02-11 527
143d683070d6ce Cryolitia PukNgae 2025-02-11 528 dev_reg = devm_hwmon_device_register_with_info(dev,
143d683070d6ce Cryolitia PukNgae 2025-02-11 529 DRIVER_NAME,
143d683070d6ce Cryolitia PukNgae 2025-02-11 530 data,
143d683070d6ce Cryolitia PukNgae 2025-02-11 531 &gpd_fan_chip_info,
143d683070d6ce Cryolitia PukNgae 2025-02-11 532 NULL);
143d683070d6ce Cryolitia PukNgae 2025-02-11 533 if (IS_ERR(dev_reg))
143d683070d6ce Cryolitia PukNgae 2025-02-11 @534 return dev_err_probe(dev, PTR_ERR(region_res),
143d683070d6ce Cryolitia PukNgae 2025-02-11 535 "Failed to register hwmon device\n");
143d683070d6ce Cryolitia PukNgae 2025-02-11 536
143d683070d6ce Cryolitia PukNgae 2025-02-11 537 return 0;
143d683070d6ce Cryolitia PukNgae 2025-02-11 538 }
143d683070d6ce Cryolitia PukNgae 2025-02-11 539
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
(?)
(?)
@ 2025-02-17 5:03 ` kernel test robot
-1 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2025-02-17 5:03 UTC (permalink / raw)
To: Cryolitia PukNgae via B4 Relay
Cc: oe-lkp, lkp, linux-kernel, linux-hwmon, Jean Delvare,
Guenter Roeck, Cryolitia PukNgae, Jonathan Corbet, linux-doc,
Celeste Liu, Yao Zi, Derek John Clark, Marcin Strągowski,
someone5678, Justin Weiss, oliver.sang
Hello,
kernel test robot noticed "Oops:general_protection_fault,probably_for_non-canonical_address#:#[##]PREEMPT_KASAN" on:
commit: 143d683070d6cedc02b6406f98f21bd5ffed8a70 ("[PATCH v5 1/2] hwmon: add GPD devices sensor driver")
url: https://github.com/intel-lab-lkp/linux/commits/Cryolitia-PukNgae-via-B4-Relay/hwmon-add-GPD-devices-sensor-driver/20250211-150418
patch link: https://lore.kernel.org/all/20250211-gpd_fan-v5-1-608f4255f0e1@gmail.com/
patch subject: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
in testcase: boot
config: x86_64-randconfig-004-20250215
compiler: gcc-12
test machine: qemu-system-x86_64 -enable-kvm -cpu SandyBridge -smp 2 -m 16G
(please refer to attached dmesg/kmsg for entire log/backtrace)
+--------------------------------------------------------------------------------------+-------+------------+
| | v6.13 | 143d683070 |
+--------------------------------------------------------------------------------------+-------+------------+
| boot_successes | 12 | 0 |
| boot_failures | 0 | 12 |
| Oops:general_protection_fault,probably_for_non-canonical_address#:#[##]PREEMPT_KASAN | 0 | 12 |
| KASAN:null-ptr-deref_in_range[#-#] | 0 | 12 |
| RIP:gpd_fan_init | 0 | 12 |
| Kernel_panic-not_syncing:Fatal_exception | 0 | 12 |
+--------------------------------------------------------------------------------------+-------+------------+
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <oliver.sang@intel.com>
| Closes: https://lore.kernel.org/oe-lkp/202502171113.9faaae-lkp@intel.com
[ 24.206559][ T1] pps_ldisc: PPS line discipline registered
[ 24.221748][ T1] Driver for 1-wire Dallas network protocol.
[ 24.228882][ T1] usbcore: registered new interface driver DS9490R
[ 24.241341][ T1] applesmc: supported laptop not found!
[ 24.245457][ T1] applesmc: driver init failed (ret=-19)!
[ 24.253994][ T1] Oops: general protection fault, probably for non-canonical address 0xdffffc000000002a: 0000 [#1] PREEMPT KASAN
[ 24.255395][ T1] KASAN: null-ptr-deref in range [0x0000000000000150-0x0000000000000157]
[ 24.255395][ T1] CPU: 0 UID: 0 PID: 1 Comm: swapper Tainted: G T 6.13.0-00001-g143d683070d6 #1
[ 24.255395][ T1] Tainted: [T]=RANDSTRUCT
[ 24.255395][ T1] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
[ 24.255395][ T1] RIP: 0010:gpd_fan_init (kbuild/src/consumer/drivers/hwmon/gpd-fan.c:568)
[ 24.255395][ T1] Code: c5 08 eb b2 48 c7 c7 40 16 90 87 e8 49 ea b6 f9 48 8d b8 50 01 00 00 49 89 c5 b8 ff ff 37 00 48 c1 e0 2a 48 89 fa 48 c1 ea 03 <80> 3c 02 00 74 05 e8 a7 5d e9 f5 4d 8b bd 50 01 00 00 b8 ed ff ff
All code
========
0: c5 08 eb (bad)
3: b2 48 mov $0x48,%dl
5: c7 c7 40 16 90 87 mov $0x87901640,%edi
b: e8 49 ea b6 f9 call 0xfffffffff9b6ea59
10: 48 8d b8 50 01 00 00 lea 0x150(%rax),%rdi
17: 49 89 c5 mov %rax,%r13
1a: b8 ff ff 37 00 mov $0x37ffff,%eax
1f: 48 c1 e0 2a shl $0x2a,%rax
23: 48 89 fa mov %rdi,%rdx
26: 48 c1 ea 03 shr $0x3,%rdx
2a:* 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1) <-- trapping instruction
2e: 74 05 je 0x35
30: e8 a7 5d e9 f5 call 0xfffffffff5e95ddc
35: 4d 8b bd 50 01 00 00 mov 0x150(%r13),%r15
3c: b8 .byte 0xb8
3d: ed in (%dx),%eax
3e: ff (bad)
3f: ff .byte 0xff
Code starting with the faulting instruction
===========================================
0: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1)
4: 74 05 je 0xb
6: e8 a7 5d e9 f5 call 0xfffffffff5e95db2
b: 4d 8b bd 50 01 00 00 mov 0x150(%r13),%r15
12: b8 .byte 0xb8
13: ed in (%dx),%eax
14: ff (bad)
15: ff .byte 0xff
[ 24.255395][ T1] RSP: 0018:ffffc9000001fcd8 EFLAGS: 00010202
[ 24.255395][ T1] RAX: dffffc0000000000 RBX: 1ffff92000003f9c RCX: ffffffff8c800034
[ 24.255395][ T1] RDX: 000000000000002a RSI: 0000000000000001 RDI: 0000000000000150
[ 24.255395][ T1] RBP: ffffc9000001fd88 R08: 0000000e56910ec6 R09: 0000000e56910ec6
[ 24.255395][ T1] R10: fffffbfff130e5ad R11: ffffffff8261d8e0 R12: ffffc9000001fd60
[ 24.255395][ T1] R13: 0000000000000000 R14: dffffc0000000000 R15: 0000000000000000
[ 24.255395][ T1] FS: 0000000000000000(0000) GS:ffffffff8890a000(0000) knlGS:0000000000000000
[ 24.255395][ T1] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 24.255395][ T1] CR2: 00007fd5127dc480 CR3: 00000000088d4000 CR4: 00000000000406b0
[ 24.255395][ T1] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 24.255395][ T1] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 24.255395][ T1] Call Trace:
[ 24.255395][ T1] <TASK>
[ 24.255395][ T1] ? show_regs (kbuild/src/consumer/arch/x86/kernel/dumpstack.c:479)
[ 24.255395][ T1] ? __die_body (kbuild/src/consumer/arch/x86/kernel/dumpstack.c:421)
[ 24.255395][ T1] ? die_addr (kbuild/src/consumer/arch/x86/kernel/dumpstack.c:455)
[ 24.255395][ T1] ? exc_general_protection (kbuild/src/consumer/arch/x86/kernel/traps.c:751 kbuild/src/consumer/arch/x86/kernel/traps.c:693)
The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20250217/202502171113.9faaae-lkp@intel.com
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
` (2 preceding siblings ...)
(?)
@ 2025-03-02 17:31 ` Guenter Roeck
2025-03-13 20:12 ` Cryolitia PukNgae
-1 siblings, 1 reply; 12+ messages in thread
From: Guenter Roeck @ 2025-03-02 17:31 UTC (permalink / raw)
To: Cryolitia
Cc: Jean Delvare, Jonathan Corbet, linux-kernel, linux-hwmon,
linux-doc, Celeste Liu, Yao Zi, Derek John Clark,
Marcin Strągowski, someone5678, Justin Weiss
On Tue, Feb 11, 2025 at 03:01:17PM +0800, Cryolitia PukNgae via B4 Relay wrote:
> From: Cryolitia PukNgae <Cryolitia@gmail.com>
>
> Sensors driver for GPD Handhelds that expose fan reading and control via
> hwmon sysfs.
>
> Shenzhen GPD Technology Co., Ltd. manufactures a series of handheld
> devices. This driver implements these functions through x86 port-mapped IO.
>
> Signed-off-by: Cryolitia PukNgae <Cryolitia@gmail.com>
0-day reported crashes with this driver applied. Please fix.
Guenter
> ---
> MAINTAINERS | 6 +
> drivers/hwmon/Kconfig | 10 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/gpd-fan.c | 611 ++++++++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 628 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0fa7c5728f1e64d031f4a47b6fce1db484ce0fc2..777ba74ccb07ccc0840c3cd34e7b4d98d726f964 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -9762,6 +9762,12 @@ F: drivers/phy/samsung/phy-gs101-ufs.c
> F: include/dt-bindings/clock/google,gs101.h
> K: [gG]oogle.?[tT]ensor
>
> +GPD FAN DRIVER
> +M: Cryolitia PukNgae <Cryolitia@gmail.com>
> +L: linux-hwmon@vger.kernel.org
> +S: Maintained
> +F: drivers/hwmon/gpd-fan.c
> +
> GPD POCKET FAN DRIVER
> M: Hans de Goede <hdegoede@redhat.com>
> L: platform-driver-x86@vger.kernel.org
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index dd376602f3f19c6f258651afeffbe1bb5d9b6b72..974b341c0bdaba147370de59f510140c0c937913 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -729,6 +729,16 @@ config SENSORS_GL520SM
> This driver can also be built as a module. If so, the module
> will be called gl520sm.
>
> +config SENSORS_GPD
> + tristate "GPD handhelds"
> + depends on X86
> + help
> + If you say yes here you get support for fan readings and
> + control over GPD handheld devices.
> +
> + Can also be built as a module. In that case it will be
> + called gpd-fan.
> +
> config SENSORS_G760A
> tristate "GMT G760A"
> depends on I2C
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index b827b92f2a7844418f3f3b6434a63b744b52c33d..cd512c19caa9737a2926a3d4860f65b65cd013c3 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -87,6 +87,7 @@ obj-$(CONFIG_SENSORS_GIGABYTE_WATERFORCE) += gigabyte_waterforce.o
> obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
> obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
> obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o
> +obj-$(CONFIG_SENSORS_GPD) += gpd-fan.o
> obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
> obj-$(CONFIG_SENSORS_GXP_FAN_CTRL) += gxp-fan-ctrl.o
> obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
> diff --git a/drivers/hwmon/gpd-fan.c b/drivers/hwmon/gpd-fan.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..8d54ebf5defa45f4d01c0dd7786b1908bca55ec0
> --- /dev/null
> +++ b/drivers/hwmon/gpd-fan.c
> @@ -0,0 +1,611 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +/* Platform driver for GPD devices that expose fan control via hwmon sysfs.
> + *
> + * Fan control is provided via pwm interface in the range [0-255].
> + * Each model has a different range in the EC, the written value is scaled to accommodate for that.
> + *
> + * Based on this repo:
> + * https://github.com/Cryolitia/gpd-fan-driver
> + *
> + * Copyright (c) 2024 Cryolitia PukNgae
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/dmi.h>
> +#include <linux/hwmon.h>
> +#include <linux/ioport.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +#define DRIVER_NAME "gpdfan"
> +
> +#define GPD_PWM_CTR_OFFSET 0x1841
> +
> +// model param, see document
> +static char *gpd_fan_board = "";
> +module_param(gpd_fan_board, charp, 0444);
> +
> +// EC read/write locker
> +// Should never access EC at the same time, otherwise system down.
> +static DEFINE_MUTEX(gpd_fan_lock);
> +
> +enum gpd_board {
> + win_mini,
> + win4_6800u,
> + win_max_2,
> +};
> +
> +enum FAN_PWM_ENABLE {
> + DISABLE = 0,
> + MANUAL = 1,
> + AUTOMATIC = 2,
> +};
> +
> +static struct {
> + enum FAN_PWM_ENABLE pwm_enable;
> + u8 pwm_value;
> +
> + const struct gpd_board_drvdata *drvdata;
> +} gpd_driver_priv;
> +
> +struct gpd_board_drvdata {
> + const char *board_name; /* Board name for module param comparison */
> + const enum gpd_board board;
> +
> + const u8 addr_port;
> + const u8 data_port;
> + const u16 manual_control_enable;
> + const u16 rpm_read;
> + const u16 pwm_write;
> + const u16 pwm_max;
> +};
> +
> +static struct gpd_board_drvdata gpd_win_mini_drvdata = {
> + .board_name = "win_mini",
> + .board = win_mini,
> +
> + .addr_port = 0x4E,
> + .data_port = 0x4F,
> + .manual_control_enable = 0x047A,
> + .rpm_read = 0x0478,
> + .pwm_write = 0x047A,
> + .pwm_max = 244,
> +};
> +
> +static struct gpd_board_drvdata gpd_win4_drvdata = {
> + .board_name = "win4",
> + .board = win4_6800u,
> +
> + .addr_port = 0x2E,
> + .data_port = 0x2F,
> + .manual_control_enable = 0xC311,
> + .rpm_read = 0xC880,
> + .pwm_write = 0xC311,
> + .pwm_max = 127,
> +};
> +
> +static struct gpd_board_drvdata gpd_wm2_drvdata = {
> + .board_name = "wm2",
> + .board = win_max_2,
> +
> + .addr_port = 0x4E,
> + .data_port = 0x4F,
> + .manual_control_enable = 0x0275,
> + .rpm_read = 0x0218,
> + .pwm_write = 0x1809,
> + .pwm_max = 184,
> +};
> +
> +static const struct dmi_system_id dmi_table[] = {
> + {
> + // GPD Win Mini
> + // GPD Win Mini with AMD Ryzen 8840U
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "G1617-01")
> + },
> + .driver_data = &gpd_win_mini_drvdata,
> + },
> + {
> + // GPD Win 4 with AMD Ryzen 6800U
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
> + DMI_MATCH(DMI_BOARD_VERSION, "Default string"),
> + },
> + .driver_data = &gpd_win4_drvdata,
> + },
> + {
> + // GPD Win 4 with Ryzen 7840U
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "G1618-04"),
> + DMI_MATCH(DMI_BOARD_VERSION, "Ver. 1.0"),
> + },
> + // Since 7840U, win4 uses the same drvdata as wm2
> + .driver_data = &gpd_wm2_drvdata,
> + },
> + {
> + // GPD Win Max 2 with Ryzen 6800U
> + // GPD Win Max 2 2023 with Ryzen 7840U
> + // GPD Win Max 2 2024 with Ryzen 8840U
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "GPD"),
> + DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"),
> + },
> + .driver_data = &gpd_wm2_drvdata,
> + },
> + {}
> +};
> +
> +static const struct gpd_board_drvdata *gpd_module_drvdata[] = {
> + &gpd_win_mini_drvdata, &gpd_win4_drvdata, &gpd_wm2_drvdata, NULL
> +};
> +
> +/* Helper functions to handle EC read/write */
> +static int gpd_ecram_read(const struct gpd_board_drvdata *drvdata, u16 offset,
> + u8 *val)
> +{
> + int ret;
> + u16 addr_port = drvdata->addr_port;
> + u16 data_port = drvdata->data_port;
> +
> + ret = mutex_lock_interruptible(&gpd_fan_lock);
> +
> + if (ret)
> + return ret;
> +
> + outb(0x2E, addr_port);
> + outb(0x11, data_port);
> + outb(0x2F, addr_port);
> + outb((u8)((offset >> 8) & 0xFF), data_port);
> +
> + outb(0x2E, addr_port);
> + outb(0x10, data_port);
> + outb(0x2F, addr_port);
> + outb((u8)(offset & 0xFF), data_port);
> +
> + outb(0x2E, addr_port);
> + outb(0x12, data_port);
> + outb(0x2F, addr_port);
> + *val = inb(data_port);
> +
> + mutex_unlock(&gpd_fan_lock);
> + return 0;
> +}
> +
> +static int gpd_ecram_write(const struct gpd_board_drvdata *drvdata, u16 offset,
> + u8 value)
> +{
> + int ret;
> + u16 addr_port = drvdata->addr_port;
> + u16 data_port = drvdata->data_port;
> +
> + ret = mutex_lock_interruptible(&gpd_fan_lock);
> +
> + if (ret)
> + return ret;
> +
> + outb(0x2E, addr_port);
> + outb(0x11, data_port);
> + outb(0x2F, addr_port);
> + outb((u8)((offset >> 8) & 0xFF), data_port);
> +
> + outb(0x2E, addr_port);
> + outb(0x10, data_port);
> + outb(0x2F, addr_port);
> + outb((u8)(offset & 0xFF), data_port);
> +
> + outb(0x2E, addr_port);
> + outb(0x12, data_port);
> + outb(0x2F, addr_port);
> + outb(value, data_port);
> +
> + mutex_unlock(&gpd_fan_lock);
> + return 0;
> +}
> +
> +static int gpd_generic_read_rpm_uncached(void)
> +{
> + u8 high, low;
> + int ret;
> + const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
> +
> + ret = gpd_ecram_read(drvdata, drvdata->rpm_read, &high);
> + if (ret)
> + return ret;
> + ret = gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low);
> + if (ret)
> + return ret;
> +
> + return (u16)high << 8 | low;
> +}
> +
> +static int gpd_win4_read_rpm_uncached(void)
> +{
> + const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
> + u8 PWMCTR;
> + int ret;
> +
> + gpd_ecram_read(drvdata, GPD_PWM_CTR_OFFSET, &PWMCTR);
> + if (PWMCTR != 0x7F)
> + gpd_ecram_write(drvdata, GPD_PWM_CTR_OFFSET, 0x7F);
> +
> + ret = gpd_generic_read_rpm_uncached();
> +
> + if (ret < 0)
> + return ret;
> +
> + if (ret == 0) {
> + //re-init EC
> + u8 chip_id;
> +
> + gpd_ecram_read(drvdata, 0x2000, &chip_id);
> + if (chip_id == 0x55) {
> + u8 chip_ver;
> +
> + if (gpd_ecram_read(drvdata, 0x1060, &chip_ver))
> + gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80);
> + }
> + }
> + return ret;
> +}
> +
> +static int gpd_wm2_read_rpm_uncached(void)
> +{
> + const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
> +
> + for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET; pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2;
> + pwm_ctr_offset++) {
> + u8 PWMCTR;
> +
> + gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR);
> + if (PWMCTR != 0xB8)
> + gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8);
> + }
> + return gpd_generic_read_rpm_uncached();
> +}
> +
> +// Read value for fan1_input
> +static int gpd_read_rpm(void)
> +{
> + switch (gpd_driver_priv.drvdata->board) {
> + case win_mini: {
> + return gpd_generic_read_rpm_uncached();
> + }
> + case win4_6800u: {
> + return gpd_win4_read_rpm_uncached();
> + }
> + case win_max_2: {
> + return gpd_wm2_read_rpm_uncached();
> + }
> + }
> + return 0;
> +}
> +
> +static int gpd_wm2_read_pwm_uncached(void)
> +{
> + const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
> + u8 var;
> + int ret = gpd_ecram_read(drvdata, drvdata->pwm_write, &var);
> +
> + if (ret < 0)
> + return ret;
> +
> + return var * 255 / drvdata->pwm_max;
> +}
> +
> +// Read value for pwm1
> +static int gpd_read_pwm(void)
> +{
> + switch (gpd_driver_priv.drvdata->board) {
> + case win_mini:
> + case win4_6800u:
> + return gpd_driver_priv.pwm_value;
> + case win_max_2:
> + return gpd_wm2_read_pwm_uncached();
> + }
> + return 0;
> +}
> +
> +static int gpd_generic_write_pwm(u8 val)
> +{
> + const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
> + u8 actual;
> +
> + // PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it.
> + actual = val * (drvdata->pwm_max - 1) / 255 + 1;
> + return gpd_ecram_write(drvdata, drvdata->pwm_write, actual);
> +}
> +
> +static int gpd_win_mini_write_pwm(u8 val)
> +{
> + if (gpd_driver_priv.pwm_enable == MANUAL)
> + return gpd_generic_write_pwm(val);
> + else
> + return -EPERM;
> +}
> +
> +static int gpd_wm2_write_pwm(u8 val)
> +{
> + if (gpd_driver_priv.pwm_enable != DISABLE)
> + return gpd_generic_write_pwm(val);
> + else
> + return -EPERM;
> +}
> +
> +// Write value for pwm1
> +static int gpd_write_pwm(u8 val)
> +{
> + switch (gpd_driver_priv.drvdata->board) {
> + case win_mini:
> + return gpd_win_mini_write_pwm(val);
> + case win4_6800u:
> + return gpd_generic_write_pwm(val);
> + case win_max_2:
> + return gpd_wm2_write_pwm(val);
> + }
> + return 0;
> +}
> +
> +static int gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
> +{
> + switch (pwm_enable) {
> + case DISABLE:
> + return gpd_generic_write_pwm(255);
> + case MANUAL:
> + return gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
> + case AUTOMATIC:
> + const struct gpd_board_drvdata *drvdata = gpd_driver_priv.drvdata;
> +
> + return gpd_ecram_write(drvdata, drvdata->pwm_write, 0);
> + }
> + return 0;
> +}
> +
> +static int gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable)
> +{
> + const struct gpd_board_drvdata *const drvdata = gpd_driver_priv.drvdata;
> + int ret;
> +
> + switch (enable) {
> + case DISABLE: {
> + ret = gpd_generic_write_pwm(255);
> +
> + if (ret)
> + return ret;
> +
> + return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
> + }
> + case MANUAL: {
> + ret = gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
> +
> + if (ret)
> + return ret;
> +
> + return gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
> + }
> + case AUTOMATIC: {
> + ret = gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0);
> +
> + return ret;
> + }
> + }
> + return 0;
> +}
> +
> +// Write value for pwm1_enable
> +static int gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable)
> +{
> + switch (gpd_driver_priv.drvdata->board) {
> + case win_mini:
> + case win4_6800u:
> + return gpd_win_mini_set_pwm_enable(enable);
> + case win_max_2:
> + return gpd_wm2_set_pwm_enable(enable);
> + }
> + return 0;
> +}
> +
> +static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata,
> + enum hwmon_sensor_types type, u32 attr,
> + __always_unused int channel)
> +{
> + if (type == hwmon_fan && attr == hwmon_fan_input) {
> + return 0444;
> + } else if (type == hwmon_pwm) {
> + switch (attr) {
> + case hwmon_pwm_enable:
> + case hwmon_pwm_input:
> + return 0644;
> + default:
> + return 0;
> + }
> + }
> + return 0;
> +}
> +
> +static int gpd_fan_hwmon_read(__always_unused struct device *dev,
> + enum hwmon_sensor_types type, u32 attr,
> + __always_unused int channel, long *val)
> +{
> + if (type == hwmon_fan) {
> + if (attr == hwmon_fan_input) {
> + int ret = gpd_read_rpm();
> +
> + if (ret < 0)
> + return ret;
> +
> + *val = ret;
> + return 0;
> + }
> + return -EOPNOTSUPP;
> + }
> + if (type == hwmon_pwm) {
> + switch (attr) {
> + case hwmon_pwm_enable:
> + *val = gpd_driver_priv.pwm_enable;
> + return 0;
> + case hwmon_pwm_input:
> + int ret = gpd_read_pwm();
> +
> + if (ret < 0)
> + return ret;
> +
> + *val = ret;
> + return 0;
> + default:
> + return -EOPNOTSUPP;
> + }
> + }
> + return -EOPNOTSUPP;
> +}
> +
> +static int gpd_fan_hwmon_write(__always_unused struct device *dev,
> + enum hwmon_sensor_types type, u32 attr,
> + __always_unused int channel, long val)
> +{
> + if (type == hwmon_pwm) {
> + switch (attr) {
> + case hwmon_pwm_enable:
> + if (!in_range(val, 0, 3))
> + return -EINVAL;
> + gpd_driver_priv.pwm_enable = val;
> + return gpd_set_pwm_enable(gpd_driver_priv.pwm_enable);
> + case hwmon_pwm_input:
> + u8 var = clamp_val(val, 0, 255);
> +
> + gpd_driver_priv.pwm_value = var;
> + return gpd_write_pwm(var);
> + default:
> + return -EOPNOTSUPP;
> + }
> + }
> + return -EOPNOTSUPP;
> +}
> +
> +static const struct hwmon_ops gpd_fan_ops = {
> + .is_visible = gpd_fan_hwmon_is_visible,
> + .read = gpd_fan_hwmon_read,
> + .write = gpd_fan_hwmon_write,
> +};
> +
> +static const struct hwmon_channel_info *gpd_fan_hwmon_channel_info[] = {
> + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
> + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), NULL
> +};
> +
> +static struct hwmon_chip_info gpd_fan_chip_info = {
> + .ops = &gpd_fan_ops,
> + .info = gpd_fan_hwmon_channel_info
> +};
> +
> +static int gpd_fan_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct gpd_driver_priv *data;
> + const struct resource *plat_res;
> + const struct device *dev_reg;
> + const struct resource *region_res;
> +
> + data = dev_get_platdata(&pdev->dev);
> + if (IS_ERR(data))
> + return -ENODEV;
> +
> + plat_res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> + if (IS_ERR(plat_res))
> + return dev_err_probe(dev, PTR_ERR(plat_res),
> + "Failed to get platform resource\n");
> +
> + region_res = devm_request_region(dev, plat_res->start,
> + resource_size(plat_res), DRIVER_NAME);
> + if (IS_ERR(region_res))
> + return dev_err_probe(dev, PTR_ERR(region_res),
> + "Failed to request region\n");
> +
> + dev_reg = devm_hwmon_device_register_with_info(dev,
> + DRIVER_NAME,
> + data,
> + &gpd_fan_chip_info,
> + NULL);
> + if (IS_ERR(dev_reg))
> + return dev_err_probe(dev, PTR_ERR(region_res),
> + "Failed to register hwmon device\n");
> +
> + return 0;
> +}
> +
> +static void gpd_fan_remove(__always_unused struct platform_device *pdev)
> +{
> + gpd_driver_priv.pwm_enable = AUTOMATIC;
> + gpd_set_pwm_enable(AUTOMATIC);
> +}
> +
> +static struct platform_driver gpd_fan_driver = {
> + .probe = gpd_fan_probe,
> + .remove = gpd_fan_remove,
> + .driver = {
> + .name = KBUILD_MODNAME,
> + },
> +};
> +
> +static struct platform_device *gpd_fan_platform_device;
> +
> +static int __init gpd_fan_init(void)
> +{
> + const struct gpd_board_drvdata *match = NULL;
> +
> + for (const struct gpd_board_drvdata **p = gpd_module_drvdata; *p; p++) {
> + if (strcmp(gpd_fan_board, (*p)->board_name) == 0) {
> + match = *p;
> + break;
> + }
> + }
> +
> + if (!match)
> + match = dmi_first_match(dmi_table)->driver_data;
> +
> + if (!match)
> + return -ENODEV;
> +
> + gpd_driver_priv.pwm_enable = AUTOMATIC;
> + gpd_driver_priv.pwm_value = 255;
> + gpd_driver_priv.drvdata = match;
> +
> + struct resource gpd_fan_resources[] = {
> + {
> + .start = match->addr_port,
> + .end = match->data_port,
> + .flags = IORESOURCE_IO,
> + },
> + };
> +
> + gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver,
> + gpd_fan_probe,
> + gpd_fan_resources,
> + 1, NULL, 0);
> +
> + if (IS_ERR(gpd_fan_platform_device)) {
> + pr_warn("Failed to create platform device\n");
> + return PTR_ERR(gpd_fan_platform_device);
> + }
> +
> + return 0;
> +}
> +
> +static void __exit gpd_fan_exit(void)
> +{
> + platform_device_unregister(gpd_fan_platform_device);
> + platform_driver_unregister(&gpd_fan_driver);
> +}
> +
> +MODULE_DEVICE_TABLE(dmi, dmi_table);
> +
> +module_init(gpd_fan_init)
> +module_exit(gpd_fan_exit)
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Cryolitia <Cryolitia@gmail.com>");
> +MODULE_DESCRIPTION("GPD Devices fan control driver");
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v5 1/2] hwmon: add GPD devices sensor driver
2025-03-02 17:31 ` Guenter Roeck
@ 2025-03-13 20:12 ` Cryolitia PukNgae
0 siblings, 0 replies; 12+ messages in thread
From: Cryolitia PukNgae @ 2025-03-13 20:12 UTC (permalink / raw)
To: Guenter Roeck
Cc: Jean Delvare, Jonathan Corbet, linux-kernel, linux-hwmon,
linux-doc, Celeste Liu, Yao Zi, Derek John Clark,
Marcin Strągowski, someone5678, Justin Weiss
> 0-day reported crashes with this driver applied. Please fix.
Fixed and v6 sent. Thx for your review!
顺颂时祺
Cryolitia
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2025-03-13 20:13 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-02-11 7:01 [PATCH v5 0/2] hwmon: add GPD devices sensor driver Cryolitia PukNgae
2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
2025-02-11 7:01 ` [PATCH v5 1/2] " Cryolitia PukNgae
2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
2025-02-13 19:32 ` kernel test robot
2025-02-17 5:03 ` kernel test robot
2025-03-02 17:31 ` Guenter Roeck
2025-03-13 20:12 ` Cryolitia PukNgae
2025-02-11 7:01 ` [PATCH v5 2/2] hwmon: document: add gpd-fan Cryolitia PukNgae
2025-02-11 7:01 ` Cryolitia PukNgae via B4 Relay
-- strict thread matches above, loose matches on Subject: below --
2025-02-14 6:05 [PATCH v5 1/2] hwmon: add GPD devices sensor driver kernel test robot
2025-02-15 2:35 kernel test robot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.