platform-driver-x86.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
@ 2025-10-31 16:36 Antheas Kapenekakis
  2025-10-31 16:36 ` [PATCH v3 1/6] " Antheas Kapenekakis
                   ` (6 more replies)
  0 siblings, 7 replies; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:36 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck, Antheas Kapenekakis

This series introduces a platform driver for Ayaneo devices, ayaneo-ec.
This driver provides hwmon support, power management, and module management
(for the new Ayaneo 3 device). Module management is done through the new
firmware attributes sysfs interface.

Luckily, all Ayaneo devices with an ACPI mapped EC use the same registers.
Older devices also use a memory mapped region for RGB[1], but that is
replaced by HID in the new Ayaneo 3. Therefore, this allows for a simple
driver design that provides robust future support. The memory mapped region
can be upstreamed as a different RGB driver in the future or remain
out-of-tree[1].

This change also allows cleaning up the oxpec driver, by removing Ayaneo
devices from it. In parallel, charge limiting is added for these devices.

[1] https://github.com/ShadowBlip/ayaneo-platform

---

V3: https://lore.kernel.org/all/20251015084414.1391595-1-lkml@antheas.dev/
V2: https://lore.kernel.org/all/20251015084414.1391595-1-lkml@antheas.dev/
V1: https://lore.kernel.org/all/20250820160628.99678-1-lkml@antheas.dev/

Changes since V3:
  - Fix various non-functional nits
  - On hibernation restore, use restore instead of thaw, switch to bools,
    and restore fan pwm mode, but only after the first pwm write after
    hibernation. Also, release pwm when entering hibernation.

Changes since V2:
  - Remove i counter from suspend hook by hardcoding the index
    (we already allocate the maximum size for the cache anyway)
  - Rename quirks to end in quirks
  - Add missing includes to controller sysfs patch, use switch statement

Changes since V1:
  - Use plain sysfs attrs for magic module attributes
  - Combine quirk for power and modules, so attribute tree is simpler
  - Use switch statement in hwmon
  - Add hibernation hook for charge bypass in last patch
    - Restoring fan speed is a liability so it is omitted, see patch notes
      Note that for EC managed fan curves, it would be acceptable
    - Regmap comment: Using regmap is unprecedented for ACPI mapped ECs
      and overkill for one value (> 100 LOC)
  - fixp_linear_interpolate() comment: it requires importing an extra header,
    is not used for static parameters in other modules, and expands to the
    same equation for parameters used, so it is omitted

Antheas Kapenekakis (6):
  platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform
    driver
  platform/x86: ayaneo-ec: Add hwmon support
  platform/x86: ayaneo-ec: Add charge control support
  platform/x86: ayaneo-ec: Add controller power and modules attributes
  platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec
  platform/x86: ayaneo-ec: Add suspend hook

 .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 +
 MAINTAINERS                                   |   7 +
 drivers/platform/x86/Kconfig                  |  16 +-
 drivers/platform/x86/Makefile                 |   3 +
 drivers/platform/x86/ayaneo-ec.c              | 582 ++++++++++++++++++
 drivers/platform/x86/oxpec.c                  | 115 +---
 6 files changed, 625 insertions(+), 117 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
 create mode 100644 drivers/platform/x86/ayaneo-ec.c


base-commit: 211ddde0823f1442e4ad052a2f30f050145ccada
-- 
2.51.1



^ permalink raw reply	[flat|nested] 30+ messages in thread

* [PATCH v3 1/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
  2025-10-31 16:36 [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
@ 2025-10-31 16:36 ` Antheas Kapenekakis
  2025-11-02 18:21   ` Armin Wolf
  2025-10-31 16:36 ` [PATCH v3 2/6] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:36 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck, Antheas Kapenekakis

Recent Ayaneo devices feature an ACPI mapped Embedded Controller (EC)
with standard addresses across models that provides access to fan
speed, fan control, battery charge limits, and controller power
controls. Introduce a new driver stub that will handle these driver
features.

Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
 MAINTAINERS                      |  6 +++
 drivers/platform/x86/Kconfig     |  9 ++++
 drivers/platform/x86/Makefile    |  3 ++
 drivers/platform/x86/ayaneo-ec.c | 90 ++++++++++++++++++++++++++++++++
 4 files changed, 108 insertions(+)
 create mode 100644 drivers/platform/x86/ayaneo-ec.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 545a4776795e..da9498d8cc89 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4187,6 +4187,12 @@ W:	https://ez.analog.com/linux-software-drivers
 F:	Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml
 F:	drivers/pwm/pwm-axi-pwmgen.c
 
+AYANEO PLATFORM EC DRIVER
+M:	Antheas Kapenekakis <lkml@antheas.dev>
+L:	platform-driver-x86@vger.kernel.org
+S:	Maintained
+F:	drivers/platform/x86/ayaneo-ec.c
+
 AZ6007 DVB DRIVER
 M:	Mauro Carvalho Chehab <mchehab@kernel.org>
 L:	linux-media@vger.kernel.org
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 46e62feeda3c..ebe7d2ab8758 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -316,6 +316,15 @@ config ASUS_TF103C_DOCK
 	  If you have an Asus TF103C tablet say Y or M here, for a generic x86
 	  distro config say M here.
 
+config AYANEO_EC
+	tristate "Ayaneo EC platform control"
+	help
+	  Enables support for the platform EC of Ayaneo devices. This
+	  includes fan control, fan speed, charge limit, magic
+	  module detection, and controller power control.
+
+	  If you have an Ayaneo device, say Y or M here.
+
 config MERAKI_MX100
 	tristate "Cisco Meraki MX100 Platform Driver"
 	depends on GPIOLIB
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index c7db2a88c11a..274a685eb92d 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -39,6 +39,9 @@ obj-$(CONFIG_ASUS_TF103C_DOCK)	+= asus-tf103c-dock.o
 obj-$(CONFIG_EEEPC_LAPTOP)	+= eeepc-laptop.o
 obj-$(CONFIG_EEEPC_WMI)		+= eeepc-wmi.o
 
+# Ayaneo
+obj-$(CONFIG_AYANEO_EC)		+= ayaneo-ec.o
+
 # Cisco/Meraki
 obj-$(CONFIG_MERAKI_MX100)	+= meraki-mx100.o
 
diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
new file mode 100644
index 000000000000..2fe66c8a89f4
--- /dev/null
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Platform driver for the Embedded Controller (EC) of Ayaneo devices. Handles
+ * hwmon (fan speed, fan control), battery charge limits, and magic module
+ * control (connected modules, controller disconnection).
+ *
+ * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
+ */
+
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+struct ayaneo_ec_quirk {
+};
+
+struct ayaneo_ec_platform_data {
+	struct platform_device *pdev;
+	struct ayaneo_ec_quirk *quirks;
+};
+
+static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
+};
+
+static const struct dmi_system_id dmi_table[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 3"),
+		},
+		.driver_data = (void *)&quirk_ayaneo3,
+	},
+	{},
+};
+
+static int ayaneo_ec_probe(struct platform_device *pdev)
+{
+	const struct dmi_system_id *dmi_entry;
+	struct ayaneo_ec_platform_data *data;
+
+	dmi_entry = dmi_first_match(dmi_table);
+	if (!dmi_entry)
+		return -ENODEV;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	data->pdev = pdev;
+	data->quirks = dmi_entry->driver_data;
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static struct platform_driver ayaneo_platform_driver = {
+	.driver = {
+		.name = "ayaneo-ec",
+	},
+	.probe = ayaneo_ec_probe,
+};
+
+static struct platform_device *ayaneo_platform_device;
+
+static int __init ayaneo_ec_init(void)
+{
+	ayaneo_platform_device =
+		platform_create_bundle(&ayaneo_platform_driver,
+				       ayaneo_ec_probe, NULL, 0, NULL, 0);
+
+	return PTR_ERR_OR_ZERO(ayaneo_platform_device);
+}
+
+static void __exit ayaneo_ec_exit(void)
+{
+	platform_device_unregister(ayaneo_platform_device);
+	platform_driver_unregister(&ayaneo_platform_driver);
+}
+
+MODULE_DEVICE_TABLE(dmi, dmi_table);
+
+module_init(ayaneo_ec_init);
+module_exit(ayaneo_ec_exit);
+
+MODULE_AUTHOR("Antheas Kapenekakis <lkml@antheas.dev>");
+MODULE_DESCRIPTION("Ayaneo Embedded Controller (EC) platform features");
+MODULE_LICENSE("GPL");
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH v3 2/6] platform/x86: ayaneo-ec: Add hwmon support
  2025-10-31 16:36 [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
  2025-10-31 16:36 ` [PATCH v3 1/6] " Antheas Kapenekakis
@ 2025-10-31 16:36 ` Antheas Kapenekakis
  2025-11-02 18:22   ` Armin Wolf
  2025-10-31 16:36 ` [PATCH v3 3/6] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:36 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck, Antheas Kapenekakis

Add hwmon single fan sensor reads and control for Ayaneo devices.
The register and method of access is the same for all devices.

Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
 drivers/platform/x86/Kconfig     |   2 +
 drivers/platform/x86/ayaneo-ec.c | 136 +++++++++++++++++++++++++++++++
 2 files changed, 138 insertions(+)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index ebe7d2ab8758..b3beaff4b03a 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -318,6 +318,8 @@ config ASUS_TF103C_DOCK
 
 config AYANEO_EC
 	tristate "Ayaneo EC platform control"
+	depends on ACPI_EC
+	depends on HWMON
 	help
 	  Enables support for the platform EC of Ayaneo devices. This
 	  includes fan control, fan speed, charge limit, magic
diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
index 2fe66c8a89f4..108a23458a4f 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -7,14 +7,24 @@
  * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
  */
 
+#include <linux/acpi.h>
 #include <linux/dmi.h>
 #include <linux/err.h>
+#include <linux/hwmon.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 
+#define AYANEO_PWM_ENABLE_REG	 0x4A
+#define AYANEO_PWM_REG		 0x4B
+#define AYANEO_PWM_MODE_AUTO	 0x00
+#define AYANEO_PWM_MODE_MANUAL	 0x01
+
+#define AYANEO_FAN_REG		 0x76
+
 struct ayaneo_ec_quirk {
+	bool has_fan_control;
 };
 
 struct ayaneo_ec_platform_data {
@@ -23,6 +33,7 @@ struct ayaneo_ec_platform_data {
 };
 
 static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
+	.has_fan_control = true,
 };
 
 static const struct dmi_system_id dmi_table[] = {
@@ -36,10 +47,128 @@ static const struct dmi_system_id dmi_table[] = {
 	{},
 };
 
+/* Callbacks for hwmon interface */
+static umode_t ayaneo_ec_hwmon_is_visible(const void *drvdata,
+					  enum hwmon_sensor_types type, u32 attr,
+					  int channel)
+{
+	switch (type) {
+	case hwmon_fan:
+		return 0444;
+	case hwmon_pwm:
+		return 0644;
+	default:
+		return 0;
+	}
+}
+
+static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
+			  u32 attr, int channel, long *val)
+{
+	u8 tmp;
+	int ret;
+
+	switch (type) {
+	case hwmon_fan:
+		switch (attr) {
+		case hwmon_fan_input:
+			ret = ec_read(AYANEO_FAN_REG, &tmp);
+			if (ret)
+				return ret;
+			*val = tmp << 8;
+			ret = ec_read(AYANEO_FAN_REG + 1, &tmp);
+			if (ret)
+				return ret;
+			*val += tmp;
+			return 0;
+		default:
+			break;
+		}
+		break;
+	case hwmon_pwm:
+		switch (attr) {
+		case hwmon_pwm_input:
+			ret = ec_read(AYANEO_PWM_REG, &tmp);
+			if (ret)
+				return ret;
+			if (tmp > 100)
+				return -EIO;
+			*val = (255 * tmp) / 100;
+			return 0;
+		case hwmon_pwm_enable:
+			ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
+			if (ret)
+				return ret;
+			if (tmp == AYANEO_PWM_MODE_MANUAL)
+				*val = 1;
+			else if (tmp == AYANEO_PWM_MODE_AUTO)
+				*val = 2;
+			else
+				return -EIO;
+			return 0;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+	return -EOPNOTSUPP;
+}
+
+static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
+			   u32 attr, int channel, long val)
+{
+	switch (type) {
+	case hwmon_pwm:
+		switch (attr) {
+		case hwmon_pwm_enable:
+			switch (val) {
+			case 1:
+				return ec_write(AYANEO_PWM_ENABLE_REG,
+						AYANEO_PWM_MODE_MANUAL);
+			case 2:
+				return ec_write(AYANEO_PWM_ENABLE_REG,
+						AYANEO_PWM_MODE_AUTO);
+			default:
+				return -EINVAL;
+			}
+		case hwmon_pwm_input:
+			if (val < 0 || val > 255)
+				return -EINVAL;
+			return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+	return -EOPNOTSUPP;
+}
+
+static const struct hwmon_ops ayaneo_ec_hwmon_ops = {
+	.is_visible = ayaneo_ec_hwmon_is_visible,
+	.read = ayaneo_ec_read,
+	.write = ayaneo_ec_write,
+};
+
+static const struct hwmon_channel_info *const ayaneo_ec_sensors[] = {
+	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
+	HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
+	NULL,
+};
+
+static const struct hwmon_chip_info ayaneo_ec_chip_info = {
+	.ops = &ayaneo_ec_hwmon_ops,
+	.info = ayaneo_ec_sensors,
+};
+
 static int ayaneo_ec_probe(struct platform_device *pdev)
 {
 	const struct dmi_system_id *dmi_entry;
 	struct ayaneo_ec_platform_data *data;
+	struct device *hwdev;
 
 	dmi_entry = dmi_first_match(dmi_table);
 	if (!dmi_entry)
@@ -53,6 +182,13 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
 	data->quirks = dmi_entry->driver_data;
 	platform_set_drvdata(pdev, data);
 
+	if (data->quirks->has_fan_control) {
+		hwdev = devm_hwmon_device_register_with_info(&pdev->dev,
+			"ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL);
+		if (IS_ERR(hwdev))
+			return PTR_ERR(hwdev);
+	}
+
 	return 0;
 }
 
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH v3 3/6] platform/x86: ayaneo-ec: Add charge control support
  2025-10-31 16:36 [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
  2025-10-31 16:36 ` [PATCH v3 1/6] " Antheas Kapenekakis
  2025-10-31 16:36 ` [PATCH v3 2/6] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
@ 2025-10-31 16:36 ` Antheas Kapenekakis
  2025-11-02 18:26   ` Armin Wolf
  2025-10-31 16:36 ` [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:36 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck, Antheas Kapenekakis, Armin Wolf

Ayaneo devices support charge inhibition via the EC. This inhibition
only works while the device is powered on, and resets between restarts.
However, it is maintained across suspend/resume cycles.

The EC does not support charge threshold control. Instead, userspace
software on Windows manually toggles charge inhibition depending on
battery level.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
 drivers/platform/x86/Kconfig     |   1 +
 drivers/platform/x86/ayaneo-ec.c | 112 +++++++++++++++++++++++++++++++
 2 files changed, 113 insertions(+)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index b3beaff4b03a..a45449ae83f8 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -319,6 +319,7 @@ config ASUS_TF103C_DOCK
 config AYANEO_EC
 	tristate "Ayaneo EC platform control"
 	depends on ACPI_EC
+	depends on ACPI_BATTERY
 	depends on HWMON
 	help
 	  Enables support for the platform EC of Ayaneo devices. This
diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
index 108a23458a4f..697bb053a7d6 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -15,6 +15,8 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <acpi/battery.h>
 
 #define AYANEO_PWM_ENABLE_REG	 0x4A
 #define AYANEO_PWM_REG		 0x4B
@@ -23,17 +25,27 @@
 
 #define AYANEO_FAN_REG		 0x76
 
+#define EC_CHARGE_CONTROL_BEHAVIOURS                         \
+	(BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |           \
+	 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
+#define AYANEO_CHARGE_REG		0x1e
+#define AYANEO_CHARGE_VAL_AUTO		0xaa
+#define AYANEO_CHARGE_VAL_INHIBIT	0x55
+
 struct ayaneo_ec_quirk {
 	bool has_fan_control;
+	bool has_charge_control;
 };
 
 struct ayaneo_ec_platform_data {
 	struct platform_device *pdev;
 	struct ayaneo_ec_quirk *quirks;
+	struct acpi_battery_hook battery_hook;
 };
 
 static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
 	.has_fan_control = true,
+	.has_charge_control = true,
 };
 
 static const struct dmi_system_id dmi_table[] = {
@@ -164,11 +176,102 @@ static const struct hwmon_chip_info ayaneo_ec_chip_info = {
 	.info = ayaneo_ec_sensors,
 };
 
+static int ayaneo_psy_ext_get_prop(struct power_supply *psy,
+				   const struct power_supply_ext *ext,
+				   void *data,
+				   enum power_supply_property psp,
+				   union power_supply_propval *val)
+{
+	int ret;
+	u8 tmp;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+		ret = ec_read(AYANEO_CHARGE_REG, &tmp);
+		if (ret)
+			return ret;
+
+		if (tmp == AYANEO_CHARGE_VAL_INHIBIT)
+			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
+		else
+			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ayaneo_psy_ext_set_prop(struct power_supply *psy,
+				   const struct power_supply_ext *ext,
+				   void *data,
+				   enum power_supply_property psp,
+				   const union power_supply_propval *val)
+{
+	u8 raw_val;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+		switch (val->intval) {
+		case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
+			raw_val = AYANEO_CHARGE_VAL_AUTO;
+			break;
+		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
+			raw_val = AYANEO_CHARGE_VAL_INHIBIT;
+			break;
+		default:
+			return -EINVAL;
+		}
+		return ec_write(AYANEO_CHARGE_REG, raw_val);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ayaneo_psy_prop_is_writeable(struct power_supply *psy,
+					const struct power_supply_ext *ext,
+					void *data,
+					enum power_supply_property psp)
+{
+	return true;
+}
+
+static const enum power_supply_property ayaneo_psy_ext_props[] = {
+	POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
+};
+
+static const struct power_supply_ext ayaneo_psy_ext = {
+	.name			= "ayaneo-charge-control",
+	.properties		= ayaneo_psy_ext_props,
+	.num_properties		= ARRAY_SIZE(ayaneo_psy_ext_props),
+	.charge_behaviours	= EC_CHARGE_CONTROL_BEHAVIOURS,
+	.get_property		= ayaneo_psy_ext_get_prop,
+	.set_property		= ayaneo_psy_ext_set_prop,
+	.property_is_writeable	= ayaneo_psy_prop_is_writeable,
+};
+
+static int ayaneo_add_battery(struct power_supply *battery,
+			      struct acpi_battery_hook *hook)
+{
+	struct ayaneo_ec_platform_data *data =
+		container_of(hook, struct ayaneo_ec_platform_data, battery_hook);
+
+	return power_supply_register_extension(battery, &ayaneo_psy_ext,
+					       &data->pdev->dev, NULL);
+}
+
+static int ayaneo_remove_battery(struct power_supply *battery,
+				 struct acpi_battery_hook *hook)
+{
+	power_supply_unregister_extension(battery, &ayaneo_psy_ext);
+	return 0;
+}
+
 static int ayaneo_ec_probe(struct platform_device *pdev)
 {
 	const struct dmi_system_id *dmi_entry;
 	struct ayaneo_ec_platform_data *data;
 	struct device *hwdev;
+	int ret;
 
 	dmi_entry = dmi_first_match(dmi_table);
 	if (!dmi_entry)
@@ -189,6 +292,15 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
 			return PTR_ERR(hwdev);
 	}
 
+	if (data->quirks->has_charge_control) {
+		data->battery_hook.add_battery = ayaneo_add_battery;
+		data->battery_hook.remove_battery = ayaneo_remove_battery;
+		data->battery_hook.name = "Ayaneo Battery";
+		ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-10-31 16:36 [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (2 preceding siblings ...)
  2025-10-31 16:36 ` [PATCH v3 3/6] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
@ 2025-10-31 16:36 ` Antheas Kapenekakis
  2025-11-02 18:30   ` Armin Wolf
  2025-10-31 16:36 ` [PATCH v3 5/6] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:36 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck, Antheas Kapenekakis, Armin Wolf

The Ayaneo 3 features hot-swappable controller modules. The ejection
and management is done through HID. However, after ejecting the modules,
the controller needs to be power cycled via the EC to re-initialize.

For this, the EC provides a variable that holds whether the left or
right modules are connected, and a power control register to turn
the controller on or off. After ejecting the modules, the controller
should be turned off. Then, after both modules are reinserted,
the controller may be powered on again to re-initialize.

This patch introduces two new sysfs attributes:
 - `controller_modules`: a read-only attribute that indicates whether
   the left and right modules are connected (none, left, right, both).
 - `controller_power`: a read-write attribute that allows the user
   to turn the controller on or off (with '1'/'0').

Therefore, after ejection is complete, userspace can power off the
controller, then wait until both modules have been reinserted
(`controller_modules` will return 'both') to turn on the controller.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
 .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 ++++
 MAINTAINERS                                   |   1 +
 drivers/platform/x86/ayaneo-ec.c              | 106 ++++++++++++++++++
 3 files changed, 126 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec

diff --git a/Documentation/ABI/testing/sysfs-platform-ayaneo-ec b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
new file mode 100644
index 000000000000..3c9c3580c685
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
@@ -0,0 +1,19 @@
+What:		/sys/devices/platform/ayaneo-ec/controller_power
+Date:		Oct 2025
+KernelVersion:	6.19
+Contact:	"Antheas Kapenekakis" <lkml@antheas.dev>
+Description:
+		Current controller power state. Allows turning on and off
+		the controller power (e.g. for power savings). Write 1 to
+		turn on, 0 to turn off. File is readable and writable.
+
+What:		/sys/devices/platform/ayaneo-ec/controller_modules
+Date:		Oct 2025
+KernelVersion:	6.19
+Contact:	"Antheas Kapenekakis"  <lkml@antheas.dev>
+Description:
+		Shows which controller modules are currently connected to
+		the device. Possible values are "left", "right" and "both".
+		File is read-only. The Windows software for this device
+		will only set controller power to 1 if both module sides
+		are connected (i.e. this file returns "both").
diff --git a/MAINTAINERS b/MAINTAINERS
index da9498d8cc89..b4d62ea9a926 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4191,6 +4191,7 @@ AYANEO PLATFORM EC DRIVER
 M:	Antheas Kapenekakis <lkml@antheas.dev>
 L:	platform-driver-x86@vger.kernel.org
 S:	Maintained
+F:	Documentation/ABI/testing/sysfs-platform-ayaneo
 F:	drivers/platform/x86/ayaneo-ec.c
 
 AZ6007 DVB DRIVER
diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
index 697bb053a7d6..0652c044ad76 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -8,6 +8,7 @@
  */
 
 #include <linux/acpi.h>
+#include <linux/bits.h>
 #include <linux/dmi.h>
 #include <linux/err.h>
 #include <linux/hwmon.h>
@@ -16,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/power_supply.h>
+#include <linux/sysfs.h>
 #include <acpi/battery.h>
 
 #define AYANEO_PWM_ENABLE_REG	 0x4A
@@ -32,9 +34,17 @@
 #define AYANEO_CHARGE_VAL_AUTO		0xaa
 #define AYANEO_CHARGE_VAL_INHIBIT	0x55
 
+#define AYANEO_POWER_REG	0x2d
+#define AYANEO_POWER_OFF	0xfe
+#define AYANEO_POWER_ON		0xff
+#define AYANEO_MODULE_REG	0x2f
+#define AYANEO_MODULE_LEFT	BIT(0)
+#define AYANEO_MODULE_RIGHT	BIT(1)
+
 struct ayaneo_ec_quirk {
 	bool has_fan_control;
 	bool has_charge_control;
+	bool has_magic_modules;
 };
 
 struct ayaneo_ec_platform_data {
@@ -46,6 +56,7 @@ struct ayaneo_ec_platform_data {
 static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
 	.has_fan_control = true,
 	.has_charge_control = true,
+	.has_magic_modules = true,
 };
 
 static const struct dmi_system_id dmi_table[] = {
@@ -266,6 +277,100 @@ static int ayaneo_remove_battery(struct power_supply *battery,
 	return 0;
 }
 
+static ssize_t controller_power_store(struct device *dev,
+				      struct device_attribute *attr,
+				      const char *buf,
+				      size_t count)
+{
+	bool value;
+	int ret;
+
+	ret = kstrtobool(buf, &value);
+	if (ret)
+		return ret;
+
+	ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static ssize_t controller_power_show(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	int ret;
+	u8 val;
+
+	ret = ec_read(AYANEO_POWER_REG, &val);
+	if (ret)
+		return ret;
+
+	return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
+}
+
+static DEVICE_ATTR_RW(controller_power);
+
+static ssize_t controller_modules_show(struct device *dev,
+				       struct device_attribute *attr, char *buf)
+{
+	char *out;
+	int ret;
+	u8 val;
+
+	ret = ec_read(AYANEO_MODULE_REG, &val);
+	if (ret)
+		return ret;
+
+	switch (~val & (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)) {
+	case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
+		out = "both";
+		break;
+	case AYANEO_MODULE_LEFT:
+		out = "left";
+		break;
+	case AYANEO_MODULE_RIGHT:
+		out = "right";
+		break;
+	default:
+		out = "none";
+		break;
+	}
+
+	return sysfs_emit(buf, "%s\n", out);
+}
+
+static DEVICE_ATTR_RO(controller_modules);
+
+static struct attribute *aya_mm_attrs[] = {
+	&dev_attr_controller_power.attr,
+	&dev_attr_controller_modules.attr,
+	NULL
+};
+
+static umode_t aya_mm_is_visible(struct kobject *kobj,
+				 struct attribute *attr, int n)
+{
+	struct device *dev = kobj_to_dev(kobj);
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
+
+	if (data->quirks->has_magic_modules)
+		return attr->mode;
+	return 0;
+}
+
+static const struct attribute_group aya_mm_attribute_group = {
+	.is_visible = aya_mm_is_visible,
+	.attrs = aya_mm_attrs,
+};
+
+static const struct attribute_group *ayaneo_ec_groups[] = {
+	&aya_mm_attribute_group,
+	NULL
+};
+
 static int ayaneo_ec_probe(struct platform_device *pdev)
 {
 	const struct dmi_system_id *dmi_entry;
@@ -307,6 +412,7 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
 static struct platform_driver ayaneo_platform_driver = {
 	.driver = {
 		.name = "ayaneo-ec",
+		.dev_groups = ayaneo_ec_groups,
 	},
 	.probe = ayaneo_ec_probe,
 };
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH v3 5/6] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec
  2025-10-31 16:36 [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (3 preceding siblings ...)
  2025-10-31 16:36 ` [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
@ 2025-10-31 16:36 ` Antheas Kapenekakis
  2025-10-31 16:36 ` [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook Antheas Kapenekakis
  2025-10-31 16:38 ` [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
  6 siblings, 0 replies; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:36 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck, Antheas Kapenekakis, Armin Wolf

Currently, the oxpec driver contains Ayaneo devices. Move them to the
new ayaneo-ec driver, which is dedicated to them.

As this driver supports charge inhibition for Ayaneo, add support for it
for the AIR, AIR 1S, AB05-Medoncino, AIR Pro, and Kun, referenced from
the out-of-tree ayaneo-platform driver.

In addition, update the readmes of oxpec to reflect this change.

Link: https://github.com/ShadowBlip/ayaneo-platform
Tested-by: Derek J. Clark <derekjohn.clark@gmail.com>
Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
 drivers/platform/x86/Kconfig     |   4 +-
 drivers/platform/x86/ayaneo-ec.c |  65 +++++++++++++++++
 drivers/platform/x86/oxpec.c     | 115 +------------------------------
 3 files changed, 67 insertions(+), 117 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index a45449ae83f8..b603f8a3bd52 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1042,9 +1042,7 @@ config OXP_EC
 	help
 		Enables support for the platform EC of OneXPlayer and AOKZOE
 		handheld devices. This includes fan speed, fan controls, and
-		disabling the default TDP behavior of the device. Due to legacy
-		reasons, this driver also provides hwmon functionality to Ayaneo
-		devices and the OrangePi Neo.
+		disabling the default TDP behavior of the device.
 
 source "drivers/platform/x86/tuxedo/Kconfig"
 
diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
index 0652c044ad76..9548e3d22093 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -53,6 +53,15 @@ struct ayaneo_ec_platform_data {
 	struct acpi_battery_hook battery_hook;
 };
 
+static const struct ayaneo_ec_quirk quirk_fan = {
+	.has_fan_control = true,
+};
+
+static const struct ayaneo_ec_quirk quirk_charge_limit = {
+	.has_fan_control = true,
+	.has_charge_control = true,
+};
+
 static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
 	.has_fan_control = true,
 	.has_charge_control = true,
@@ -60,6 +69,62 @@ static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
 };
 
 static const struct dmi_system_id dmi_table[] = {
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
+		},
+		.driver_data = (void *)&quirk_fan,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_MATCH(DMI_BOARD_NAME, "FLIP"),
+		},
+		.driver_data = (void *)&quirk_fan,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_MATCH(DMI_BOARD_NAME, "GEEK"),
+		},
+		.driver_data = (void *)&quirk_fan,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
+		},
+		.driver_data = (void *)&quirk_charge_limit,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),
+		},
+		.driver_data = (void *)&quirk_charge_limit,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
+		},
+		.driver_data = (void *)&quirk_charge_limit,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
+		},
+		.driver_data = (void *)&quirk_charge_limit,
+	},
+	{
+		.matches = {
+			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
+			DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),
+		},
+		.driver_data = (void *)&quirk_charge_limit,
+	},
 	{
 		.matches = {
 			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c
index 54377b282ff8..144a454103b9 100644
--- a/drivers/platform/x86/oxpec.c
+++ b/drivers/platform/x86/oxpec.c
@@ -1,8 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
- * Platform driver for OneXPlayer and AOKZOE devices. For the time being,
- * it also exposes fan controls for AYANEO, and OrangePi Handhelds via
- * hwmon sysfs.
+ * Platform driver for OneXPlayer and AOKZOE devices.
  *
  * Fan control is provided via pwm interface in the range [0-255].
  * Old AMD boards use [0-100] as range in the EC, the written value is
@@ -43,14 +41,6 @@ static bool unlock_global_acpi_lock(void)
 
 enum oxp_board {
 	aok_zoe_a1 = 1,
-	aya_neo_2,
-	aya_neo_air,
-	aya_neo_air_1s,
-	aya_neo_air_plus_mendo,
-	aya_neo_air_pro,
-	aya_neo_flip,
-	aya_neo_geek,
-	aya_neo_kun,
 	orange_pi_neo,
 	oxp_2,
 	oxp_fly,
@@ -131,62 +121,6 @@ static const struct dmi_system_id dmi_table[] = {
 		},
 		.driver_data = (void *)oxp_fly,
 	},
-	{
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
-			DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
-		},
-		.driver_data = (void *)aya_neo_2,
-	},
-	{
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
-			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR"),
-		},
-		.driver_data = (void *)aya_neo_air,
-	},
-	{
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
-			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),
-		},
-		.driver_data = (void *)aya_neo_air_1s,
-	},
-	{
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
-			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
-		},
-		.driver_data = (void *)aya_neo_air_plus_mendo,
-	},
-	{
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
-			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR Pro"),
-		},
-		.driver_data = (void *)aya_neo_air_pro,
-	},
-	{
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
-			DMI_MATCH(DMI_BOARD_NAME, "FLIP"),
-		},
-		.driver_data = (void *)aya_neo_flip,
-	},
-	{
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
-			DMI_MATCH(DMI_BOARD_NAME, "GEEK"),
-		},
-		.driver_data = (void *)aya_neo_geek,
-	},
-	{
-		.matches = {
-			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
-			DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),
-		},
-		.driver_data = (void *)aya_neo_kun,
-	},
 	{
 		.matches = {
 			DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"),
@@ -672,13 +606,6 @@ static int oxp_pwm_enable(void)
 	case orange_pi_neo:
 		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
 	case aok_zoe_a1:
-	case aya_neo_2:
-	case aya_neo_air:
-	case aya_neo_air_plus_mendo:
-	case aya_neo_air_pro:
-	case aya_neo_flip:
-	case aya_neo_geek:
-	case aya_neo_kun:
 	case oxp_2:
 	case oxp_fly:
 	case oxp_mini_amd:
@@ -699,14 +626,6 @@ static int oxp_pwm_disable(void)
 	case orange_pi_neo:
 		return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
 	case aok_zoe_a1:
-	case aya_neo_2:
-	case aya_neo_air:
-	case aya_neo_air_1s:
-	case aya_neo_air_plus_mendo:
-	case aya_neo_air_pro:
-	case aya_neo_flip:
-	case aya_neo_geek:
-	case aya_neo_kun:
 	case oxp_2:
 	case oxp_fly:
 	case oxp_mini_amd:
@@ -727,14 +646,6 @@ static int oxp_pwm_read(long *val)
 	case orange_pi_neo:
 		return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);
 	case aok_zoe_a1:
-	case aya_neo_2:
-	case aya_neo_air:
-	case aya_neo_air_1s:
-	case aya_neo_air_plus_mendo:
-	case aya_neo_air_pro:
-	case aya_neo_flip:
-	case aya_neo_geek:
-	case aya_neo_kun:
 	case oxp_2:
 	case oxp_fly:
 	case oxp_mini_amd:
@@ -774,14 +685,6 @@ static int oxp_pwm_fan_speed(long *val)
 	case oxp_g1_i:
 		return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
 	case aok_zoe_a1:
-	case aya_neo_2:
-	case aya_neo_air:
-	case aya_neo_air_1s:
-	case aya_neo_air_plus_mendo:
-	case aya_neo_air_pro:
-	case aya_neo_flip:
-	case aya_neo_geek:
-	case aya_neo_kun:
 	case oxp_fly:
 	case oxp_mini_amd:
 	case oxp_mini_amd_a07:
@@ -810,14 +713,6 @@ static int oxp_pwm_input_write(long val)
 		/* scale to range [0-184] */
 		val = (val * 184) / 255;
 		return write_to_ec(OXP_SENSOR_PWM_REG, val);
-	case aya_neo_2:
-	case aya_neo_air:
-	case aya_neo_air_1s:
-	case aya_neo_air_plus_mendo:
-	case aya_neo_air_pro:
-	case aya_neo_flip:
-	case aya_neo_geek:
-	case aya_neo_kun:
 	case oxp_mini_amd:
 	case oxp_mini_amd_a07:
 		/* scale to range [0-100] */
@@ -854,14 +749,6 @@ static int oxp_pwm_input_read(long *val)
 		/* scale from range [0-184] */
 		*val = (*val * 255) / 184;
 		break;
-	case aya_neo_2:
-	case aya_neo_air:
-	case aya_neo_air_1s:
-	case aya_neo_air_plus_mendo:
-	case aya_neo_air_pro:
-	case aya_neo_flip:
-	case aya_neo_geek:
-	case aya_neo_kun:
 	case oxp_mini_amd:
 	case oxp_mini_amd_a07:
 		ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-10-31 16:36 [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (4 preceding siblings ...)
  2025-10-31 16:36 ` [PATCH v3 5/6] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
@ 2025-10-31 16:36 ` Antheas Kapenekakis
  2025-11-02 18:35   ` Armin Wolf
  2025-11-03 16:51   ` Mario Limonciello (AMD) (kernel.org)
  2025-10-31 16:38 ` [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
  6 siblings, 2 replies; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:36 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck, Antheas Kapenekakis

The Ayaneo EC resets after hibernation, losing the charge control state.
Add a small PM hook to restore this state on hibernation resume.

The fan speed is also lost during hibernation, but since hibernation
failures are common with this class of devices, setting a low fan speed
when the userspace program controlling the fan will potentially not
take over could cause the device to overheat, so it is not restored.

Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
 drivers/platform/x86/ayaneo-ec.c | 73 ++++++++++++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
index 9548e3d22093..e1ad5968d3b4 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -41,6 +41,8 @@
 #define AYANEO_MODULE_LEFT	BIT(0)
 #define AYANEO_MODULE_RIGHT	BIT(1)
 
+#define AYANEO_CACHE_LEN	1
+
 struct ayaneo_ec_quirk {
 	bool has_fan_control;
 	bool has_charge_control;
@@ -51,6 +53,9 @@ struct ayaneo_ec_platform_data {
 	struct platform_device *pdev;
 	struct ayaneo_ec_quirk *quirks;
 	struct acpi_battery_hook battery_hook;
+
+	bool restore_charge_limit;
+	bool restore_pwm;
 };
 
 static const struct ayaneo_ec_quirk quirk_fan = {
@@ -207,10 +212,14 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
 static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
 			   u32 attr, int channel, long val)
 {
+	struct ayaneo_ec_platform_data *data = platform_get_drvdata(
+		to_platform_device(dev));
+	int ret;
 	switch (type) {
 	case hwmon_pwm:
 		switch (attr) {
 		case hwmon_pwm_enable:
+			data->restore_pwm = false;
 			switch (val) {
 			case 1:
 				return ec_write(AYANEO_PWM_ENABLE_REG,
@@ -224,6 +233,15 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
 		case hwmon_pwm_input:
 			if (val < 0 || val > 255)
 				return -EINVAL;
+			if (data->restore_pwm) {
+				// Defer restoring PWM control to after
+				// userspace resumes successfully
+				ret = ec_write(AYANEO_PWM_ENABLE_REG,
+					       AYANEO_PWM_MODE_MANUAL);
+				if (ret)
+					return ret;
+				data->restore_pwm = false;
+			}
 			return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
 		default:
 			break;
@@ -474,10 +492,65 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
 	return 0;
 }
 
+static int ayaneo_freeze(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
+	int ret;
+	u8 tmp;
+
+	if (data->quirks->has_charge_control) {
+		ret = ec_read(AYANEO_CHARGE_REG, &tmp);
+		if (ret)
+			return ret;
+
+		data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
+	}
+
+	if (data->quirks->has_fan_control) {
+		ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
+		if (ret)
+			return ret;
+
+		data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
+
+		// Release the fan when entering hibernation to avoid
+		// overheating if hibernation fails and hangs
+		if (data->restore_pwm) {
+			ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int ayaneo_restore(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
+	int ret;
+
+	if (data->quirks->has_charge_control && data->restore_charge_limit) {
+		ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops ayaneo_pm_ops = {
+	.freeze = ayaneo_freeze,
+	.restore = ayaneo_restore,
+};
+
 static struct platform_driver ayaneo_platform_driver = {
 	.driver = {
 		.name = "ayaneo-ec",
 		.dev_groups = ayaneo_ec_groups,
+		.pm = &ayaneo_pm_ops,
 	},
 	.probe = ayaneo_ec_probe,
 };
-- 
2.51.1



^ permalink raw reply related	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
  2025-10-31 16:36 [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (5 preceding siblings ...)
  2025-10-31 16:36 ` [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook Antheas Kapenekakis
@ 2025-10-31 16:38 ` Antheas Kapenekakis
  2025-10-31 16:39   ` Antheas Kapenekakis
  6 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:38 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

On Fri, 31 Oct 2025 at 17:36, Antheas Kapenekakis <lkml@antheas.dev> wrote:
>
> This series introduces a platform driver for Ayaneo devices, ayaneo-ec.
> This driver provides hwmon support, power management, and module management
> (for the new Ayaneo 3 device). Module management is done through the new
> firmware attributes sysfs interface.

I resent this as V3 didnt I. Mmmm

>
> Luckily, all Ayaneo devices with an ACPI mapped EC use the same registers.
> Older devices also use a memory mapped region for RGB[1], but that is
> replaced by HID in the new Ayaneo 3. Therefore, this allows for a simple
> driver design that provides robust future support. The memory mapped region
> can be upstreamed as a different RGB driver in the future or remain
> out-of-tree[1].
>
> This change also allows cleaning up the oxpec driver, by removing Ayaneo
> devices from it. In parallel, charge limiting is added for these devices.
>
> [1] https://github.com/ShadowBlip/ayaneo-platform
>
> ---
>
> V3: https://lore.kernel.org/all/20251015084414.1391595-1-lkml@antheas.dev/
> V2: https://lore.kernel.org/all/20251015084414.1391595-1-lkml@antheas.dev/
> V1: https://lore.kernel.org/all/20250820160628.99678-1-lkml@antheas.dev/
>
> Changes since V3:
>   - Fix various non-functional nits
>   - On hibernation restore, use restore instead of thaw, switch to bools,
>     and restore fan pwm mode, but only after the first pwm write after
>     hibernation. Also, release pwm when entering hibernation.
>
> Changes since V2:
>   - Remove i counter from suspend hook by hardcoding the index
>     (we already allocate the maximum size for the cache anyway)
>   - Rename quirks to end in quirks
>   - Add missing includes to controller sysfs patch, use switch statement
>
> Changes since V1:
>   - Use plain sysfs attrs for magic module attributes
>   - Combine quirk for power and modules, so attribute tree is simpler
>   - Use switch statement in hwmon
>   - Add hibernation hook for charge bypass in last patch
>     - Restoring fan speed is a liability so it is omitted, see patch notes
>       Note that for EC managed fan curves, it would be acceptable
>     - Regmap comment: Using regmap is unprecedented for ACPI mapped ECs
>       and overkill for one value (> 100 LOC)
>   - fixp_linear_interpolate() comment: it requires importing an extra header,
>     is not used for static parameters in other modules, and expands to the
>     same equation for parameters used, so it is omitted
>
> Antheas Kapenekakis (6):
>   platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform
>     driver
>   platform/x86: ayaneo-ec: Add hwmon support
>   platform/x86: ayaneo-ec: Add charge control support
>   platform/x86: ayaneo-ec: Add controller power and modules attributes
>   platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec
>   platform/x86: ayaneo-ec: Add suspend hook
>
>  .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 +
>  MAINTAINERS                                   |   7 +
>  drivers/platform/x86/Kconfig                  |  16 +-
>  drivers/platform/x86/Makefile                 |   3 +
>  drivers/platform/x86/ayaneo-ec.c              | 582 ++++++++++++++++++
>  drivers/platform/x86/oxpec.c                  | 115 +---
>  6 files changed, 625 insertions(+), 117 deletions(-)
>  create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
>  create mode 100644 drivers/platform/x86/ayaneo-ec.c
>
>
> base-commit: 211ddde0823f1442e4ad052a2f30f050145ccada
> --
> 2.51.1
>
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
  2025-10-31 16:38 ` [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
@ 2025-10-31 16:39   ` Antheas Kapenekakis
  0 siblings, 0 replies; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-10-31 16:39 UTC (permalink / raw)
  To: platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

On Fri, 31 Oct 2025 at 17:38, Antheas Kapenekakis <lkml@antheas.dev> wrote:
>
> On Fri, 31 Oct 2025 at 17:36, Antheas Kapenekakis <lkml@antheas.dev> wrote:
> >
> > This series introduces a platform driver for Ayaneo devices, ayaneo-ec.
> > This driver provides hwmon support, power management, and module management
> > (for the new Ayaneo 3 device). Module management is done through the new
> > firmware attributes sysfs interface.
>
> I resent this as V3 didnt I. Mmmm

No, it's correct. I redid the fixes and got confused by the changelog.

>
> >
> > Luckily, all Ayaneo devices with an ACPI mapped EC use the same registers.
> > Older devices also use a memory mapped region for RGB[1], but that is
> > replaced by HID in the new Ayaneo 3. Therefore, this allows for a simple
> > driver design that provides robust future support. The memory mapped region
> > can be upstreamed as a different RGB driver in the future or remain
> > out-of-tree[1].
> >
> > This change also allows cleaning up the oxpec driver, by removing Ayaneo
> > devices from it. In parallel, charge limiting is added for these devices.
> >
> > [1] https://github.com/ShadowBlip/ayaneo-platform
> >
> > ---
> >
> > V3: https://lore.kernel.org/all/20251015084414.1391595-1-lkml@antheas.dev/
Ignore V3 here

> > V2: https://lore.kernel.org/all/20251015084414.1391595-1-lkml@antheas.dev/
> > V1: https://lore.kernel.org/all/20250820160628.99678-1-lkml@antheas.dev/
> >
> > Changes since V3:
> >   - Fix various non-functional nits
> >   - On hibernation restore, use restore instead of thaw, switch to bools,
> >     and restore fan pwm mode, but only after the first pwm write after
> >     hibernation. Also, release pwm when entering hibernation.
> >
> > Changes since V2:
> >   - Remove i counter from suspend hook by hardcoding the index
> >     (we already allocate the maximum size for the cache anyway)
> >   - Rename quirks to end in quirks
> >   - Add missing includes to controller sysfs patch, use switch statement
Ignore the V2 changelog, treat v3 as v2

> >
> > Changes since V1:
> >   - Use plain sysfs attrs for magic module attributes
> >   - Combine quirk for power and modules, so attribute tree is simpler
> >   - Use switch statement in hwmon
> >   - Add hibernation hook for charge bypass in last patch
> >     - Restoring fan speed is a liability so it is omitted, see patch notes
> >       Note that for EC managed fan curves, it would be acceptable
> >     - Regmap comment: Using regmap is unprecedented for ACPI mapped ECs
> >       and overkill for one value (> 100 LOC)
> >   - fixp_linear_interpolate() comment: it requires importing an extra header,
> >     is not used for static parameters in other modules, and expands to the
> >     same equation for parameters used, so it is omitted
> >
> > Antheas Kapenekakis (6):
> >   platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform
> >     driver
> >   platform/x86: ayaneo-ec: Add hwmon support
> >   platform/x86: ayaneo-ec: Add charge control support
> >   platform/x86: ayaneo-ec: Add controller power and modules attributes
> >   platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec
> >   platform/x86: ayaneo-ec: Add suspend hook
> >
> >  .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 +
> >  MAINTAINERS                                   |   7 +
> >  drivers/platform/x86/Kconfig                  |  16 +-
> >  drivers/platform/x86/Makefile                 |   3 +
> >  drivers/platform/x86/ayaneo-ec.c              | 582 ++++++++++++++++++
> >  drivers/platform/x86/oxpec.c                  | 115 +---
> >  6 files changed, 625 insertions(+), 117 deletions(-)
> >  create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> >  create mode 100644 drivers/platform/x86/ayaneo-ec.c
> >
> >
> > base-commit: 211ddde0823f1442e4ad052a2f30f050145ccada
> > --
> > 2.51.1
> >
> >


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 1/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
  2025-10-31 16:36 ` [PATCH v3 1/6] " Antheas Kapenekakis
@ 2025-11-02 18:21   ` Armin Wolf
  2025-11-02 18:35     ` Armin Wolf
       [not found]     ` <CAGwozwGO=sZjGro7NaKH=zC4x_GR8H7kaPSn8NP60H7EZ4s3+g@mail.gmail.com>
  0 siblings, 2 replies; 30+ messages in thread
From: Armin Wolf @ 2025-11-02 18:21 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:

> Recent Ayaneo devices feature an ACPI mapped Embedded Controller (EC)
> with standard addresses across models that provides access to fan
> speed, fan control, battery charge limits, and controller power
> controls. Introduce a new driver stub that will handle these driver
> features.
>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   MAINTAINERS                      |  6 +++
>   drivers/platform/x86/Kconfig     |  9 ++++
>   drivers/platform/x86/Makefile    |  3 ++
>   drivers/platform/x86/ayaneo-ec.c | 90 ++++++++++++++++++++++++++++++++
>   4 files changed, 108 insertions(+)
>   create mode 100644 drivers/platform/x86/ayaneo-ec.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 545a4776795e..da9498d8cc89 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4187,6 +4187,12 @@ W:	https://ez.analog.com/linux-software-drivers
>   F:	Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml
>   F:	drivers/pwm/pwm-axi-pwmgen.c
>   
> +AYANEO PLATFORM EC DRIVER
> +M:	Antheas Kapenekakis <lkml@antheas.dev>
> +L:	platform-driver-x86@vger.kernel.org
> +S:	Maintained
> +F:	drivers/platform/x86/ayaneo-ec.c
> +
>   AZ6007 DVB DRIVER
>   M:	Mauro Carvalho Chehab <mchehab@kernel.org>
>   L:	linux-media@vger.kernel.org
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 46e62feeda3c..ebe7d2ab8758 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -316,6 +316,15 @@ config ASUS_TF103C_DOCK
>   	  If you have an Asus TF103C tablet say Y or M here, for a generic x86
>   	  distro config say M here.
>   
> +config AYANEO_EC
> +	tristate "Ayaneo EC platform control"
> +	help
> +	  Enables support for the platform EC of Ayaneo devices. This
> +	  includes fan control, fan speed, charge limit, magic
> +	  module detection, and controller power control.
> +
> +	  If you have an Ayaneo device, say Y or M here.
> +
>   config MERAKI_MX100
>   	tristate "Cisco Meraki MX100 Platform Driver"
>   	depends on GPIOLIB
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index c7db2a88c11a..274a685eb92d 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -39,6 +39,9 @@ obj-$(CONFIG_ASUS_TF103C_DOCK)	+= asus-tf103c-dock.o
>   obj-$(CONFIG_EEEPC_LAPTOP)	+= eeepc-laptop.o
>   obj-$(CONFIG_EEEPC_WMI)		+= eeepc-wmi.o
>   
> +# Ayaneo
> +obj-$(CONFIG_AYANEO_EC)		+= ayaneo-ec.o
> +
>   # Cisco/Meraki
>   obj-$(CONFIG_MERAKI_MX100)	+= meraki-mx100.o
>   
> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> new file mode 100644
> index 000000000000..2fe66c8a89f4
> --- /dev/null
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -0,0 +1,90 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Platform driver for the Embedded Controller (EC) of Ayaneo devices. Handles
> + * hwmon (fan speed, fan control), battery charge limits, and magic module
> + * control (connected modules, controller disconnection).
> + *
> + * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
> + */
> +
> +#include <linux/dmi.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +
> +struct ayaneo_ec_quirk {
> +};
> +
> +struct ayaneo_ec_platform_data {
> +	struct platform_device *pdev;
> +	struct ayaneo_ec_quirk *quirks;
> +};
> +
> +static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
> +};
> +
> +static const struct dmi_system_id dmi_table[] = {
> +	{
> +		.matches = {
> +			DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
> +			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 3"),
> +		},
> +		.driver_data = (void *)&quirk_ayaneo3,
> +	},
> +	{},
> +};

MODULE_DEVICE_TABLE() is missing, please add it so that the driver will
automatically load on supported devices.

> +
> +static int ayaneo_ec_probe(struct platform_device *pdev)
> +{
> +	const struct dmi_system_id *dmi_entry;
> +	struct ayaneo_ec_platform_data *data;
> +
> +	dmi_entry = dmi_first_match(dmi_table);
> +	if (!dmi_entry)
> +		return -ENODEV;

Please store the quirk inside a global variable and perform the DMI match
inside ayaneo_ec_init. This will allow you to mark the DMI table as __initconst.

Thanks,
Armin Wolf

> +
> +	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	data->pdev = pdev;
> +	data->quirks = dmi_entry->driver_data;
> +	platform_set_drvdata(pdev, data);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver ayaneo_platform_driver = {
> +	.driver = {
> +		.name = "ayaneo-ec",
> +	},
> +	.probe = ayaneo_ec_probe,
> +};
> +
> +static struct platform_device *ayaneo_platform_device;
> +
> +static int __init ayaneo_ec_init(void)
> +{
> +	ayaneo_platform_device =
> +		platform_create_bundle(&ayaneo_platform_driver,
> +				       ayaneo_ec_probe, NULL, 0, NULL, 0);
> +
> +	return PTR_ERR_OR_ZERO(ayaneo_platform_device);
> +}
> +
> +static void __exit ayaneo_ec_exit(void)
> +{
> +	platform_device_unregister(ayaneo_platform_device);
> +	platform_driver_unregister(&ayaneo_platform_driver);
> +}
> +
> +MODULE_DEVICE_TABLE(dmi, dmi_table);
> +
> +module_init(ayaneo_ec_init);
> +module_exit(ayaneo_ec_exit);
> +
> +MODULE_AUTHOR("Antheas Kapenekakis <lkml@antheas.dev>");
> +MODULE_DESCRIPTION("Ayaneo Embedded Controller (EC) platform features");
> +MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 2/6] platform/x86: ayaneo-ec: Add hwmon support
  2025-10-31 16:36 ` [PATCH v3 2/6] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
@ 2025-11-02 18:22   ` Armin Wolf
  0 siblings, 0 replies; 30+ messages in thread
From: Armin Wolf @ 2025-11-02 18:22 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:

> Add hwmon single fan sensor reads and control for Ayaneo devices.
> The register and method of access is the same for all devices.

Reviewed-by: Armin-Wolf <W_Armin@gmx.de>

> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   drivers/platform/x86/Kconfig     |   2 +
>   drivers/platform/x86/ayaneo-ec.c | 136 +++++++++++++++++++++++++++++++
>   2 files changed, 138 insertions(+)
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index ebe7d2ab8758..b3beaff4b03a 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -318,6 +318,8 @@ config ASUS_TF103C_DOCK
>   
>   config AYANEO_EC
>   	tristate "Ayaneo EC platform control"
> +	depends on ACPI_EC
> +	depends on HWMON
>   	help
>   	  Enables support for the platform EC of Ayaneo devices. This
>   	  includes fan control, fan speed, charge limit, magic
> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> index 2fe66c8a89f4..108a23458a4f 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -7,14 +7,24 @@
>    * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
>    */
>   
> +#include <linux/acpi.h>
>   #include <linux/dmi.h>
>   #include <linux/err.h>
> +#include <linux/hwmon.h>
>   #include <linux/init.h>
>   #include <linux/kernel.h>
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
>   
> +#define AYANEO_PWM_ENABLE_REG	 0x4A
> +#define AYANEO_PWM_REG		 0x4B
> +#define AYANEO_PWM_MODE_AUTO	 0x00
> +#define AYANEO_PWM_MODE_MANUAL	 0x01
> +
> +#define AYANEO_FAN_REG		 0x76
> +
>   struct ayaneo_ec_quirk {
> +	bool has_fan_control;
>   };
>   
>   struct ayaneo_ec_platform_data {
> @@ -23,6 +33,7 @@ struct ayaneo_ec_platform_data {
>   };
>   
>   static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
> +	.has_fan_control = true,
>   };
>   
>   static const struct dmi_system_id dmi_table[] = {
> @@ -36,10 +47,128 @@ static const struct dmi_system_id dmi_table[] = {
>   	{},
>   };
>   
> +/* Callbacks for hwmon interface */
> +static umode_t ayaneo_ec_hwmon_is_visible(const void *drvdata,
> +					  enum hwmon_sensor_types type, u32 attr,
> +					  int channel)
> +{
> +	switch (type) {
> +	case hwmon_fan:
> +		return 0444;
> +	case hwmon_pwm:
> +		return 0644;
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
> +			  u32 attr, int channel, long *val)
> +{
> +	u8 tmp;
> +	int ret;
> +
> +	switch (type) {
> +	case hwmon_fan:
> +		switch (attr) {
> +		case hwmon_fan_input:
> +			ret = ec_read(AYANEO_FAN_REG, &tmp);
> +			if (ret)
> +				return ret;
> +			*val = tmp << 8;
> +			ret = ec_read(AYANEO_FAN_REG + 1, &tmp);
> +			if (ret)
> +				return ret;
> +			*val += tmp;
> +			return 0;
> +		default:
> +			break;
> +		}
> +		break;
> +	case hwmon_pwm:
> +		switch (attr) {
> +		case hwmon_pwm_input:
> +			ret = ec_read(AYANEO_PWM_REG, &tmp);
> +			if (ret)
> +				return ret;
> +			if (tmp > 100)
> +				return -EIO;
> +			*val = (255 * tmp) / 100;
> +			return 0;
> +		case hwmon_pwm_enable:
> +			ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
> +			if (ret)
> +				return ret;
> +			if (tmp == AYANEO_PWM_MODE_MANUAL)
> +				*val = 1;
> +			else if (tmp == AYANEO_PWM_MODE_AUTO)
> +				*val = 2;
> +			else
> +				return -EIO;
> +			return 0;
> +		default:
> +			break;
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +	return -EOPNOTSUPP;
> +}
> +
> +static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> +			   u32 attr, int channel, long val)
> +{
> +	switch (type) {
> +	case hwmon_pwm:
> +		switch (attr) {
> +		case hwmon_pwm_enable:
> +			switch (val) {
> +			case 1:
> +				return ec_write(AYANEO_PWM_ENABLE_REG,
> +						AYANEO_PWM_MODE_MANUAL);
> +			case 2:
> +				return ec_write(AYANEO_PWM_ENABLE_REG,
> +						AYANEO_PWM_MODE_AUTO);
> +			default:
> +				return -EINVAL;
> +			}
> +		case hwmon_pwm_input:
> +			if (val < 0 || val > 255)
> +				return -EINVAL;
> +			return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
> +		default:
> +			break;
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +	return -EOPNOTSUPP;
> +}
> +
> +static const struct hwmon_ops ayaneo_ec_hwmon_ops = {
> +	.is_visible = ayaneo_ec_hwmon_is_visible,
> +	.read = ayaneo_ec_read,
> +	.write = ayaneo_ec_write,
> +};
> +
> +static const struct hwmon_channel_info *const ayaneo_ec_sensors[] = {
> +	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
> +	HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
> +	NULL,
> +};
> +
> +static const struct hwmon_chip_info ayaneo_ec_chip_info = {
> +	.ops = &ayaneo_ec_hwmon_ops,
> +	.info = ayaneo_ec_sensors,
> +};
> +
>   static int ayaneo_ec_probe(struct platform_device *pdev)
>   {
>   	const struct dmi_system_id *dmi_entry;
>   	struct ayaneo_ec_platform_data *data;
> +	struct device *hwdev;
>   
>   	dmi_entry = dmi_first_match(dmi_table);
>   	if (!dmi_entry)
> @@ -53,6 +182,13 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>   	data->quirks = dmi_entry->driver_data;
>   	platform_set_drvdata(pdev, data);
>   
> +	if (data->quirks->has_fan_control) {
> +		hwdev = devm_hwmon_device_register_with_info(&pdev->dev,
> +			"ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL);
> +		if (IS_ERR(hwdev))
> +			return PTR_ERR(hwdev);
> +	}
> +
>   	return 0;
>   }
>   

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 3/6] platform/x86: ayaneo-ec: Add charge control support
  2025-10-31 16:36 ` [PATCH v3 3/6] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
@ 2025-11-02 18:26   ` Armin Wolf
  2025-11-02 18:37     ` Antheas Kapenekakis
  0 siblings, 1 reply; 30+ messages in thread
From: Armin Wolf @ 2025-11-02 18:26 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:

> Ayaneo devices support charge inhibition via the EC. This inhibition
> only works while the device is powered on, and resets between restarts.
> However, it is maintained across suspend/resume cycles.
>
> The EC does not support charge threshold control. Instead, userspace
> software on Windows manually toggles charge inhibition depending on
> battery level.
>
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   drivers/platform/x86/Kconfig     |   1 +
>   drivers/platform/x86/ayaneo-ec.c | 112 +++++++++++++++++++++++++++++++
>   2 files changed, 113 insertions(+)
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index b3beaff4b03a..a45449ae83f8 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -319,6 +319,7 @@ config ASUS_TF103C_DOCK
>   config AYANEO_EC
>   	tristate "Ayaneo EC platform control"
>   	depends on ACPI_EC
> +	depends on ACPI_BATTERY
>   	depends on HWMON
>   	help
>   	  Enables support for the platform EC of Ayaneo devices. This
> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> index 108a23458a4f..697bb053a7d6 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -15,6 +15,8 @@
>   #include <linux/kernel.h>
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> +#include <acpi/battery.h>
>   
>   #define AYANEO_PWM_ENABLE_REG	 0x4A
>   #define AYANEO_PWM_REG		 0x4B
> @@ -23,17 +25,27 @@
>   
>   #define AYANEO_FAN_REG		 0x76
>   
> +#define EC_CHARGE_CONTROL_BEHAVIOURS                         \
> +	(BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |           \
> +	 BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))

I think POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE would be more suitable here.

Other than that:
Reviewed-by: Armin Wolf <W_Armin@gmx.de>

> +#define AYANEO_CHARGE_REG		0x1e
> +#define AYANEO_CHARGE_VAL_AUTO		0xaa
> +#define AYANEO_CHARGE_VAL_INHIBIT	0x55
> +
>   struct ayaneo_ec_quirk {
>   	bool has_fan_control;
> +	bool has_charge_control;
>   };
>   
>   struct ayaneo_ec_platform_data {
>   	struct platform_device *pdev;
>   	struct ayaneo_ec_quirk *quirks;
> +	struct acpi_battery_hook battery_hook;
>   };
>   
>   static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
>   	.has_fan_control = true,
> +	.has_charge_control = true,
>   };
>   
>   static const struct dmi_system_id dmi_table[] = {
> @@ -164,11 +176,102 @@ static const struct hwmon_chip_info ayaneo_ec_chip_info = {
>   	.info = ayaneo_ec_sensors,
>   };
>   
> +static int ayaneo_psy_ext_get_prop(struct power_supply *psy,
> +				   const struct power_supply_ext *ext,
> +				   void *data,
> +				   enum power_supply_property psp,
> +				   union power_supply_propval *val)
> +{
> +	int ret;
> +	u8 tmp;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
> +		ret = ec_read(AYANEO_CHARGE_REG, &tmp);
> +		if (ret)
> +			return ret;
> +
> +		if (tmp == AYANEO_CHARGE_VAL_INHIBIT)
> +			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
> +		else
> +			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int ayaneo_psy_ext_set_prop(struct power_supply *psy,
> +				   const struct power_supply_ext *ext,
> +				   void *data,
> +				   enum power_supply_property psp,
> +				   const union power_supply_propval *val)
> +{
> +	u8 raw_val;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
> +		switch (val->intval) {
> +		case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
> +			raw_val = AYANEO_CHARGE_VAL_AUTO;
> +			break;
> +		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
> +			raw_val = AYANEO_CHARGE_VAL_INHIBIT;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +		return ec_write(AYANEO_CHARGE_REG, raw_val);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int ayaneo_psy_prop_is_writeable(struct power_supply *psy,
> +					const struct power_supply_ext *ext,
> +					void *data,
> +					enum power_supply_property psp)
> +{
> +	return true;
> +}
> +
> +static const enum power_supply_property ayaneo_psy_ext_props[] = {
> +	POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
> +};
> +
> +static const struct power_supply_ext ayaneo_psy_ext = {
> +	.name			= "ayaneo-charge-control",
> +	.properties		= ayaneo_psy_ext_props,
> +	.num_properties		= ARRAY_SIZE(ayaneo_psy_ext_props),
> +	.charge_behaviours	= EC_CHARGE_CONTROL_BEHAVIOURS,
> +	.get_property		= ayaneo_psy_ext_get_prop,
> +	.set_property		= ayaneo_psy_ext_set_prop,
> +	.property_is_writeable	= ayaneo_psy_prop_is_writeable,
> +};
> +
> +static int ayaneo_add_battery(struct power_supply *battery,
> +			      struct acpi_battery_hook *hook)
> +{
> +	struct ayaneo_ec_platform_data *data =
> +		container_of(hook, struct ayaneo_ec_platform_data, battery_hook);
> +
> +	return power_supply_register_extension(battery, &ayaneo_psy_ext,
> +					       &data->pdev->dev, NULL);
> +}
> +
> +static int ayaneo_remove_battery(struct power_supply *battery,
> +				 struct acpi_battery_hook *hook)
> +{
> +	power_supply_unregister_extension(battery, &ayaneo_psy_ext);
> +	return 0;
> +}
> +
>   static int ayaneo_ec_probe(struct platform_device *pdev)
>   {
>   	const struct dmi_system_id *dmi_entry;
>   	struct ayaneo_ec_platform_data *data;
>   	struct device *hwdev;
> +	int ret;
>   
>   	dmi_entry = dmi_first_match(dmi_table);
>   	if (!dmi_entry)
> @@ -189,6 +292,15 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>   			return PTR_ERR(hwdev);
>   	}
>   
> +	if (data->quirks->has_charge_control) {
> +		data->battery_hook.add_battery = ayaneo_add_battery;
> +		data->battery_hook.remove_battery = ayaneo_remove_battery;
> +		data->battery_hook.name = "Ayaneo Battery";
> +		ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
> +		if (ret)
> +			return ret;
> +	}
> +
>   	return 0;
>   }
>   

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-10-31 16:36 ` [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
@ 2025-11-02 18:30   ` Armin Wolf
  2025-11-02 18:46     ` Antheas Kapenekakis
  0 siblings, 1 reply; 30+ messages in thread
From: Armin Wolf @ 2025-11-02 18:30 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:

> The Ayaneo 3 features hot-swappable controller modules. The ejection
> and management is done through HID. However, after ejecting the modules,
> the controller needs to be power cycled via the EC to re-initialize.
>
> For this, the EC provides a variable that holds whether the left or
> right modules are connected, and a power control register to turn
> the controller on or off. After ejecting the modules, the controller
> should be turned off. Then, after both modules are reinserted,
> the controller may be powered on again to re-initialize.
>
> This patch introduces two new sysfs attributes:
>   - `controller_modules`: a read-only attribute that indicates whether
>     the left and right modules are connected (none, left, right, both).
>   - `controller_power`: a read-write attribute that allows the user
>     to turn the controller on or off (with '1'/'0').
>
> Therefore, after ejection is complete, userspace can power off the
> controller, then wait until both modules have been reinserted
> (`controller_modules` will return 'both') to turn on the controller.
>
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 ++++
>   MAINTAINERS                                   |   1 +
>   drivers/platform/x86/ayaneo-ec.c              | 106 ++++++++++++++++++
>   3 files changed, 126 insertions(+)
>   create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
>
> diff --git a/Documentation/ABI/testing/sysfs-platform-ayaneo-ec b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> new file mode 100644
> index 000000000000..3c9c3580c685
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> @@ -0,0 +1,19 @@
> +What:		/sys/devices/platform/ayaneo-ec/controller_power
> +Date:		Oct 2025

I think you need to update those dates.

> +KernelVersion:	6.19
> +Contact:	"Antheas Kapenekakis" <lkml@antheas.dev>
> +Description:
> +		Current controller power state. Allows turning on and off
> +		the controller power (e.g. for power savings). Write 1 to
> +		turn on, 0 to turn off. File is readable and writable.
> +
> +What:		/sys/devices/platform/ayaneo-ec/controller_modules
> +Date:		Oct 2025
> +KernelVersion:	6.19
> +Contact:	"Antheas Kapenekakis"  <lkml@antheas.dev>
> +Description:
> +		Shows which controller modules are currently connected to
> +		the device. Possible values are "left", "right" and "both".
> +		File is read-only. The Windows software for this device
> +		will only set controller power to 1 if both module sides
> +		are connected (i.e. this file returns "both").
> diff --git a/MAINTAINERS b/MAINTAINERS
> index da9498d8cc89..b4d62ea9a926 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4191,6 +4191,7 @@ AYANEO PLATFORM EC DRIVER
>   M:	Antheas Kapenekakis <lkml@antheas.dev>
>   L:	platform-driver-x86@vger.kernel.org
>   S:	Maintained
> +F:	Documentation/ABI/testing/sysfs-platform-ayaneo
>   F:	drivers/platform/x86/ayaneo-ec.c
>   
>   AZ6007 DVB DRIVER
> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> index 697bb053a7d6..0652c044ad76 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -8,6 +8,7 @@
>    */
>   
>   #include <linux/acpi.h>
> +#include <linux/bits.h>
>   #include <linux/dmi.h>
>   #include <linux/err.h>
>   #include <linux/hwmon.h>
> @@ -16,6 +17,7 @@
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
>   #include <linux/power_supply.h>
> +#include <linux/sysfs.h>
>   #include <acpi/battery.h>
>   
>   #define AYANEO_PWM_ENABLE_REG	 0x4A
> @@ -32,9 +34,17 @@
>   #define AYANEO_CHARGE_VAL_AUTO		0xaa
>   #define AYANEO_CHARGE_VAL_INHIBIT	0x55
>   
> +#define AYANEO_POWER_REG	0x2d
> +#define AYANEO_POWER_OFF	0xfe
> +#define AYANEO_POWER_ON		0xff
> +#define AYANEO_MODULE_REG	0x2f
> +#define AYANEO_MODULE_LEFT	BIT(0)
> +#define AYANEO_MODULE_RIGHT	BIT(1)

Using GENMASK() would make sense here.

With those issues being fixed:
Reviewed-by: Armin Wolf <W_Armin@gmx.de>

> +
>   struct ayaneo_ec_quirk {
>   	bool has_fan_control;
>   	bool has_charge_control;
> +	bool has_magic_modules;
>   };
>   
>   struct ayaneo_ec_platform_data {
> @@ -46,6 +56,7 @@ struct ayaneo_ec_platform_data {
>   static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
>   	.has_fan_control = true,
>   	.has_charge_control = true,
> +	.has_magic_modules = true,
>   };
>   
>   static const struct dmi_system_id dmi_table[] = {
> @@ -266,6 +277,100 @@ static int ayaneo_remove_battery(struct power_supply *battery,
>   	return 0;
>   }
>   
> +static ssize_t controller_power_store(struct device *dev,
> +				      struct device_attribute *attr,
> +				      const char *buf,
> +				      size_t count)
> +{
> +	bool value;
> +	int ret;
> +
> +	ret = kstrtobool(buf, &value);
> +	if (ret)
> +		return ret;
> +
> +	ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
> +	if (ret)
> +		return ret;
> +
> +	return count;
> +}
> +
> +static ssize_t controller_power_show(struct device *dev,
> +				     struct device_attribute *attr,
> +				     char *buf)
> +{
> +	int ret;
> +	u8 val;
> +
> +	ret = ec_read(AYANEO_POWER_REG, &val);
> +	if (ret)
> +		return ret;
> +
> +	return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
> +}
> +
> +static DEVICE_ATTR_RW(controller_power);
> +
> +static ssize_t controller_modules_show(struct device *dev,
> +				       struct device_attribute *attr, char *buf)
> +{
> +	char *out;
> +	int ret;
> +	u8 val;
> +
> +	ret = ec_read(AYANEO_MODULE_REG, &val);
> +	if (ret)
> +		return ret;
> +
> +	switch (~val & (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)) {
> +	case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
> +		out = "both";
> +		break;
> +	case AYANEO_MODULE_LEFT:
> +		out = "left";
> +		break;
> +	case AYANEO_MODULE_RIGHT:
> +		out = "right";
> +		break;
> +	default:
> +		out = "none";
> +		break;
> +	}
> +
> +	return sysfs_emit(buf, "%s\n", out);
> +}
> +
> +static DEVICE_ATTR_RO(controller_modules);
> +
> +static struct attribute *aya_mm_attrs[] = {
> +	&dev_attr_controller_power.attr,
> +	&dev_attr_controller_modules.attr,
> +	NULL
> +};
> +
> +static umode_t aya_mm_is_visible(struct kobject *kobj,
> +				 struct attribute *attr, int n)
> +{
> +	struct device *dev = kobj_to_dev(kobj);
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> +
> +	if (data->quirks->has_magic_modules)
> +		return attr->mode;
> +	return 0;
> +}
> +
> +static const struct attribute_group aya_mm_attribute_group = {
> +	.is_visible = aya_mm_is_visible,
> +	.attrs = aya_mm_attrs,
> +};
> +
> +static const struct attribute_group *ayaneo_ec_groups[] = {
> +	&aya_mm_attribute_group,
> +	NULL
> +};
> +
>   static int ayaneo_ec_probe(struct platform_device *pdev)
>   {
>   	const struct dmi_system_id *dmi_entry;
> @@ -307,6 +412,7 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>   static struct platform_driver ayaneo_platform_driver = {
>   	.driver = {
>   		.name = "ayaneo-ec",
> +		.dev_groups = ayaneo_ec_groups,
>   	},
>   	.probe = ayaneo_ec_probe,
>   };

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-10-31 16:36 ` [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook Antheas Kapenekakis
@ 2025-11-02 18:35   ` Armin Wolf
  2025-11-02 18:58     ` Antheas Kapenekakis
  2025-11-03 16:51   ` Mario Limonciello (AMD) (kernel.org)
  1 sibling, 1 reply; 30+ messages in thread
From: Armin Wolf @ 2025-11-02 18:35 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:

> The Ayaneo EC resets after hibernation, losing the charge control state.
> Add a small PM hook to restore this state on hibernation resume.
>
> The fan speed is also lost during hibernation, but since hibernation
> failures are common with this class of devices, setting a low fan speed
> when the userspace program controlling the fan will potentially not
> take over could cause the device to overheat, so it is not restored.

Please update the patch description.

>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   drivers/platform/x86/ayaneo-ec.c | 73 ++++++++++++++++++++++++++++++++
>   1 file changed, 73 insertions(+)
>
> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> index 9548e3d22093..e1ad5968d3b4 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -41,6 +41,8 @@
>   #define AYANEO_MODULE_LEFT	BIT(0)
>   #define AYANEO_MODULE_RIGHT	BIT(1)
>   
> +#define AYANEO_CACHE_LEN	1
> +
>   struct ayaneo_ec_quirk {
>   	bool has_fan_control;
>   	bool has_charge_control;
> @@ -51,6 +53,9 @@ struct ayaneo_ec_platform_data {
>   	struct platform_device *pdev;
>   	struct ayaneo_ec_quirk *quirks;
>   	struct acpi_battery_hook battery_hook;
> +
> +	bool restore_charge_limit;
> +	bool restore_pwm;
>   };
>   
>   static const struct ayaneo_ec_quirk quirk_fan = {
> @@ -207,10 +212,14 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
>   static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
>   			   u32 attr, int channel, long val)
>   {
> +	struct ayaneo_ec_platform_data *data = platform_get_drvdata(
> +		to_platform_device(dev));
> +	int ret;
>   	switch (type) {
>   	case hwmon_pwm:
>   		switch (attr) {
>   		case hwmon_pwm_enable:
> +			data->restore_pwm = false;
>   			switch (val) {
>   			case 1:
>   				return ec_write(AYANEO_PWM_ENABLE_REG,
> @@ -224,6 +233,15 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
>   		case hwmon_pwm_input:
>   			if (val < 0 || val > 255)
>   				return -EINVAL;
> +			if (data->restore_pwm) {
> +				// Defer restoring PWM control to after
> +				// userspace resumes successfully
> +				ret = ec_write(AYANEO_PWM_ENABLE_REG,
> +					       AYANEO_PWM_MODE_MANUAL);
> +				if (ret)
> +					return ret;
> +				data->restore_pwm = false;

I suspect that you need to use a mutex to protect the restore sequence.

Thanks,
Armin Wolf

> +			}
>   			return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
>   		default:
>   			break;
> @@ -474,10 +492,65 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>   	return 0;
>   }
>   
> +static int ayaneo_freeze(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> +	int ret;
> +	u8 tmp;
> +
> +	if (data->quirks->has_charge_control) {
> +		ret = ec_read(AYANEO_CHARGE_REG, &tmp);
> +		if (ret)
> +			return ret;
> +
> +		data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
> +	}
> +
> +	if (data->quirks->has_fan_control) {
> +		ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
> +		if (ret)
> +			return ret;
> +
> +		data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
> +
> +		// Release the fan when entering hibernation to avoid
> +		// overheating if hibernation fails and hangs
> +		if (data->restore_pwm) {
> +			ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int ayaneo_restore(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	if (data->quirks->has_charge_control && data->restore_charge_limit) {
> +		ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops ayaneo_pm_ops = {
> +	.freeze = ayaneo_freeze,
> +	.restore = ayaneo_restore,
> +};
> +
>   static struct platform_driver ayaneo_platform_driver = {
>   	.driver = {
>   		.name = "ayaneo-ec",
>   		.dev_groups = ayaneo_ec_groups,
> +		.pm = &ayaneo_pm_ops,
>   	},
>   	.probe = ayaneo_ec_probe,
>   };

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 1/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
  2025-11-02 18:21   ` Armin Wolf
@ 2025-11-02 18:35     ` Armin Wolf
       [not found]     ` <CAGwozwGO=sZjGro7NaKH=zC4x_GR8H7kaPSn8NP60H7EZ4s3+g@mail.gmail.com>
  1 sibling, 0 replies; 30+ messages in thread
From: Armin Wolf @ 2025-11-02 18:35 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 02.11.25 um 19:21 schrieb Armin Wolf:

> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
>
>> Recent Ayaneo devices feature an ACPI mapped Embedded Controller (EC)
>> with standard addresses across models that provides access to fan
>> speed, fan control, battery charge limits, and controller power
>> controls. Introduce a new driver stub that will handle these driver
>> features.
>>
>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
>> ---
>>   MAINTAINERS                      |  6 +++
>>   drivers/platform/x86/Kconfig     |  9 ++++
>>   drivers/platform/x86/Makefile    |  3 ++
>>   drivers/platform/x86/ayaneo-ec.c | 90 ++++++++++++++++++++++++++++++++
>>   4 files changed, 108 insertions(+)
>>   create mode 100644 drivers/platform/x86/ayaneo-ec.c
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 545a4776795e..da9498d8cc89 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -4187,6 +4187,12 @@ W: https://ez.analog.com/linux-software-drivers
>>   F: Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml
>>   F:    drivers/pwm/pwm-axi-pwmgen.c
>>   +AYANEO PLATFORM EC DRIVER
>> +M:    Antheas Kapenekakis <lkml@antheas.dev>
>> +L:    platform-driver-x86@vger.kernel.org
>> +S:    Maintained
>> +F:    drivers/platform/x86/ayaneo-ec.c
>> +
>>   AZ6007 DVB DRIVER
>>   M:    Mauro Carvalho Chehab <mchehab@kernel.org>
>>   L:    linux-media@vger.kernel.org
>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>> index 46e62feeda3c..ebe7d2ab8758 100644
>> --- a/drivers/platform/x86/Kconfig
>> +++ b/drivers/platform/x86/Kconfig
>> @@ -316,6 +316,15 @@ config ASUS_TF103C_DOCK
>>         If you have an Asus TF103C tablet say Y or M here, for a 
>> generic x86
>>         distro config say M here.
>>   +config AYANEO_EC
>> +    tristate "Ayaneo EC platform control"

I forgot to mention that you need to depend on DMI.

>> +    help
>> +      Enables support for the platform EC of Ayaneo devices. This
>> +      includes fan control, fan speed, charge limit, magic
>> +      module detection, and controller power control.
>> +
>> +      If you have an Ayaneo device, say Y or M here.
>> +
>>   config MERAKI_MX100
>>       tristate "Cisco Meraki MX100 Platform Driver"
>>       depends on GPIOLIB
>> diff --git a/drivers/platform/x86/Makefile 
>> b/drivers/platform/x86/Makefile
>> index c7db2a88c11a..274a685eb92d 100644
>> --- a/drivers/platform/x86/Makefile
>> +++ b/drivers/platform/x86/Makefile
>> @@ -39,6 +39,9 @@ obj-$(CONFIG_ASUS_TF103C_DOCK)    += 
>> asus-tf103c-dock.o
>>   obj-$(CONFIG_EEEPC_LAPTOP)    += eeepc-laptop.o
>>   obj-$(CONFIG_EEEPC_WMI)        += eeepc-wmi.o
>>   +# Ayaneo
>> +obj-$(CONFIG_AYANEO_EC)        += ayaneo-ec.o
>> +
>>   # Cisco/Meraki
>>   obj-$(CONFIG_MERAKI_MX100)    += meraki-mx100.o
>>   diff --git a/drivers/platform/x86/ayaneo-ec.c 
>> b/drivers/platform/x86/ayaneo-ec.c
>> new file mode 100644
>> index 000000000000..2fe66c8a89f4
>> --- /dev/null
>> +++ b/drivers/platform/x86/ayaneo-ec.c
>> @@ -0,0 +1,90 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Platform driver for the Embedded Controller (EC) of Ayaneo 
>> devices. Handles
>> + * hwmon (fan speed, fan control), battery charge limits, and magic 
>> module
>> + * control (connected modules, controller disconnection).
>> + *
>> + * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
>> + */
>> +
>> +#include <linux/dmi.h>
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +
>> +struct ayaneo_ec_quirk {
>> +};
>> +
>> +struct ayaneo_ec_platform_data {
>> +    struct platform_device *pdev;
>> +    struct ayaneo_ec_quirk *quirks;
>> +};
>> +
>> +static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
>> +};
>> +
>> +static const struct dmi_system_id dmi_table[] = {
>> +    {
>> +        .matches = {
>> +            DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
>> +            DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 3"),
>> +        },
>> +        .driver_data = (void *)&quirk_ayaneo3,
>> +    },
>> +    {},
>> +};
>
> MODULE_DEVICE_TABLE() is missing, please add it so that the driver will
> automatically load on supported devices.
>
>> +
>> +static int ayaneo_ec_probe(struct platform_device *pdev)
>> +{
>> +    const struct dmi_system_id *dmi_entry;
>> +    struct ayaneo_ec_platform_data *data;
>> +
>> +    dmi_entry = dmi_first_match(dmi_table);
>> +    if (!dmi_entry)
>> +        return -ENODEV;
>
> Please store the quirk inside a global variable and perform the DMI match
> inside ayaneo_ec_init. This will allow you to mark the DMI table as 
> __initconst.
>
> Thanks,
> Armin Wolf
>
>> +
>> +    data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
>> +    if (!data)
>> +        return -ENOMEM;
>> +
>> +    data->pdev = pdev;
>> +    data->quirks = dmi_entry->driver_data;
>> +    platform_set_drvdata(pdev, data);
>> +
>> +    return 0;
>> +}
>> +
>> +static struct platform_driver ayaneo_platform_driver = {
>> +    .driver = {
>> +        .name = "ayaneo-ec",
>> +    },
>> +    .probe = ayaneo_ec_probe,
>> +};
>> +
>> +static struct platform_device *ayaneo_platform_device;
>> +
>> +static int __init ayaneo_ec_init(void)
>> +{
>> +    ayaneo_platform_device =
>> +        platform_create_bundle(&ayaneo_platform_driver,
>> +                       ayaneo_ec_probe, NULL, 0, NULL, 0);
>> +
>> +    return PTR_ERR_OR_ZERO(ayaneo_platform_device);
>> +}
>> +
>> +static void __exit ayaneo_ec_exit(void)
>> +{
>> +    platform_device_unregister(ayaneo_platform_device);
>> +    platform_driver_unregister(&ayaneo_platform_driver);
>> +}
>> +
>> +MODULE_DEVICE_TABLE(dmi, dmi_table);
>> +
>> +module_init(ayaneo_ec_init);
>> +module_exit(ayaneo_ec_exit);
>> +
>> +MODULE_AUTHOR("Antheas Kapenekakis <lkml@antheas.dev>");
>> +MODULE_DESCRIPTION("Ayaneo Embedded Controller (EC) platform 
>> features");
>> +MODULE_LICENSE("GPL");
>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 3/6] platform/x86: ayaneo-ec: Add charge control support
  2025-11-02 18:26   ` Armin Wolf
@ 2025-11-02 18:37     ` Antheas Kapenekakis
  2025-11-04 20:00       ` Armin Wolf
  0 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-11-02 18:37 UTC (permalink / raw)
  To: Armin Wolf
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

On Sun, 2 Nov 2025 at 19:26, Armin Wolf <W_Armin@gmx.de> wrote:
>
> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
>
> > Ayaneo devices support charge inhibition via the EC. This inhibition
> > only works while the device is powered on, and resets between restarts.
> > However, it is maintained across suspend/resume cycles.
> >
> > The EC does not support charge threshold control. Instead, userspace
> > software on Windows manually toggles charge inhibition depending on
> > battery level.
> >
> > Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> >   drivers/platform/x86/Kconfig     |   1 +
> >   drivers/platform/x86/ayaneo-ec.c | 112 +++++++++++++++++++++++++++++++
> >   2 files changed, 113 insertions(+)
> >
> > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> > index b3beaff4b03a..a45449ae83f8 100644
> > --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -319,6 +319,7 @@ config ASUS_TF103C_DOCK
> >   config AYANEO_EC
> >       tristate "Ayaneo EC platform control"
> >       depends on ACPI_EC
> > +     depends on ACPI_BATTERY
> >       depends on HWMON
> >       help
> >         Enables support for the platform EC of Ayaneo devices. This
> > diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> > index 108a23458a4f..697bb053a7d6 100644
> > --- a/drivers/platform/x86/ayaneo-ec.c
> > +++ b/drivers/platform/x86/ayaneo-ec.c
> > @@ -15,6 +15,8 @@
> >   #include <linux/kernel.h>
> >   #include <linux/module.h>
> >   #include <linux/platform_device.h>
> > +#include <linux/power_supply.h>
> > +#include <acpi/battery.h>
> >
> >   #define AYANEO_PWM_ENABLE_REG        0x4A
> >   #define AYANEO_PWM_REG               0x4B
> > @@ -23,17 +25,27 @@
> >
> >   #define AYANEO_FAN_REG               0x76
> >
> > +#define EC_CHARGE_CONTROL_BEHAVIOURS                         \
> > +     (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |           \
> > +      BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
>
> I think POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE would be more suitable here.

Charge inhibition works during sleep.

> Other than that:
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>
> > +#define AYANEO_CHARGE_REG            0x1e
> > +#define AYANEO_CHARGE_VAL_AUTO               0xaa
> > +#define AYANEO_CHARGE_VAL_INHIBIT    0x55
> > +
> >   struct ayaneo_ec_quirk {
> >       bool has_fan_control;
> > +     bool has_charge_control;
> >   };
> >
> >   struct ayaneo_ec_platform_data {
> >       struct platform_device *pdev;
> >       struct ayaneo_ec_quirk *quirks;
> > +     struct acpi_battery_hook battery_hook;
> >   };
> >
> >   static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
> >       .has_fan_control = true,
> > +     .has_charge_control = true,
> >   };
> >
> >   static const struct dmi_system_id dmi_table[] = {
> > @@ -164,11 +176,102 @@ static const struct hwmon_chip_info ayaneo_ec_chip_info = {
> >       .info = ayaneo_ec_sensors,
> >   };
> >
> > +static int ayaneo_psy_ext_get_prop(struct power_supply *psy,
> > +                                const struct power_supply_ext *ext,
> > +                                void *data,
> > +                                enum power_supply_property psp,
> > +                                union power_supply_propval *val)
> > +{
> > +     int ret;
> > +     u8 tmp;
> > +
> > +     switch (psp) {
> > +     case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
> > +             ret = ec_read(AYANEO_CHARGE_REG, &tmp);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             if (tmp == AYANEO_CHARGE_VAL_INHIBIT)
> > +                     val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
> > +             else
> > +                     val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
> > +             return 0;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +}
> > +
> > +static int ayaneo_psy_ext_set_prop(struct power_supply *psy,
> > +                                const struct power_supply_ext *ext,
> > +                                void *data,
> > +                                enum power_supply_property psp,
> > +                                const union power_supply_propval *val)
> > +{
> > +     u8 raw_val;
> > +
> > +     switch (psp) {
> > +     case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
> > +             switch (val->intval) {
> > +             case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
> > +                     raw_val = AYANEO_CHARGE_VAL_AUTO;
> > +                     break;
> > +             case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
> > +                     raw_val = AYANEO_CHARGE_VAL_INHIBIT;
> > +                     break;
> > +             default:
> > +                     return -EINVAL;
> > +             }
> > +             return ec_write(AYANEO_CHARGE_REG, raw_val);
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +}
> > +
> > +static int ayaneo_psy_prop_is_writeable(struct power_supply *psy,
> > +                                     const struct power_supply_ext *ext,
> > +                                     void *data,
> > +                                     enum power_supply_property psp)
> > +{
> > +     return true;
> > +}
> > +
> > +static const enum power_supply_property ayaneo_psy_ext_props[] = {
> > +     POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
> > +};
> > +
> > +static const struct power_supply_ext ayaneo_psy_ext = {
> > +     .name                   = "ayaneo-charge-control",
> > +     .properties             = ayaneo_psy_ext_props,
> > +     .num_properties         = ARRAY_SIZE(ayaneo_psy_ext_props),
> > +     .charge_behaviours      = EC_CHARGE_CONTROL_BEHAVIOURS,
> > +     .get_property           = ayaneo_psy_ext_get_prop,
> > +     .set_property           = ayaneo_psy_ext_set_prop,
> > +     .property_is_writeable  = ayaneo_psy_prop_is_writeable,
> > +};
> > +
> > +static int ayaneo_add_battery(struct power_supply *battery,
> > +                           struct acpi_battery_hook *hook)
> > +{
> > +     struct ayaneo_ec_platform_data *data =
> > +             container_of(hook, struct ayaneo_ec_platform_data, battery_hook);
> > +
> > +     return power_supply_register_extension(battery, &ayaneo_psy_ext,
> > +                                            &data->pdev->dev, NULL);
> > +}
> > +
> > +static int ayaneo_remove_battery(struct power_supply *battery,
> > +                              struct acpi_battery_hook *hook)
> > +{
> > +     power_supply_unregister_extension(battery, &ayaneo_psy_ext);
> > +     return 0;
> > +}
> > +
> >   static int ayaneo_ec_probe(struct platform_device *pdev)
> >   {
> >       const struct dmi_system_id *dmi_entry;
> >       struct ayaneo_ec_platform_data *data;
> >       struct device *hwdev;
> > +     int ret;
> >
> >       dmi_entry = dmi_first_match(dmi_table);
> >       if (!dmi_entry)
> > @@ -189,6 +292,15 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >                       return PTR_ERR(hwdev);
> >       }
> >
> > +     if (data->quirks->has_charge_control) {
> > +             data->battery_hook.add_battery = ayaneo_add_battery;
> > +             data->battery_hook.remove_battery = ayaneo_remove_battery;
> > +             data->battery_hook.name = "Ayaneo Battery";
> > +             ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> >       return 0;
> >   }
> >
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-11-02 18:30   ` Armin Wolf
@ 2025-11-02 18:46     ` Antheas Kapenekakis
  2025-11-04 20:03       ` Armin Wolf
  0 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-11-02 18:46 UTC (permalink / raw)
  To: Armin Wolf
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

On Sun, 2 Nov 2025 at 19:30, Armin Wolf <W_Armin@gmx.de> wrote:
>
> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
>
> > The Ayaneo 3 features hot-swappable controller modules. The ejection
> > and management is done through HID. However, after ejecting the modules,
> > the controller needs to be power cycled via the EC to re-initialize.
> >
> > For this, the EC provides a variable that holds whether the left or
> > right modules are connected, and a power control register to turn
> > the controller on or off. After ejecting the modules, the controller
> > should be turned off. Then, after both modules are reinserted,
> > the controller may be powered on again to re-initialize.
> >
> > This patch introduces two new sysfs attributes:
> >   - `controller_modules`: a read-only attribute that indicates whether
> >     the left and right modules are connected (none, left, right, both).
> >   - `controller_power`: a read-write attribute that allows the user
> >     to turn the controller on or off (with '1'/'0').
> >
> > Therefore, after ejection is complete, userspace can power off the
> > controller, then wait until both modules have been reinserted
> > (`controller_modules` will return 'both') to turn on the controller.
> >
> > Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> >   .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 ++++
> >   MAINTAINERS                                   |   1 +
> >   drivers/platform/x86/ayaneo-ec.c              | 106 ++++++++++++++++++
> >   3 files changed, 126 insertions(+)
> >   create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> >
> > diff --git a/Documentation/ABI/testing/sysfs-platform-ayaneo-ec b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > new file mode 100644
> > index 000000000000..3c9c3580c685
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > @@ -0,0 +1,19 @@
> > +What:                /sys/devices/platform/ayaneo-ec/controller_power
> > +Date:                Oct 2025
>
> I think you need to update those dates.
>
> > +KernelVersion:       6.19
> > +Contact:     "Antheas Kapenekakis" <lkml@antheas.dev>
> > +Description:
> > +             Current controller power state. Allows turning on and off
> > +             the controller power (e.g. for power savings). Write 1 to
> > +             turn on, 0 to turn off. File is readable and writable.
> > +
> > +What:                /sys/devices/platform/ayaneo-ec/controller_modules
> > +Date:                Oct 2025
> > +KernelVersion:       6.19
> > +Contact:     "Antheas Kapenekakis"  <lkml@antheas.dev>
> > +Description:
> > +             Shows which controller modules are currently connected to
> > +             the device. Possible values are "left", "right" and "both".
> > +             File is read-only. The Windows software for this device
> > +             will only set controller power to 1 if both module sides
> > +             are connected (i.e. this file returns "both").
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index da9498d8cc89..b4d62ea9a926 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -4191,6 +4191,7 @@ AYANEO PLATFORM EC DRIVER
> >   M:  Antheas Kapenekakis <lkml@antheas.dev>
> >   L:  platform-driver-x86@vger.kernel.org
> >   S:  Maintained
> > +F:   Documentation/ABI/testing/sysfs-platform-ayaneo
> >   F:  drivers/platform/x86/ayaneo-ec.c
> >
> >   AZ6007 DVB DRIVER
> > diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> > index 697bb053a7d6..0652c044ad76 100644
> > --- a/drivers/platform/x86/ayaneo-ec.c
> > +++ b/drivers/platform/x86/ayaneo-ec.c
> > @@ -8,6 +8,7 @@
> >    */
> >
> >   #include <linux/acpi.h>
> > +#include <linux/bits.h>
> >   #include <linux/dmi.h>
> >   #include <linux/err.h>
> >   #include <linux/hwmon.h>
> > @@ -16,6 +17,7 @@
> >   #include <linux/module.h>
> >   #include <linux/platform_device.h>
> >   #include <linux/power_supply.h>
> > +#include <linux/sysfs.h>
> >   #include <acpi/battery.h>
> >
> >   #define AYANEO_PWM_ENABLE_REG        0x4A
> > @@ -32,9 +34,17 @@
> >   #define AYANEO_CHARGE_VAL_AUTO              0xaa
> >   #define AYANEO_CHARGE_VAL_INHIBIT   0x55
> >
> > +#define AYANEO_POWER_REG     0x2d
> > +#define AYANEO_POWER_OFF     0xfe
> > +#define AYANEO_POWER_ON              0xff
> > +#define AYANEO_MODULE_REG    0x2f
> > +#define AYANEO_MODULE_LEFT   BIT(0)
> > +#define AYANEO_MODULE_RIGHT  BIT(1)
>
> Using GENMASK() would make sense here.

Only a single bit is being used though? GENMASK is used for a contiguous series?

> With those issues being fixed:
> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>
> > +
> >   struct ayaneo_ec_quirk {
> >       bool has_fan_control;
> >       bool has_charge_control;
> > +     bool has_magic_modules;
> >   };
> >
> >   struct ayaneo_ec_platform_data {
> > @@ -46,6 +56,7 @@ struct ayaneo_ec_platform_data {
> >   static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
> >       .has_fan_control = true,
> >       .has_charge_control = true,
> > +     .has_magic_modules = true,
> >   };
> >
> >   static const struct dmi_system_id dmi_table[] = {
> > @@ -266,6 +277,100 @@ static int ayaneo_remove_battery(struct power_supply *battery,
> >       return 0;
> >   }
> >
> > +static ssize_t controller_power_store(struct device *dev,
> > +                                   struct device_attribute *attr,
> > +                                   const char *buf,
> > +                                   size_t count)
> > +{
> > +     bool value;
> > +     int ret;
> > +
> > +     ret = kstrtobool(buf, &value);
> > +     if (ret)
> > +             return ret;
> > +
> > +     ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return count;
> > +}
> > +
> > +static ssize_t controller_power_show(struct device *dev,
> > +                                  struct device_attribute *attr,
> > +                                  char *buf)
> > +{
> > +     int ret;
> > +     u8 val;
> > +
> > +     ret = ec_read(AYANEO_POWER_REG, &val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
> > +}
> > +
> > +static DEVICE_ATTR_RW(controller_power);
> > +
> > +static ssize_t controller_modules_show(struct device *dev,
> > +                                    struct device_attribute *attr, char *buf)
> > +{
> > +     char *out;
> > +     int ret;
> > +     u8 val;
> > +
> > +     ret = ec_read(AYANEO_MODULE_REG, &val);
> > +     if (ret)
> > +             return ret;
> > +
> > +     switch (~val & (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)) {
> > +     case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
> > +             out = "both";
> > +             break;
> > +     case AYANEO_MODULE_LEFT:
> > +             out = "left";
> > +             break;
> > +     case AYANEO_MODULE_RIGHT:
> > +             out = "right";
> > +             break;
> > +     default:
> > +             out = "none";
> > +             break;
> > +     }
> > +
> > +     return sysfs_emit(buf, "%s\n", out);
> > +}
> > +
> > +static DEVICE_ATTR_RO(controller_modules);
> > +
> > +static struct attribute *aya_mm_attrs[] = {
> > +     &dev_attr_controller_power.attr,
> > +     &dev_attr_controller_modules.attr,
> > +     NULL
> > +};
> > +
> > +static umode_t aya_mm_is_visible(struct kobject *kobj,
> > +                              struct attribute *attr, int n)
> > +{
> > +     struct device *dev = kobj_to_dev(kobj);
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > +
> > +     if (data->quirks->has_magic_modules)
> > +             return attr->mode;
> > +     return 0;
> > +}
> > +
> > +static const struct attribute_group aya_mm_attribute_group = {
> > +     .is_visible = aya_mm_is_visible,
> > +     .attrs = aya_mm_attrs,
> > +};
> > +
> > +static const struct attribute_group *ayaneo_ec_groups[] = {
> > +     &aya_mm_attribute_group,
> > +     NULL
> > +};
> > +
> >   static int ayaneo_ec_probe(struct platform_device *pdev)
> >   {
> >       const struct dmi_system_id *dmi_entry;
> > @@ -307,6 +412,7 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >   static struct platform_driver ayaneo_platform_driver = {
> >       .driver = {
> >               .name = "ayaneo-ec",
> > +             .dev_groups = ayaneo_ec_groups,
> >       },
> >       .probe = ayaneo_ec_probe,
> >   };
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-11-02 18:35   ` Armin Wolf
@ 2025-11-02 18:58     ` Antheas Kapenekakis
  2025-11-10 12:11       ` Ilpo Järvinen
  0 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-11-02 18:58 UTC (permalink / raw)
  To: Armin Wolf
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

On Sun, 2 Nov 2025 at 19:35, Armin Wolf <W_Armin@gmx.de> wrote:
>
> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
>
> > The Ayaneo EC resets after hibernation, losing the charge control state.
> > Add a small PM hook to restore this state on hibernation resume.
> >
> > The fan speed is also lost during hibernation, but since hibernation
> > failures are common with this class of devices, setting a low fan speed
> > when the userspace program controlling the fan will potentially not
> > take over could cause the device to overheat, so it is not restored.
>
> Please update the patch description.
>
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> >   drivers/platform/x86/ayaneo-ec.c | 73 ++++++++++++++++++++++++++++++++
> >   1 file changed, 73 insertions(+)
> >
> > diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> > index 9548e3d22093..e1ad5968d3b4 100644
> > --- a/drivers/platform/x86/ayaneo-ec.c
> > +++ b/drivers/platform/x86/ayaneo-ec.c
> > @@ -41,6 +41,8 @@
> >   #define AYANEO_MODULE_LEFT  BIT(0)
> >   #define AYANEO_MODULE_RIGHT BIT(1)
> >
> > +#define AYANEO_CACHE_LEN     1
> > +
> >   struct ayaneo_ec_quirk {
> >       bool has_fan_control;
> >       bool has_charge_control;
> > @@ -51,6 +53,9 @@ struct ayaneo_ec_platform_data {
> >       struct platform_device *pdev;
> >       struct ayaneo_ec_quirk *quirks;
> >       struct acpi_battery_hook battery_hook;
> > +
> > +     bool restore_charge_limit;
> > +     bool restore_pwm;
> >   };
> >
> >   static const struct ayaneo_ec_quirk quirk_fan = {
> > @@ -207,10 +212,14 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
> >   static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> >                          u32 attr, int channel, long val)
> >   {
> > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(
> > +             to_platform_device(dev));
> > +     int ret;
> >       switch (type) {
> >       case hwmon_pwm:
> >               switch (attr) {
> >               case hwmon_pwm_enable:
> > +                     data->restore_pwm = false;
> >                       switch (val) {
> >                       case 1:
> >                               return ec_write(AYANEO_PWM_ENABLE_REG,
> > @@ -224,6 +233,15 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> >               case hwmon_pwm_input:
> >                       if (val < 0 || val > 255)
> >                               return -EINVAL;
> > +                     if (data->restore_pwm) {
> > +                             // Defer restoring PWM control to after
> > +                             // userspace resumes successfully
> > +                             ret = ec_write(AYANEO_PWM_ENABLE_REG,
> > +                                            AYANEO_PWM_MODE_MANUAL);
> > +                             if (ret)
> > +                                     return ret;
> > +                             data->restore_pwm = false;
>
> I suspect that you need to use a mutex to protect the restore sequence.

This is indeed true. I can respin the last patch with a mutex and fix
the description.

If the date on the control modules patch is the only issue, I can skip
re-sending the first 5.


> Thanks,
> Armin Wolf
>
> > +                     }
> >                       return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
> >               default:
> >                       break;
> > @@ -474,10 +492,65 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >       return 0;
> >   }
> >
> > +static int ayaneo_freeze(struct device *dev)
> > +{
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > +     int ret;
> > +     u8 tmp;
> > +
> > +     if (data->quirks->has_charge_control) {
> > +             ret = ec_read(AYANEO_CHARGE_REG, &tmp);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
> > +     }
> > +
> > +     if (data->quirks->has_fan_control) {
> > +             ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
> > +
> > +             // Release the fan when entering hibernation to avoid
> > +             // overheating if hibernation fails and hangs
> > +             if (data->restore_pwm) {
> > +                     ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
> > +                     if (ret)
> > +                             return ret;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int ayaneo_restore(struct device *dev)
> > +{
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > +     int ret;
> > +
> > +     if (data->quirks->has_charge_control && data->restore_charge_limit) {
> > +             ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct dev_pm_ops ayaneo_pm_ops = {
> > +     .freeze = ayaneo_freeze,
> > +     .restore = ayaneo_restore,
> > +};
> > +
> >   static struct platform_driver ayaneo_platform_driver = {
> >       .driver = {
> >               .name = "ayaneo-ec",
> >               .dev_groups = ayaneo_ec_groups,
> > +             .pm = &ayaneo_pm_ops,
> >       },
> >       .probe = ayaneo_ec_probe,
> >   };
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-10-31 16:36 ` [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook Antheas Kapenekakis
  2025-11-02 18:35   ` Armin Wolf
@ 2025-11-03 16:51   ` Mario Limonciello (AMD) (kernel.org)
  2025-11-03 21:20     ` Antheas Kapenekakis
  1 sibling, 1 reply; 30+ messages in thread
From: Mario Limonciello (AMD) (kernel.org) @ 2025-11-03 16:51 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck



On 10/31/2025 11:36 AM, Antheas Kapenekakis wrote:
> The Ayaneo EC resets after hibernation, losing the charge control state.
> Add a small PM hook to restore this state on hibernation resume.
> 
> The fan speed is also lost during hibernation, but since hibernation
> failures are common with this class of devices, setting a low fan speed
> when the userspace program controlling the fan will potentially not
> take over could cause the device to overheat, so it is not restored.
> 
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   drivers/platform/x86/ayaneo-ec.c | 73 ++++++++++++++++++++++++++++++++
>   1 file changed, 73 insertions(+)
> 
> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> index 9548e3d22093..e1ad5968d3b4 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -41,6 +41,8 @@
>   #define AYANEO_MODULE_LEFT	BIT(0)
>   #define AYANEO_MODULE_RIGHT	BIT(1)
>   
> +#define AYANEO_CACHE_LEN	1
> +
>   struct ayaneo_ec_quirk {
>   	bool has_fan_control;
>   	bool has_charge_control;
> @@ -51,6 +53,9 @@ struct ayaneo_ec_platform_data {
>   	struct platform_device *pdev;
>   	struct ayaneo_ec_quirk *quirks;
>   	struct acpi_battery_hook battery_hook;
> +
> +	bool restore_charge_limit;
> +	bool restore_pwm;
>   };
>   
>   static const struct ayaneo_ec_quirk quirk_fan = {
> @@ -207,10 +212,14 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
>   static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
>   			   u32 attr, int channel, long val)
>   {
> +	struct ayaneo_ec_platform_data *data = platform_get_drvdata(
> +		to_platform_device(dev));
> +	int ret;
>   	switch (type) {
>   	case hwmon_pwm:
>   		switch (attr) {
>   		case hwmon_pwm_enable:
> +			data->restore_pwm = false;
>   			switch (val) {
>   			case 1:
>   				return ec_write(AYANEO_PWM_ENABLE_REG,
> @@ -224,6 +233,15 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
>   		case hwmon_pwm_input:
>   			if (val < 0 || val > 255)
>   				return -EINVAL;
> +			if (data->restore_pwm) {
> +				// Defer restoring PWM control to after
> +				// userspace resumes successfully
> +				ret = ec_write(AYANEO_PWM_ENABLE_REG,
> +					       AYANEO_PWM_MODE_MANUAL);
> +				if (ret)
> +					return ret;
> +				data->restore_pwm = false;
> +			}
>   			return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
>   		default:
>   			break;
> @@ -474,10 +492,65 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>   	return 0;
>   }
>   
> +static int ayaneo_freeze(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> +	int ret;
> +	u8 tmp;
> +
> +	if (data->quirks->has_charge_control) {
> +		ret = ec_read(AYANEO_CHARGE_REG, &tmp);
> +		if (ret)
> +			return ret;
> +
> +		data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
> +	}
> +
> +	if (data->quirks->has_fan_control) {
> +		ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
> +		if (ret)
> +			return ret;
> +
> +		data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;

Why bother with the temp variable in the first place?

You could just make the data type of restore_pwm a u8 and then:

ec_read(AYANEO_PWM_ENABLE_REG, data->restore_pwm);


> +
> +		// Release the fan when entering hibernation to avoid
> +		// overheating if hibernation fails and hangs

Multi-line comments should be done with /* */

> +		if (data->restore_pwm) {
> +			ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int ayaneo_restore(struct device *dev)
> +{
> +	struct platform_device *pdev = to_platform_device(dev);
> +	struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	if (data->quirks->has_charge_control && data->restore_charge_limit) {
> +		ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops ayaneo_pm_ops = {
> +	.freeze = ayaneo_freeze,
> +	.restore = ayaneo_restore,
> +};

Why are freeze and restore special?  Userspace is frozen for the suspend 
sequence of any flow.  Hangs could happen in suspend just like they can 
in hibernate.  If you're going to protect users from this I would expect 
parity for "regular" suspend/resume.

Can you just use SIMPLE_DEV_PM_OPS and rename the functions accordingly?

> +
>   static struct platform_driver ayaneo_platform_driver = {
>   	.driver = {
>   		.name = "ayaneo-ec",
>   		.dev_groups = ayaneo_ec_groups,
> +		.pm = &ayaneo_pm_ops,
>   	},
>   	.probe = ayaneo_ec_probe,
>   };


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-11-03 16:51   ` Mario Limonciello (AMD) (kernel.org)
@ 2025-11-03 21:20     ` Antheas Kapenekakis
  2025-11-03 21:33       ` Mario Limonciello (AMD) (kernel.org)
  0 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-11-03 21:20 UTC (permalink / raw)
  To: Mario Limonciello (AMD) (kernel.org)
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

On Mon, 3 Nov 2025 at 17:51, Mario Limonciello (AMD) (kernel.org)
<superm1@kernel.org> wrote:
>
>
>
> On 10/31/2025 11:36 AM, Antheas Kapenekakis wrote:
> > The Ayaneo EC resets after hibernation, losing the charge control state.
> > Add a small PM hook to restore this state on hibernation resume.
> >
> > The fan speed is also lost during hibernation, but since hibernation
> > failures are common with this class of devices, setting a low fan speed
> > when the userspace program controlling the fan will potentially not
> > take over could cause the device to overheat, so it is not restored.
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> >   drivers/platform/x86/ayaneo-ec.c | 73 ++++++++++++++++++++++++++++++++
> >   1 file changed, 73 insertions(+)
> >
> > diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> > index 9548e3d22093..e1ad5968d3b4 100644
> > --- a/drivers/platform/x86/ayaneo-ec.c
> > +++ b/drivers/platform/x86/ayaneo-ec.c
> > @@ -41,6 +41,8 @@
> >   #define AYANEO_MODULE_LEFT  BIT(0)
> >   #define AYANEO_MODULE_RIGHT BIT(1)
> >
> > +#define AYANEO_CACHE_LEN     1
> > +
> >   struct ayaneo_ec_quirk {
> >       bool has_fan_control;
> >       bool has_charge_control;
> > @@ -51,6 +53,9 @@ struct ayaneo_ec_platform_data {
> >       struct platform_device *pdev;
> >       struct ayaneo_ec_quirk *quirks;
> >       struct acpi_battery_hook battery_hook;
> > +
> > +     bool restore_charge_limit;
> > +     bool restore_pwm;
> >   };
> >
> >   static const struct ayaneo_ec_quirk quirk_fan = {
> > @@ -207,10 +212,14 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
> >   static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> >                          u32 attr, int channel, long val)
> >   {
> > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(
> > +             to_platform_device(dev));
> > +     int ret;
> >       switch (type) {
> >       case hwmon_pwm:
> >               switch (attr) {
> >               case hwmon_pwm_enable:
> > +                     data->restore_pwm = false;
> >                       switch (val) {
> >                       case 1:
> >                               return ec_write(AYANEO_PWM_ENABLE_REG,
> > @@ -224,6 +233,15 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> >               case hwmon_pwm_input:
> >                       if (val < 0 || val > 255)
> >                               return -EINVAL;
> > +                     if (data->restore_pwm) {
> > +                             // Defer restoring PWM control to after
> > +                             // userspace resumes successfully
> > +                             ret = ec_write(AYANEO_PWM_ENABLE_REG,
> > +                                            AYANEO_PWM_MODE_MANUAL);
> > +                             if (ret)
> > +                                     return ret;
> > +                             data->restore_pwm = false;
> > +                     }
> >                       return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
> >               default:
> >                       break;
> > @@ -474,10 +492,65 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >       return 0;
> >   }
> >
> > +static int ayaneo_freeze(struct device *dev)
> > +{
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > +     int ret;
> > +     u8 tmp;
> > +
> > +     if (data->quirks->has_charge_control) {
> > +             ret = ec_read(AYANEO_CHARGE_REG, &tmp);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
> > +     }
> > +
> > +     if (data->quirks->has_fan_control) {
> > +             ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
>
> Why bother with the temp variable in the first place?
>
> You could just make the data type of restore_pwm a u8 and then:
>
> ec_read(AYANEO_PWM_ENABLE_REG, data->restore_pwm);

For restore_pwm it needs to be a bool because it is applied lazily on
resume only if manual. charge limit could be a u8 (it was on the
previous patch) but I chose to do a bool to match restore_pwm and so
that I also only apply it selectively.

>
> > +
> > +             // Release the fan when entering hibernation to avoid
> > +             // overheating if hibernation fails and hangs
>
> Multi-line comments should be done with /* */
>
> > +             if (data->restore_pwm) {
> > +                     ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
> > +                     if (ret)
> > +                             return ret;
> > +             }
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int ayaneo_restore(struct device *dev)
> > +{
> > +     struct platform_device *pdev = to_platform_device(dev);
> > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > +     int ret;
> > +
> > +     if (data->quirks->has_charge_control && data->restore_charge_limit) {
> > +             ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct dev_pm_ops ayaneo_pm_ops = {
> > +     .freeze = ayaneo_freeze,
> > +     .restore = ayaneo_restore,
> > +};
>
> Why are freeze and restore special?  Userspace is frozen for the suspend
> sequence of any flow.  Hangs could happen in suspend just like they can
> in hibernate.  If you're going to protect users from this I would expect
> parity for "regular" suspend/resume.
>
> Can you just use SIMPLE_DEV_PM_OPS and rename the functions accordingly?

Well, the ops here do two functions. First, they restore fan and
charge limiting state, which is only required for hibernation (both
are maintained during sleep).

Second, they ensure from entry to exit there is an automatic fan
curve. For hibernation, the failure rate is 30%-80% depending on
kernel version and userspace load (incl. which devices such as GPU are
loaded and how much). Both entry and exit can fail equally. In which
case the device may be stuck with an inappropriate fan speed for
minutes. Moreover, even without a failure, hibernation entry and exit
take around 1-2 minutes to complete so it is a nice touch to release
the manual speed for entry to maintain a reasonable fan speed.

For sleep, it is different. It always works, so there is no failure
rate. Then, it requires around 3 seconds for entry and 2 seconds for
exit, so for successful entry and exit using an automatic fan speed is
not needed. Introducing restoring auto speed a failsafe risks
introducing a user-visible flaw where the fan would spike before and
after sleep. It could potentially introduce other bugs as it does
unnecessary writes. So this is not a good reason for introducing this.

So ops are not required for sleep for either reason they were
implemented for hibernation

Ack on the rest

Antheas

> > +
> >   static struct platform_driver ayaneo_platform_driver = {
> >       .driver = {
> >               .name = "ayaneo-ec",
> >               .dev_groups = ayaneo_ec_groups,
> > +             .pm = &ayaneo_pm_ops,
> >       },
> >       .probe = ayaneo_ec_probe,
> >   };
>
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-11-03 21:20     ` Antheas Kapenekakis
@ 2025-11-03 21:33       ` Mario Limonciello (AMD) (kernel.org)
  2025-11-03 22:09         ` Antheas Kapenekakis
  0 siblings, 1 reply; 30+ messages in thread
From: Mario Limonciello (AMD) (kernel.org) @ 2025-11-03 21:33 UTC (permalink / raw)
  To: Antheas Kapenekakis
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck



On 11/3/2025 3:20 PM, Antheas Kapenekakis wrote:
> On Mon, 3 Nov 2025 at 17:51, Mario Limonciello (AMD) (kernel.org)
> <superm1@kernel.org> wrote:
>>
>>
>>
>> On 10/31/2025 11:36 AM, Antheas Kapenekakis wrote:
>>> The Ayaneo EC resets after hibernation, losing the charge control state.
>>> Add a small PM hook to restore this state on hibernation resume.
>>>
>>> The fan speed is also lost during hibernation, but since hibernation
>>> failures are common with this class of devices, setting a low fan speed
>>> when the userspace program controlling the fan will potentially not
>>> take over could cause the device to overheat, so it is not restored.
>>>
>>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
>>> ---
>>>    drivers/platform/x86/ayaneo-ec.c | 73 ++++++++++++++++++++++++++++++++
>>>    1 file changed, 73 insertions(+)
>>>
>>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
>>> index 9548e3d22093..e1ad5968d3b4 100644
>>> --- a/drivers/platform/x86/ayaneo-ec.c
>>> +++ b/drivers/platform/x86/ayaneo-ec.c
>>> @@ -41,6 +41,8 @@
>>>    #define AYANEO_MODULE_LEFT  BIT(0)
>>>    #define AYANEO_MODULE_RIGHT BIT(1)
>>>
>>> +#define AYANEO_CACHE_LEN     1
>>> +
>>>    struct ayaneo_ec_quirk {
>>>        bool has_fan_control;
>>>        bool has_charge_control;
>>> @@ -51,6 +53,9 @@ struct ayaneo_ec_platform_data {
>>>        struct platform_device *pdev;
>>>        struct ayaneo_ec_quirk *quirks;
>>>        struct acpi_battery_hook battery_hook;
>>> +
>>> +     bool restore_charge_limit;
>>> +     bool restore_pwm;
>>>    };
>>>
>>>    static const struct ayaneo_ec_quirk quirk_fan = {
>>> @@ -207,10 +212,14 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
>>>    static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
>>>                           u32 attr, int channel, long val)
>>>    {
>>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(
>>> +             to_platform_device(dev));
>>> +     int ret;
>>>        switch (type) {
>>>        case hwmon_pwm:
>>>                switch (attr) {
>>>                case hwmon_pwm_enable:
>>> +                     data->restore_pwm = false;
>>>                        switch (val) {
>>>                        case 1:
>>>                                return ec_write(AYANEO_PWM_ENABLE_REG,
>>> @@ -224,6 +233,15 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
>>>                case hwmon_pwm_input:
>>>                        if (val < 0 || val > 255)
>>>                                return -EINVAL;
>>> +                     if (data->restore_pwm) {
>>> +                             // Defer restoring PWM control to after
>>> +                             // userspace resumes successfully
>>> +                             ret = ec_write(AYANEO_PWM_ENABLE_REG,
>>> +                                            AYANEO_PWM_MODE_MANUAL);
>>> +                             if (ret)
>>> +                                     return ret;
>>> +                             data->restore_pwm = false;
>>> +                     }
>>>                        return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
>>>                default:
>>>                        break;
>>> @@ -474,10 +492,65 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>>>        return 0;
>>>    }
>>>
>>> +static int ayaneo_freeze(struct device *dev)
>>> +{
>>> +     struct platform_device *pdev = to_platform_device(dev);
>>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
>>> +     int ret;
>>> +     u8 tmp;
>>> +
>>> +     if (data->quirks->has_charge_control) {
>>> +             ret = ec_read(AYANEO_CHARGE_REG, &tmp);
>>> +             if (ret)
>>> +                     return ret;
>>> +
>>> +             data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
>>> +     }
>>> +
>>> +     if (data->quirks->has_fan_control) {
>>> +             ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
>>> +             if (ret)
>>> +                     return ret;
>>> +
>>> +             data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
>>
>> Why bother with the temp variable in the first place?
>>
>> You could just make the data type of restore_pwm a u8 and then:
>>
>> ec_read(AYANEO_PWM_ENABLE_REG, data->restore_pwm);
> 
> For restore_pwm it needs to be a bool because it is applied lazily on
> resume only if manual. charge limit could be a u8 (it was on the
> previous patch) but I chose to do a bool to match restore_pwm and so
> that I also only apply it selectively.

But you can interpret a u8 as a boolean as well was my point.  If it's 0 
it's false, if it's anything else it's true.

But I'm not gonna die on this hill, just wanted to point it out.

> 
>>
>>> +
>>> +             // Release the fan when entering hibernation to avoid
>>> +             // overheating if hibernation fails and hangs
>>
>> Multi-line comments should be done with /* */
>>
>>> +             if (data->restore_pwm) {
>>> +                     ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
>>> +                     if (ret)
>>> +                             return ret;
>>> +             }
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static int ayaneo_restore(struct device *dev)
>>> +{
>>> +     struct platform_device *pdev = to_platform_device(dev);
>>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
>>> +     int ret;
>>> +
>>> +     if (data->quirks->has_charge_control && data->restore_charge_limit) {
>>> +             ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
>>> +             if (ret)
>>> +                     return ret;
>>> +     }
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static const struct dev_pm_ops ayaneo_pm_ops = {
>>> +     .freeze = ayaneo_freeze,
>>> +     .restore = ayaneo_restore,
>>> +};
>>
>> Why are freeze and restore special?  Userspace is frozen for the suspend
>> sequence of any flow.  Hangs could happen in suspend just like they can
>> in hibernate.  If you're going to protect users from this I would expect
>> parity for "regular" suspend/resume.
>>
>> Can you just use SIMPLE_DEV_PM_OPS and rename the functions accordingly?
> 
> Well, the ops here do two functions. First, they restore fan and
> charge limiting state, which is only required for hibernation (both
> are maintained during sleep).
> 
> Second, they ensure from entry to exit there is an automatic fan
> curve. For hibernation, the failure rate is 30%-80% depending on
> kernel version and userspace load (incl. which devices such as GPU are
> loaded and how much). Both entry and exit can fail equally. In which
> case the device may be stuck with an inappropriate fan speed for
> minutes. Moreover, even without a failure, hibernation entry and exit
> take around 1-2 minutes to complete so it is a nice touch to release
> the manual speed for entry to maintain a reasonable fan speed.
> 
> For sleep, it is different. It always works, 

Having spent enough time looking at sleep problems I would never make a 
statement like that.  I try really hard to stay on on top of it, but the 
reality is regressions happen all the time.

> so there is no failure
> rate. Then, it requires around 3 seconds for entry and 2 seconds for
> exit, so for successful entry and exit using an automatic fan speed is
> not needed. Introducing restoring auto speed a failsafe risks
> introducing a user-visible flaw where the fan would spike before and
> after sleep. It could potentially introduce other bugs as it does
> unnecessary writes. So this is not a good reason for introducing this.

The other thing to keep in mind is that regressions can happen in 
firmware too, and this is why I generally feel it's best to be 
conservative around sleep states in this area.

I would never tell someone to do it, but technically you can unbind the 
lps0 device.  If this happens what happens to this fan curve stuff? 
Userspace will be frozen and the hardware won't got to a hardware sleep 
state.
> 
> So ops are not required for sleep for either reason they were
> implemented for hibernation
> 
> Ack on the rest
> 
> Antheas
> 
>>> +
>>>    static struct platform_driver ayaneo_platform_driver = {
>>>        .driver = {
>>>                .name = "ayaneo-ec",
>>>                .dev_groups = ayaneo_ec_groups,
>>> +             .pm = &ayaneo_pm_ops,
>>>        },
>>>        .probe = ayaneo_ec_probe,
>>>    };
>>
>>
> 


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-11-03 21:33       ` Mario Limonciello (AMD) (kernel.org)
@ 2025-11-03 22:09         ` Antheas Kapenekakis
  0 siblings, 0 replies; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-11-03 22:09 UTC (permalink / raw)
  To: Mario Limonciello (AMD) (kernel.org)
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

On Mon, 3 Nov 2025 at 22:34, Mario Limonciello (AMD) (kernel.org)
<superm1@kernel.org> wrote:
>
>
>
> On 11/3/2025 3:20 PM, Antheas Kapenekakis wrote:
> > On Mon, 3 Nov 2025 at 17:51, Mario Limonciello (AMD) (kernel.org)
> > <superm1@kernel.org> wrote:
> >>
> >>
> >>
> >> On 10/31/2025 11:36 AM, Antheas Kapenekakis wrote:
> >>> The Ayaneo EC resets after hibernation, losing the charge control state.
> >>> Add a small PM hook to restore this state on hibernation resume.
> >>>
> >>> The fan speed is also lost during hibernation, but since hibernation
> >>> failures are common with this class of devices, setting a low fan speed
> >>> when the userspace program controlling the fan will potentially not
> >>> take over could cause the device to overheat, so it is not restored.
> >>>
> >>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> >>> ---
> >>>    drivers/platform/x86/ayaneo-ec.c | 73 ++++++++++++++++++++++++++++++++
> >>>    1 file changed, 73 insertions(+)
> >>>
> >>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> >>> index 9548e3d22093..e1ad5968d3b4 100644
> >>> --- a/drivers/platform/x86/ayaneo-ec.c
> >>> +++ b/drivers/platform/x86/ayaneo-ec.c
> >>> @@ -41,6 +41,8 @@
> >>>    #define AYANEO_MODULE_LEFT  BIT(0)
> >>>    #define AYANEO_MODULE_RIGHT BIT(1)
> >>>
> >>> +#define AYANEO_CACHE_LEN     1
> >>> +
> >>>    struct ayaneo_ec_quirk {
> >>>        bool has_fan_control;
> >>>        bool has_charge_control;
> >>> @@ -51,6 +53,9 @@ struct ayaneo_ec_platform_data {
> >>>        struct platform_device *pdev;
> >>>        struct ayaneo_ec_quirk *quirks;
> >>>        struct acpi_battery_hook battery_hook;
> >>> +
> >>> +     bool restore_charge_limit;
> >>> +     bool restore_pwm;
> >>>    };
> >>>
> >>>    static const struct ayaneo_ec_quirk quirk_fan = {
> >>> @@ -207,10 +212,14 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
> >>>    static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> >>>                           u32 attr, int channel, long val)
> >>>    {
> >>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(
> >>> +             to_platform_device(dev));
> >>> +     int ret;
> >>>        switch (type) {
> >>>        case hwmon_pwm:
> >>>                switch (attr) {
> >>>                case hwmon_pwm_enable:
> >>> +                     data->restore_pwm = false;
> >>>                        switch (val) {
> >>>                        case 1:
> >>>                                return ec_write(AYANEO_PWM_ENABLE_REG,
> >>> @@ -224,6 +233,15 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> >>>                case hwmon_pwm_input:
> >>>                        if (val < 0 || val > 255)
> >>>                                return -EINVAL;
> >>> +                     if (data->restore_pwm) {
> >>> +                             // Defer restoring PWM control to after
> >>> +                             // userspace resumes successfully
> >>> +                             ret = ec_write(AYANEO_PWM_ENABLE_REG,
> >>> +                                            AYANEO_PWM_MODE_MANUAL);
> >>> +                             if (ret)
> >>> +                                     return ret;
> >>> +                             data->restore_pwm = false;
> >>> +                     }
> >>>                        return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
> >>>                default:
> >>>                        break;
> >>> @@ -474,10 +492,65 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >>>        return 0;
> >>>    }
> >>>
> >>> +static int ayaneo_freeze(struct device *dev)
> >>> +{
> >>> +     struct platform_device *pdev = to_platform_device(dev);
> >>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> >>> +     int ret;
> >>> +     u8 tmp;
> >>> +
> >>> +     if (data->quirks->has_charge_control) {
> >>> +             ret = ec_read(AYANEO_CHARGE_REG, &tmp);
> >>> +             if (ret)
> >>> +                     return ret;
> >>> +
> >>> +             data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
> >>> +     }
> >>> +
> >>> +     if (data->quirks->has_fan_control) {
> >>> +             ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
> >>> +             if (ret)
> >>> +                     return ret;
> >>> +
> >>> +             data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
> >>
> >> Why bother with the temp variable in the first place?
> >>
> >> You could just make the data type of restore_pwm a u8 and then:
> >>
> >> ec_read(AYANEO_PWM_ENABLE_REG, data->restore_pwm);
> >
> > For restore_pwm it needs to be a bool because it is applied lazily on
> > resume only if manual. charge limit could be a u8 (it was on the
> > previous patch) but I chose to do a bool to match restore_pwm and so
> > that I also only apply it selectively.
>
> But you can interpret a u8 as a boolean as well was my point.  If it's 0
> it's false, if it's anything else it's true.
>
> But I'm not gonna die on this hill, just wanted to point it out.

Fair. Technically, 0 is a valid register value but in this case it
would work the same, as it would be silently skipped. Ideally, it
would be u16 with -1 but at that point it is a combined boolean +
register which is the same as a boolean with a fixed register value
but more complicated.

> >
> >>
> >>> +
> >>> +             // Release the fan when entering hibernation to avoid
> >>> +             // overheating if hibernation fails and hangs
> >>
> >> Multi-line comments should be done with /* */
> >>
> >>> +             if (data->restore_pwm) {
> >>> +                     ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
> >>> +                     if (ret)
> >>> +                             return ret;
> >>> +             }
> >>> +     }
> >>> +
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +static int ayaneo_restore(struct device *dev)
> >>> +{
> >>> +     struct platform_device *pdev = to_platform_device(dev);
> >>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> >>> +     int ret;
> >>> +
> >>> +     if (data->quirks->has_charge_control && data->restore_charge_limit) {
> >>> +             ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
> >>> +             if (ret)
> >>> +                     return ret;
> >>> +     }
> >>> +
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +static const struct dev_pm_ops ayaneo_pm_ops = {
> >>> +     .freeze = ayaneo_freeze,
> >>> +     .restore = ayaneo_restore,
> >>> +};
> >>
> >> Why are freeze and restore special?  Userspace is frozen for the suspend
> >> sequence of any flow.  Hangs could happen in suspend just like they can
> >> in hibernate.  If you're going to protect users from this I would expect
> >> parity for "regular" suspend/resume.
> >>
> >> Can you just use SIMPLE_DEV_PM_OPS and rename the functions accordingly?
> >
> > Well, the ops here do two functions. First, they restore fan and
> > charge limiting state, which is only required for hibernation (both
> > are maintained during sleep).
> >
> > Second, they ensure from entry to exit there is an automatic fan
> > curve. For hibernation, the failure rate is 30%-80% depending on
> > kernel version and userspace load (incl. which devices such as GPU are
> > loaded and how much). Both entry and exit can fail equally. In which
> > case the device may be stuck with an inappropriate fan speed for
> > minutes. Moreover, even without a failure, hibernation entry and exit
> > take around 1-2 minutes to complete so it is a nice touch to release
> > the manual speed for entry to maintain a reasonable fan speed.
> >
> > For sleep, it is different. It always works,
>
> Having spent enough time looking at sleep problems I would never make a
> statement like that.  I try really hard to stay on on top of it, but the
> reality is regressions happen all the time.

Yeah that is true, but when it comes to this small subset of handhelds
the kernel is considered broken if it has a failure rate on sleep so I
would not optimize for that.

> > so there is no failure
> > rate. Then, it requires around 3 seconds for entry and 2 seconds for
> > exit, so for successful entry and exit using an automatic fan speed is
> > not needed. Introducing restoring auto speed a failsafe risks
> > introducing a user-visible flaw where the fan would spike before and
> > after sleep. It could potentially introduce other bugs as it does
> > unnecessary writes. So this is not a good reason for introducing this.
>
> The other thing to keep in mind is that regressions can happen in
> firmware too, and this is why I generally feel it's best to be
> conservative around sleep states in this area.

This is what other bugs is referring to.

> I would never tell someone to do it, but technically you can unbind the
> lps0 device.  If this happens what happens to this fan curve stuff?
> Userspace will be frozen and the hardware won't got to a hardware sleep
> state.

For these devices the fan speed would remain static, which is a
failure mode. For other devices such as the Xbox Ally, it has a
hardware fan curve with baked minimums so it is ok.

The good thing with the lsp0 device is that if it is in ACPI it always
works (except on the Xbox Ally which is a different thread). So it
either never works or it always does and there is no chance a user
will inadvertently have it fail and not notice it.

If the LSP0 device is not in ACPI, this means the BIOS is set with
modern standby disabled, which is an issue in certain models of this
manufacturer. But in this case, the FADT does not have the "S0ix more
efficient than S3" bit toggled, which can be detected. So e.g., on
Bazzite if this is the case the device is forced to hibernate or the
sleep button does not work. In other distributions, sleep will never
work, so users will notice and again it's not inadvertent.

Now, if users decide to use smokeless on a distribution other than
Bazzite to enable CBS and toggle on modern standby there is a
different failure mode. And that is that when the battery runs out and
the CMOS memory resets, modern standby will be disabled, so the device
will fail to sleep the next time and they might not notice it. I do
not recommend my users do BIOS mods, but specifically for those that
chose to do it and are not my users (see: forced hibernation), this
can potentially happen

Antheas

> >
> > So ops are not required for sleep for either reason they were
> > implemented for hibernation
> >
> > Ack on the rest
> >
> > Antheas
> >
> >>> +
> >>>    static struct platform_driver ayaneo_platform_driver = {
> >>>        .driver = {
> >>>                .name = "ayaneo-ec",
> >>>                .dev_groups = ayaneo_ec_groups,
> >>> +             .pm = &ayaneo_pm_ops,
> >>>        },
> >>>        .probe = ayaneo_ec_probe,
> >>>    };
> >>
> >>
> >
>
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 1/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
       [not found]     ` <CAGwozwGO=sZjGro7NaKH=zC4x_GR8H7kaPSn8NP60H7EZ4s3+g@mail.gmail.com>
@ 2025-11-04 19:58       ` Armin Wolf
  0 siblings, 0 replies; 30+ messages in thread
From: Armin Wolf @ 2025-11-04 19:58 UTC (permalink / raw)
  To: Antheas Kapenekakis
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

Am 02.11.25 um 19:37 schrieb Antheas Kapenekakis:

> On Sun, 2 Nov 2025 at 19:21, Armin Wolf <W_Armin@gmx.de> wrote:
>> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
>>
>>> Recent Ayaneo devices feature an ACPI mapped Embedded Controller (EC)
>>> with standard addresses across models that provides access to fan
>>> speed, fan control, battery charge limits, and controller power
>>> controls. Introduce a new driver stub that will handle these driver
>>> features.
>>>
>>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
>>> ---
>>>    MAINTAINERS                      |  6 +++
>>>    drivers/platform/x86/Kconfig     |  9 ++++
>>>    drivers/platform/x86/Makefile    |  3 ++
>>>    drivers/platform/x86/ayaneo-ec.c | 90 ++++++++++++++++++++++++++++++++
>>>    4 files changed, 108 insertions(+)
>>>    create mode 100644 drivers/platform/x86/ayaneo-ec.c
>>>
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index 545a4776795e..da9498d8cc89 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -4187,6 +4187,12 @@ W:     https://ez.analog.com/linux-software-drivers
>>>    F:  Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml
>>>    F:  drivers/pwm/pwm-axi-pwmgen.c
>>>
>>> +AYANEO PLATFORM EC DRIVER
>>> +M:   Antheas Kapenekakis <lkml@antheas.dev>
>>> +L:   platform-driver-x86@vger.kernel.org
>>> +S:   Maintained
>>> +F:   drivers/platform/x86/ayaneo-ec.c
>>> +
>>>    AZ6007 DVB DRIVER
>>>    M:  Mauro Carvalho Chehab <mchehab@kernel.org>
>>>    L:  linux-media@vger.kernel.org
>>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>>> index 46e62feeda3c..ebe7d2ab8758 100644
>>> --- a/drivers/platform/x86/Kconfig
>>> +++ b/drivers/platform/x86/Kconfig
>>> @@ -316,6 +316,15 @@ config ASUS_TF103C_DOCK
>>>          If you have an Asus TF103C tablet say Y or M here, for a generic x86
>>>          distro config say M here.
>>>
>>> +config AYANEO_EC
>>> +     tristate "Ayaneo EC platform control"
>>> +     help
>>> +       Enables support for the platform EC of Ayaneo devices. This
>>> +       includes fan control, fan speed, charge limit, magic
>>> +       module detection, and controller power control.
>>> +
>>> +       If you have an Ayaneo device, say Y or M here.
>>> +
>>>    config MERAKI_MX100
>>>        tristate "Cisco Meraki MX100 Platform Driver"
>>>        depends on GPIOLIB
>>> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
>>> index c7db2a88c11a..274a685eb92d 100644
>>> --- a/drivers/platform/x86/Makefile
>>> +++ b/drivers/platform/x86/Makefile
>>> @@ -39,6 +39,9 @@ obj-$(CONFIG_ASUS_TF103C_DOCK)      += asus-tf103c-dock.o
>>>    obj-$(CONFIG_EEEPC_LAPTOP)  += eeepc-laptop.o
>>>    obj-$(CONFIG_EEEPC_WMI)             += eeepc-wmi.o
>>>
>>> +# Ayaneo
>>> +obj-$(CONFIG_AYANEO_EC)              += ayaneo-ec.o
>>> +
>>>    # Cisco/Meraki
>>>    obj-$(CONFIG_MERAKI_MX100)  += meraki-mx100.o
>>>
>>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
>>> new file mode 100644
>>> index 000000000000..2fe66c8a89f4
>>> --- /dev/null
>>> +++ b/drivers/platform/x86/ayaneo-ec.c
>>> @@ -0,0 +1,90 @@
>>> +// SPDX-License-Identifier: GPL-2.0+
>>> +/*
>>> + * Platform driver for the Embedded Controller (EC) of Ayaneo devices. Handles
>>> + * hwmon (fan speed, fan control), battery charge limits, and magic module
>>> + * control (connected modules, controller disconnection).
>>> + *
>>> + * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
>>> + */
>>> +
>>> +#include <linux/dmi.h>
>>> +#include <linux/err.h>
>>> +#include <linux/init.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/module.h>
>>> +#include <linux/platform_device.h>
>>> +
>>> +struct ayaneo_ec_quirk {
>>> +};
>>> +
>>> +struct ayaneo_ec_platform_data {
>>> +     struct platform_device *pdev;
>>> +     struct ayaneo_ec_quirk *quirks;
>>> +};
>>> +
>>> +static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
>>> +};
>>> +
>>> +static const struct dmi_system_id dmi_table[] = {
>>> +     {
>>> +             .matches = {
>>> +                     DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
>>> +                     DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 3"),
>>> +             },
>>> +             .driver_data = (void *)&quirk_ayaneo3,
>>> +     },
>>> +     {},
>>> +};
>> MODULE_DEVICE_TABLE() is missing, please add it so that the driver will
>> automatically load on supported devices.
> It is not. See below.

I see, my bad.

>>> +
>>> +static int ayaneo_ec_probe(struct platform_device *pdev)
>>> +{
>>> +     const struct dmi_system_id *dmi_entry;
>>> +     struct ayaneo_ec_platform_data *data;
>>> +
>>> +     dmi_entry = dmi_first_match(dmi_table);
>>> +     if (!dmi_entry)
>>> +             return -ENODEV;
>> Please store the quirk inside a global variable and perform the DMI match
>> inside ayaneo_ec_init. This will allow you to mark the DMI table as __initconst.
> This driver was designed to not need a global quirk variable, which
> required a lot of additional work (the drvdata structure) . I am not
> re-adding it as a workaround for __initconst.
>
> Adding init to probe emits a warning because there is a dangling
> reference to it.

Ok.

Thanks,
Armin Wolf

>> Thanks,
>> Armin Wolf
>>
>>> +
>>> +     data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
>>> +     if (!data)
>>> +             return -ENOMEM;
>>> +
>>> +     data->pdev = pdev;
>>> +     data->quirks = dmi_entry->driver_data;
>>> +     platform_set_drvdata(pdev, data);
>>> +
>>> +     return 0;
>>> +}
>>> +
>>> +static struct platform_driver ayaneo_platform_driver = {
>>> +     .driver = {
>>> +             .name = "ayaneo-ec",
>>> +     },
>>> +     .probe = ayaneo_ec_probe,
>>> +};
>>> +
>>> +static struct platform_device *ayaneo_platform_device;
>>> +
>>> +static int __init ayaneo_ec_init(void)
>>> +{
>>> +     ayaneo_platform_device =
>>> +             platform_create_bundle(&ayaneo_platform_driver,
>>> +                                    ayaneo_ec_probe, NULL, 0, NULL, 0);
>>> +
>>> +     return PTR_ERR_OR_ZERO(ayaneo_platform_device);
>>> +}
>>> +
>>> +static void __exit ayaneo_ec_exit(void)
>>> +{
>>> +     platform_device_unregister(ayaneo_platform_device);
>>> +     platform_driver_unregister(&ayaneo_platform_driver);
>>> +}
>>> +
>>> +MODULE_DEVICE_TABLE(dmi, dmi_table);
> Here
>
>>> +
>>> +module_init(ayaneo_ec_init);
>>> +module_exit(ayaneo_ec_exit);
>>> +
>>> +MODULE_AUTHOR("Antheas Kapenekakis <lkml@antheas.dev>");
>>> +MODULE_DESCRIPTION("Ayaneo Embedded Controller (EC) platform features");
>>> +MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 3/6] platform/x86: ayaneo-ec: Add charge control support
  2025-11-02 18:37     ` Antheas Kapenekakis
@ 2025-11-04 20:00       ` Armin Wolf
  0 siblings, 0 replies; 30+ messages in thread
From: Armin Wolf @ 2025-11-04 20:00 UTC (permalink / raw)
  To: Antheas Kapenekakis
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

Am 02.11.25 um 19:37 schrieb Antheas Kapenekakis:

> On Sun, 2 Nov 2025 at 19:26, Armin Wolf <W_Armin@gmx.de> wrote:
>> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
>>
>>> Ayaneo devices support charge inhibition via the EC. This inhibition
>>> only works while the device is powered on, and resets between restarts.
>>> However, it is maintained across suspend/resume cycles.
>>>
>>> The EC does not support charge threshold control. Instead, userspace
>>> software on Windows manually toggles charge inhibition depending on
>>> battery level.
>>>
>>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
>>> ---
>>>    drivers/platform/x86/Kconfig     |   1 +
>>>    drivers/platform/x86/ayaneo-ec.c | 112 +++++++++++++++++++++++++++++++
>>>    2 files changed, 113 insertions(+)
>>>
>>> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
>>> index b3beaff4b03a..a45449ae83f8 100644
>>> --- a/drivers/platform/x86/Kconfig
>>> +++ b/drivers/platform/x86/Kconfig
>>> @@ -319,6 +319,7 @@ config ASUS_TF103C_DOCK
>>>    config AYANEO_EC
>>>        tristate "Ayaneo EC platform control"
>>>        depends on ACPI_EC
>>> +     depends on ACPI_BATTERY
>>>        depends on HWMON
>>>        help
>>>          Enables support for the platform EC of Ayaneo devices. This
>>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
>>> index 108a23458a4f..697bb053a7d6 100644
>>> --- a/drivers/platform/x86/ayaneo-ec.c
>>> +++ b/drivers/platform/x86/ayaneo-ec.c
>>> @@ -15,6 +15,8 @@
>>>    #include <linux/kernel.h>
>>>    #include <linux/module.h>
>>>    #include <linux/platform_device.h>
>>> +#include <linux/power_supply.h>
>>> +#include <acpi/battery.h>
>>>
>>>    #define AYANEO_PWM_ENABLE_REG        0x4A
>>>    #define AYANEO_PWM_REG               0x4B
>>> @@ -23,17 +25,27 @@
>>>
>>>    #define AYANEO_FAN_REG               0x76
>>>
>>> +#define EC_CHARGE_CONTROL_BEHAVIOURS                         \
>>> +     (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |           \
>>> +      BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE))
>> I think POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE_AWAKE would be more suitable here.
> Charge inhibition works during sleep.

I must have suffered from a misunderstanding then. In this case:

Reviewed-by: Armin Wolf <W_Armin@gmx.de>

>> Other than that:
>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>>
>>> +#define AYANEO_CHARGE_REG            0x1e
>>> +#define AYANEO_CHARGE_VAL_AUTO               0xaa
>>> +#define AYANEO_CHARGE_VAL_INHIBIT    0x55
>>> +
>>>    struct ayaneo_ec_quirk {
>>>        bool has_fan_control;
>>> +     bool has_charge_control;
>>>    };
>>>
>>>    struct ayaneo_ec_platform_data {
>>>        struct platform_device *pdev;
>>>        struct ayaneo_ec_quirk *quirks;
>>> +     struct acpi_battery_hook battery_hook;
>>>    };
>>>
>>>    static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
>>>        .has_fan_control = true,
>>> +     .has_charge_control = true,
>>>    };
>>>
>>>    static const struct dmi_system_id dmi_table[] = {
>>> @@ -164,11 +176,102 @@ static const struct hwmon_chip_info ayaneo_ec_chip_info = {
>>>        .info = ayaneo_ec_sensors,
>>>    };
>>>
>>> +static int ayaneo_psy_ext_get_prop(struct power_supply *psy,
>>> +                                const struct power_supply_ext *ext,
>>> +                                void *data,
>>> +                                enum power_supply_property psp,
>>> +                                union power_supply_propval *val)
>>> +{
>>> +     int ret;
>>> +     u8 tmp;
>>> +
>>> +     switch (psp) {
>>> +     case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
>>> +             ret = ec_read(AYANEO_CHARGE_REG, &tmp);
>>> +             if (ret)
>>> +                     return ret;
>>> +
>>> +             if (tmp == AYANEO_CHARGE_VAL_INHIBIT)
>>> +                     val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
>>> +             else
>>> +                     val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
>>> +             return 0;
>>> +     default:
>>> +             return -EINVAL;
>>> +     }
>>> +}
>>> +
>>> +static int ayaneo_psy_ext_set_prop(struct power_supply *psy,
>>> +                                const struct power_supply_ext *ext,
>>> +                                void *data,
>>> +                                enum power_supply_property psp,
>>> +                                const union power_supply_propval *val)
>>> +{
>>> +     u8 raw_val;
>>> +
>>> +     switch (psp) {
>>> +     case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
>>> +             switch (val->intval) {
>>> +             case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
>>> +                     raw_val = AYANEO_CHARGE_VAL_AUTO;
>>> +                     break;
>>> +             case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
>>> +                     raw_val = AYANEO_CHARGE_VAL_INHIBIT;
>>> +                     break;
>>> +             default:
>>> +                     return -EINVAL;
>>> +             }
>>> +             return ec_write(AYANEO_CHARGE_REG, raw_val);
>>> +     default:
>>> +             return -EINVAL;
>>> +     }
>>> +}
>>> +
>>> +static int ayaneo_psy_prop_is_writeable(struct power_supply *psy,
>>> +                                     const struct power_supply_ext *ext,
>>> +                                     void *data,
>>> +                                     enum power_supply_property psp)
>>> +{
>>> +     return true;
>>> +}
>>> +
>>> +static const enum power_supply_property ayaneo_psy_ext_props[] = {
>>> +     POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
>>> +};
>>> +
>>> +static const struct power_supply_ext ayaneo_psy_ext = {
>>> +     .name                   = "ayaneo-charge-control",
>>> +     .properties             = ayaneo_psy_ext_props,
>>> +     .num_properties         = ARRAY_SIZE(ayaneo_psy_ext_props),
>>> +     .charge_behaviours      = EC_CHARGE_CONTROL_BEHAVIOURS,
>>> +     .get_property           = ayaneo_psy_ext_get_prop,
>>> +     .set_property           = ayaneo_psy_ext_set_prop,
>>> +     .property_is_writeable  = ayaneo_psy_prop_is_writeable,
>>> +};
>>> +
>>> +static int ayaneo_add_battery(struct power_supply *battery,
>>> +                           struct acpi_battery_hook *hook)
>>> +{
>>> +     struct ayaneo_ec_platform_data *data =
>>> +             container_of(hook, struct ayaneo_ec_platform_data, battery_hook);
>>> +
>>> +     return power_supply_register_extension(battery, &ayaneo_psy_ext,
>>> +                                            &data->pdev->dev, NULL);
>>> +}
>>> +
>>> +static int ayaneo_remove_battery(struct power_supply *battery,
>>> +                              struct acpi_battery_hook *hook)
>>> +{
>>> +     power_supply_unregister_extension(battery, &ayaneo_psy_ext);
>>> +     return 0;
>>> +}
>>> +
>>>    static int ayaneo_ec_probe(struct platform_device *pdev)
>>>    {
>>>        const struct dmi_system_id *dmi_entry;
>>>        struct ayaneo_ec_platform_data *data;
>>>        struct device *hwdev;
>>> +     int ret;
>>>
>>>        dmi_entry = dmi_first_match(dmi_table);
>>>        if (!dmi_entry)
>>> @@ -189,6 +292,15 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>>>                        return PTR_ERR(hwdev);
>>>        }
>>>
>>> +     if (data->quirks->has_charge_control) {
>>> +             data->battery_hook.add_battery = ayaneo_add_battery;
>>> +             data->battery_hook.remove_battery = ayaneo_remove_battery;
>>> +             data->battery_hook.name = "Ayaneo Battery";
>>> +             ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
>>> +             if (ret)
>>> +                     return ret;
>>> +     }
>>> +
>>>        return 0;
>>>    }
>>>

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-11-02 18:46     ` Antheas Kapenekakis
@ 2025-11-04 20:03       ` Armin Wolf
  2025-11-05 12:33         ` Antheas Kapenekakis
  0 siblings, 1 reply; 30+ messages in thread
From: Armin Wolf @ 2025-11-04 20:03 UTC (permalink / raw)
  To: Antheas Kapenekakis
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

Am 02.11.25 um 19:46 schrieb Antheas Kapenekakis:

> On Sun, 2 Nov 2025 at 19:30, Armin Wolf <W_Armin@gmx.de> wrote:
>> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
>>
>>> The Ayaneo 3 features hot-swappable controller modules. The ejection
>>> and management is done through HID. However, after ejecting the modules,
>>> the controller needs to be power cycled via the EC to re-initialize.
>>>
>>> For this, the EC provides a variable that holds whether the left or
>>> right modules are connected, and a power control register to turn
>>> the controller on or off. After ejecting the modules, the controller
>>> should be turned off. Then, after both modules are reinserted,
>>> the controller may be powered on again to re-initialize.
>>>
>>> This patch introduces two new sysfs attributes:
>>>    - `controller_modules`: a read-only attribute that indicates whether
>>>      the left and right modules are connected (none, left, right, both).
>>>    - `controller_power`: a read-write attribute that allows the user
>>>      to turn the controller on or off (with '1'/'0').
>>>
>>> Therefore, after ejection is complete, userspace can power off the
>>> controller, then wait until both modules have been reinserted
>>> (`controller_modules` will return 'both') to turn on the controller.
>>>
>>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
>>> ---
>>>    .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 ++++
>>>    MAINTAINERS                                   |   1 +
>>>    drivers/platform/x86/ayaneo-ec.c              | 106 ++++++++++++++++++
>>>    3 files changed, 126 insertions(+)
>>>    create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
>>>
>>> diff --git a/Documentation/ABI/testing/sysfs-platform-ayaneo-ec b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
>>> new file mode 100644
>>> index 000000000000..3c9c3580c685
>>> --- /dev/null
>>> +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
>>> @@ -0,0 +1,19 @@
>>> +What:                /sys/devices/platform/ayaneo-ec/controller_power
>>> +Date:                Oct 2025
>> I think you need to update those dates.
>>
>>> +KernelVersion:       6.19
>>> +Contact:     "Antheas Kapenekakis" <lkml@antheas.dev>
>>> +Description:
>>> +             Current controller power state. Allows turning on and off
>>> +             the controller power (e.g. for power savings). Write 1 to
>>> +             turn on, 0 to turn off. File is readable and writable.
>>> +
>>> +What:                /sys/devices/platform/ayaneo-ec/controller_modules
>>> +Date:                Oct 2025
>>> +KernelVersion:       6.19
>>> +Contact:     "Antheas Kapenekakis"  <lkml@antheas.dev>
>>> +Description:
>>> +             Shows which controller modules are currently connected to
>>> +             the device. Possible values are "left", "right" and "both".
>>> +             File is read-only. The Windows software for this device
>>> +             will only set controller power to 1 if both module sides
>>> +             are connected (i.e. this file returns "both").
>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>> index da9498d8cc89..b4d62ea9a926 100644
>>> --- a/MAINTAINERS
>>> +++ b/MAINTAINERS
>>> @@ -4191,6 +4191,7 @@ AYANEO PLATFORM EC DRIVER
>>>    M:  Antheas Kapenekakis <lkml@antheas.dev>
>>>    L:  platform-driver-x86@vger.kernel.org
>>>    S:  Maintained
>>> +F:   Documentation/ABI/testing/sysfs-platform-ayaneo
>>>    F:  drivers/platform/x86/ayaneo-ec.c
>>>
>>>    AZ6007 DVB DRIVER
>>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
>>> index 697bb053a7d6..0652c044ad76 100644
>>> --- a/drivers/platform/x86/ayaneo-ec.c
>>> +++ b/drivers/platform/x86/ayaneo-ec.c
>>> @@ -8,6 +8,7 @@
>>>     */
>>>
>>>    #include <linux/acpi.h>
>>> +#include <linux/bits.h>
>>>    #include <linux/dmi.h>
>>>    #include <linux/err.h>
>>>    #include <linux/hwmon.h>
>>> @@ -16,6 +17,7 @@
>>>    #include <linux/module.h>
>>>    #include <linux/platform_device.h>
>>>    #include <linux/power_supply.h>
>>> +#include <linux/sysfs.h>
>>>    #include <acpi/battery.h>
>>>
>>>    #define AYANEO_PWM_ENABLE_REG        0x4A
>>> @@ -32,9 +34,17 @@
>>>    #define AYANEO_CHARGE_VAL_AUTO              0xaa
>>>    #define AYANEO_CHARGE_VAL_INHIBIT   0x55
>>>
>>> +#define AYANEO_POWER_REG     0x2d
>>> +#define AYANEO_POWER_OFF     0xfe
>>> +#define AYANEO_POWER_ON              0xff
>>> +#define AYANEO_MODULE_REG    0x2f
>>> +#define AYANEO_MODULE_LEFT   BIT(0)
>>> +#define AYANEO_MODULE_RIGHT  BIT(1)
>> Using GENMASK() would make sense here.
> Only a single bit is being used though? GENMASK is used for a contiguous series?

I was thinking of using GENMASK() for both bits:

#define AYANEO_MODULE_MASK	GENMASK(1, 0)

You can then retrieve both bits using FIELD_GET() and simply use a switch statement
together with an enum in controller_modules_show().

Thanks,
Armin Wolf

>> With those issues being fixed:
>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>>
>>> +
>>>    struct ayaneo_ec_quirk {
>>>        bool has_fan_control;
>>>        bool has_charge_control;
>>> +     bool has_magic_modules;
>>>    };
>>>
>>>    struct ayaneo_ec_platform_data {
>>> @@ -46,6 +56,7 @@ struct ayaneo_ec_platform_data {
>>>    static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
>>>        .has_fan_control = true,
>>>        .has_charge_control = true,
>>> +     .has_magic_modules = true,
>>>    };
>>>
>>>    static const struct dmi_system_id dmi_table[] = {
>>> @@ -266,6 +277,100 @@ static int ayaneo_remove_battery(struct power_supply *battery,
>>>        return 0;
>>>    }
>>>
>>> +static ssize_t controller_power_store(struct device *dev,
>>> +                                   struct device_attribute *attr,
>>> +                                   const char *buf,
>>> +                                   size_t count)
>>> +{
>>> +     bool value;
>>> +     int ret;
>>> +
>>> +     ret = kstrtobool(buf, &value);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     return count;
>>> +}
>>> +
>>> +static ssize_t controller_power_show(struct device *dev,
>>> +                                  struct device_attribute *attr,
>>> +                                  char *buf)
>>> +{
>>> +     int ret;
>>> +     u8 val;
>>> +
>>> +     ret = ec_read(AYANEO_POWER_REG, &val);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
>>> +}
>>> +
>>> +static DEVICE_ATTR_RW(controller_power);
>>> +
>>> +static ssize_t controller_modules_show(struct device *dev,
>>> +                                    struct device_attribute *attr, char *buf)
>>> +{
>>> +     char *out;
>>> +     int ret;
>>> +     u8 val;
>>> +
>>> +     ret = ec_read(AYANEO_MODULE_REG, &val);
>>> +     if (ret)
>>> +             return ret;
>>> +
>>> +     switch (~val & (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)) {
>>> +     case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
>>> +             out = "both";
>>> +             break;
>>> +     case AYANEO_MODULE_LEFT:
>>> +             out = "left";
>>> +             break;
>>> +     case AYANEO_MODULE_RIGHT:
>>> +             out = "right";
>>> +             break;
>>> +     default:
>>> +             out = "none";
>>> +             break;
>>> +     }
>>> +
>>> +     return sysfs_emit(buf, "%s\n", out);
>>> +}
>>> +
>>> +static DEVICE_ATTR_RO(controller_modules);
>>> +
>>> +static struct attribute *aya_mm_attrs[] = {
>>> +     &dev_attr_controller_power.attr,
>>> +     &dev_attr_controller_modules.attr,
>>> +     NULL
>>> +};
>>> +
>>> +static umode_t aya_mm_is_visible(struct kobject *kobj,
>>> +                              struct attribute *attr, int n)
>>> +{
>>> +     struct device *dev = kobj_to_dev(kobj);
>>> +     struct platform_device *pdev = to_platform_device(dev);
>>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
>>> +
>>> +     if (data->quirks->has_magic_modules)
>>> +             return attr->mode;
>>> +     return 0;
>>> +}
>>> +
>>> +static const struct attribute_group aya_mm_attribute_group = {
>>> +     .is_visible = aya_mm_is_visible,
>>> +     .attrs = aya_mm_attrs,
>>> +};
>>> +
>>> +static const struct attribute_group *ayaneo_ec_groups[] = {
>>> +     &aya_mm_attribute_group,
>>> +     NULL
>>> +};
>>> +
>>>    static int ayaneo_ec_probe(struct platform_device *pdev)
>>>    {
>>>        const struct dmi_system_id *dmi_entry;
>>> @@ -307,6 +412,7 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>>>    static struct platform_driver ayaneo_platform_driver = {
>>>        .driver = {
>>>                .name = "ayaneo-ec",
>>> +             .dev_groups = ayaneo_ec_groups,
>>>        },
>>>        .probe = ayaneo_ec_probe,
>>>    };

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-11-04 20:03       ` Armin Wolf
@ 2025-11-05 12:33         ` Antheas Kapenekakis
  2025-11-05 13:24           ` Ilpo Järvinen
  0 siblings, 1 reply; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-11-05 12:33 UTC (permalink / raw)
  To: Armin Wolf
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

On Tue, 4 Nov 2025 at 21:04, Armin Wolf <W_Armin@gmx.de> wrote:
>
> Am 02.11.25 um 19:46 schrieb Antheas Kapenekakis:
>
> > On Sun, 2 Nov 2025 at 19:30, Armin Wolf <W_Armin@gmx.de> wrote:
> >> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
> >>
> >>> The Ayaneo 3 features hot-swappable controller modules. The ejection
> >>> and management is done through HID. However, after ejecting the modules,
> >>> the controller needs to be power cycled via the EC to re-initialize.
> >>>
> >>> For this, the EC provides a variable that holds whether the left or
> >>> right modules are connected, and a power control register to turn
> >>> the controller on or off. After ejecting the modules, the controller
> >>> should be turned off. Then, after both modules are reinserted,
> >>> the controller may be powered on again to re-initialize.
> >>>
> >>> This patch introduces two new sysfs attributes:
> >>>    - `controller_modules`: a read-only attribute that indicates whether
> >>>      the left and right modules are connected (none, left, right, both).
> >>>    - `controller_power`: a read-write attribute that allows the user
> >>>      to turn the controller on or off (with '1'/'0').
> >>>
> >>> Therefore, after ejection is complete, userspace can power off the
> >>> controller, then wait until both modules have been reinserted
> >>> (`controller_modules` will return 'both') to turn on the controller.
> >>>
> >>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> >>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> >>> ---
> >>>    .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 ++++
> >>>    MAINTAINERS                                   |   1 +
> >>>    drivers/platform/x86/ayaneo-ec.c              | 106 ++++++++++++++++++
> >>>    3 files changed, 126 insertions(+)
> >>>    create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> >>>
> >>> diff --git a/Documentation/ABI/testing/sysfs-platform-ayaneo-ec b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> >>> new file mode 100644
> >>> index 000000000000..3c9c3580c685
> >>> --- /dev/null
> >>> +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> >>> @@ -0,0 +1,19 @@
> >>> +What:                /sys/devices/platform/ayaneo-ec/controller_power
> >>> +Date:                Oct 2025
> >> I think you need to update those dates.
> >>
> >>> +KernelVersion:       6.19
> >>> +Contact:     "Antheas Kapenekakis" <lkml@antheas.dev>
> >>> +Description:
> >>> +             Current controller power state. Allows turning on and off
> >>> +             the controller power (e.g. for power savings). Write 1 to
> >>> +             turn on, 0 to turn off. File is readable and writable.
> >>> +
> >>> +What:                /sys/devices/platform/ayaneo-ec/controller_modules
> >>> +Date:                Oct 2025
> >>> +KernelVersion:       6.19
> >>> +Contact:     "Antheas Kapenekakis"  <lkml@antheas.dev>
> >>> +Description:
> >>> +             Shows which controller modules are currently connected to
> >>> +             the device. Possible values are "left", "right" and "both".
> >>> +             File is read-only. The Windows software for this device
> >>> +             will only set controller power to 1 if both module sides
> >>> +             are connected (i.e. this file returns "both").
> >>> diff --git a/MAINTAINERS b/MAINTAINERS
> >>> index da9498d8cc89..b4d62ea9a926 100644
> >>> --- a/MAINTAINERS
> >>> +++ b/MAINTAINERS
> >>> @@ -4191,6 +4191,7 @@ AYANEO PLATFORM EC DRIVER
> >>>    M:  Antheas Kapenekakis <lkml@antheas.dev>
> >>>    L:  platform-driver-x86@vger.kernel.org
> >>>    S:  Maintained
> >>> +F:   Documentation/ABI/testing/sysfs-platform-ayaneo
> >>>    F:  drivers/platform/x86/ayaneo-ec.c
> >>>
> >>>    AZ6007 DVB DRIVER
> >>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> >>> index 697bb053a7d6..0652c044ad76 100644
> >>> --- a/drivers/platform/x86/ayaneo-ec.c
> >>> +++ b/drivers/platform/x86/ayaneo-ec.c
> >>> @@ -8,6 +8,7 @@
> >>>     */
> >>>
> >>>    #include <linux/acpi.h>
> >>> +#include <linux/bits.h>
> >>>    #include <linux/dmi.h>
> >>>    #include <linux/err.h>
> >>>    #include <linux/hwmon.h>
> >>> @@ -16,6 +17,7 @@
> >>>    #include <linux/module.h>
> >>>    #include <linux/platform_device.h>
> >>>    #include <linux/power_supply.h>
> >>> +#include <linux/sysfs.h>
> >>>    #include <acpi/battery.h>
> >>>
> >>>    #define AYANEO_PWM_ENABLE_REG        0x4A
> >>> @@ -32,9 +34,17 @@
> >>>    #define AYANEO_CHARGE_VAL_AUTO              0xaa
> >>>    #define AYANEO_CHARGE_VAL_INHIBIT   0x55
> >>>
> >>> +#define AYANEO_POWER_REG     0x2d
> >>> +#define AYANEO_POWER_OFF     0xfe
> >>> +#define AYANEO_POWER_ON              0xff
> >>> +#define AYANEO_MODULE_REG    0x2f
> >>> +#define AYANEO_MODULE_LEFT   BIT(0)
> >>> +#define AYANEO_MODULE_RIGHT  BIT(1)
> >> Using GENMASK() would make sense here.
> > Only a single bit is being used though? GENMASK is used for a contiguous series?
>
> I was thinking of using GENMASK() for both bits:
>
> #define AYANEO_MODULE_MASK      GENMASK(1, 0)
>
> You can then retrieve both bits using FIELD_GET() and simply use a switch statement
> together with an enum in controller_modules_show().

I will look at it closer if I revise the first 5 patches. The logic of
the register is complicated due to the flip, so I would not be eager
to.

I could do #define AYANEO_MODULE_MASK (AYANEO_MODULE_LEFT |
AYANEO_MODULE_RIGHT) for stylistic reasons.

@Ilpo: for the first 5 of this series is there something missing other
than perhaps the month? If not, I can respin the last patch on its own
later today. I would like this driver + the asus stuff to go into 6.19
if possible (there: i dealt with the asusctl bug by skipping the
quirk), I am unsure for the timeline for that. I want to say merge
window starts in two weeks?

Antheas

> Thanks,
> Armin Wolf
>
> >> With those issues being fixed:
> >> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> >>
> >>> +
> >>>    struct ayaneo_ec_quirk {
> >>>        bool has_fan_control;
> >>>        bool has_charge_control;
> >>> +     bool has_magic_modules;
> >>>    };
> >>>
> >>>    struct ayaneo_ec_platform_data {
> >>> @@ -46,6 +56,7 @@ struct ayaneo_ec_platform_data {
> >>>    static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
> >>>        .has_fan_control = true,
> >>>        .has_charge_control = true,
> >>> +     .has_magic_modules = true,
> >>>    };
> >>>
> >>>    static const struct dmi_system_id dmi_table[] = {
> >>> @@ -266,6 +277,100 @@ static int ayaneo_remove_battery(struct power_supply *battery,
> >>>        return 0;
> >>>    }
> >>>
> >>> +static ssize_t controller_power_store(struct device *dev,
> >>> +                                   struct device_attribute *attr,
> >>> +                                   const char *buf,
> >>> +                                   size_t count)
> >>> +{
> >>> +     bool value;
> >>> +     int ret;
> >>> +
> >>> +     ret = kstrtobool(buf, &value);
> >>> +     if (ret)
> >>> +             return ret;
> >>> +
> >>> +     ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
> >>> +     if (ret)
> >>> +             return ret;
> >>> +
> >>> +     return count;
> >>> +}
> >>> +
> >>> +static ssize_t controller_power_show(struct device *dev,
> >>> +                                  struct device_attribute *attr,
> >>> +                                  char *buf)
> >>> +{
> >>> +     int ret;
> >>> +     u8 val;
> >>> +
> >>> +     ret = ec_read(AYANEO_POWER_REG, &val);
> >>> +     if (ret)
> >>> +             return ret;
> >>> +
> >>> +     return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
> >>> +}
> >>> +
> >>> +static DEVICE_ATTR_RW(controller_power);
> >>> +
> >>> +static ssize_t controller_modules_show(struct device *dev,
> >>> +                                    struct device_attribute *attr, char *buf)
> >>> +{
> >>> +     char *out;
> >>> +     int ret;
> >>> +     u8 val;
> >>> +
> >>> +     ret = ec_read(AYANEO_MODULE_REG, &val);
> >>> +     if (ret)
> >>> +             return ret;
> >>> +
> >>> +     switch (~val & (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)) {
> >>> +     case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
> >>> +             out = "both";
> >>> +             break;
> >>> +     case AYANEO_MODULE_LEFT:
> >>> +             out = "left";
> >>> +             break;
> >>> +     case AYANEO_MODULE_RIGHT:
> >>> +             out = "right";
> >>> +             break;
> >>> +     default:
> >>> +             out = "none";
> >>> +             break;
> >>> +     }
> >>> +
> >>> +     return sysfs_emit(buf, "%s\n", out);
> >>> +}
> >>> +
> >>> +static DEVICE_ATTR_RO(controller_modules);
> >>> +
> >>> +static struct attribute *aya_mm_attrs[] = {
> >>> +     &dev_attr_controller_power.attr,
> >>> +     &dev_attr_controller_modules.attr,
> >>> +     NULL
> >>> +};
> >>> +
> >>> +static umode_t aya_mm_is_visible(struct kobject *kobj,
> >>> +                              struct attribute *attr, int n)
> >>> +{
> >>> +     struct device *dev = kobj_to_dev(kobj);
> >>> +     struct platform_device *pdev = to_platform_device(dev);
> >>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> >>> +
> >>> +     if (data->quirks->has_magic_modules)
> >>> +             return attr->mode;
> >>> +     return 0;
> >>> +}
> >>> +
> >>> +static const struct attribute_group aya_mm_attribute_group = {
> >>> +     .is_visible = aya_mm_is_visible,
> >>> +     .attrs = aya_mm_attrs,
> >>> +};
> >>> +
> >>> +static const struct attribute_group *ayaneo_ec_groups[] = {
> >>> +     &aya_mm_attribute_group,
> >>> +     NULL
> >>> +};
> >>> +
> >>>    static int ayaneo_ec_probe(struct platform_device *pdev)
> >>>    {
> >>>        const struct dmi_system_id *dmi_entry;
> >>> @@ -307,6 +412,7 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >>>    static struct platform_driver ayaneo_platform_driver = {
> >>>        .driver = {
> >>>                .name = "ayaneo-ec",
> >>> +             .dev_groups = ayaneo_ec_groups,
> >>>        },
> >>>        .probe = ayaneo_ec_probe,
> >>>    };
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-11-05 12:33         ` Antheas Kapenekakis
@ 2025-11-05 13:24           ` Ilpo Järvinen
  2025-11-05 13:38             ` Antheas Kapenekakis
  2025-11-05 22:20             ` Armin Wolf
  0 siblings, 2 replies; 30+ messages in thread
From: Ilpo Järvinen @ 2025-11-05 13:24 UTC (permalink / raw)
  To: Antheas Kapenekakis
  Cc: Armin Wolf, platform-driver-x86, LKML, linux-hwmon, Hans de Goede,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

On Wed, 5 Nov 2025, Antheas Kapenekakis wrote:

> On Tue, 4 Nov 2025 at 21:04, Armin Wolf <W_Armin@gmx.de> wrote:
> >
> > Am 02.11.25 um 19:46 schrieb Antheas Kapenekakis:
> >
> > > On Sun, 2 Nov 2025 at 19:30, Armin Wolf <W_Armin@gmx.de> wrote:
> > >> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
> > >>
> > >>> The Ayaneo 3 features hot-swappable controller modules. The ejection
> > >>> and management is done through HID. However, after ejecting the modules,
> > >>> the controller needs to be power cycled via the EC to re-initialize.
> > >>>
> > >>> For this, the EC provides a variable that holds whether the left or
> > >>> right modules are connected, and a power control register to turn
> > >>> the controller on or off. After ejecting the modules, the controller
> > >>> should be turned off. Then, after both modules are reinserted,
> > >>> the controller may be powered on again to re-initialize.
> > >>>
> > >>> This patch introduces two new sysfs attributes:
> > >>>    - `controller_modules`: a read-only attribute that indicates whether
> > >>>      the left and right modules are connected (none, left, right, both).
> > >>>    - `controller_power`: a read-write attribute that allows the user
> > >>>      to turn the controller on or off (with '1'/'0').
> > >>>
> > >>> Therefore, after ejection is complete, userspace can power off the
> > >>> controller, then wait until both modules have been reinserted
> > >>> (`controller_modules` will return 'both') to turn on the controller.
> > >>>
> > >>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> > >>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > >>> ---
> > >>>    .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 ++++
> > >>>    MAINTAINERS                                   |   1 +
> > >>>    drivers/platform/x86/ayaneo-ec.c              | 106 ++++++++++++++++++
> > >>>    3 files changed, 126 insertions(+)
> > >>>    create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > >>>
> > >>> diff --git a/Documentation/ABI/testing/sysfs-platform-ayaneo-ec b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > >>> new file mode 100644
> > >>> index 000000000000..3c9c3580c685
> > >>> --- /dev/null
> > >>> +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > >>> @@ -0,0 +1,19 @@
> > >>> +What:                /sys/devices/platform/ayaneo-ec/controller_power
> > >>> +Date:                Oct 2025
> > >> I think you need to update those dates.
> > >>
> > >>> +KernelVersion:       6.19
> > >>> +Contact:     "Antheas Kapenekakis" <lkml@antheas.dev>
> > >>> +Description:
> > >>> +             Current controller power state. Allows turning on and off
> > >>> +             the controller power (e.g. for power savings). Write 1 to
> > >>> +             turn on, 0 to turn off. File is readable and writable.
> > >>> +
> > >>> +What:                /sys/devices/platform/ayaneo-ec/controller_modules
> > >>> +Date:                Oct 2025
> > >>> +KernelVersion:       6.19
> > >>> +Contact:     "Antheas Kapenekakis"  <lkml@antheas.dev>
> > >>> +Description:
> > >>> +             Shows which controller modules are currently connected to
> > >>> +             the device. Possible values are "left", "right" and "both".
> > >>> +             File is read-only. The Windows software for this device
> > >>> +             will only set controller power to 1 if both module sides
> > >>> +             are connected (i.e. this file returns "both").
> > >>> diff --git a/MAINTAINERS b/MAINTAINERS
> > >>> index da9498d8cc89..b4d62ea9a926 100644
> > >>> --- a/MAINTAINERS
> > >>> +++ b/MAINTAINERS
> > >>> @@ -4191,6 +4191,7 @@ AYANEO PLATFORM EC DRIVER
> > >>>    M:  Antheas Kapenekakis <lkml@antheas.dev>
> > >>>    L:  platform-driver-x86@vger.kernel.org
> > >>>    S:  Maintained
> > >>> +F:   Documentation/ABI/testing/sysfs-platform-ayaneo
> > >>>    F:  drivers/platform/x86/ayaneo-ec.c
> > >>>
> > >>>    AZ6007 DVB DRIVER
> > >>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> > >>> index 697bb053a7d6..0652c044ad76 100644
> > >>> --- a/drivers/platform/x86/ayaneo-ec.c
> > >>> +++ b/drivers/platform/x86/ayaneo-ec.c
> > >>> @@ -8,6 +8,7 @@
> > >>>     */
> > >>>
> > >>>    #include <linux/acpi.h>
> > >>> +#include <linux/bits.h>
> > >>>    #include <linux/dmi.h>
> > >>>    #include <linux/err.h>
> > >>>    #include <linux/hwmon.h>
> > >>> @@ -16,6 +17,7 @@
> > >>>    #include <linux/module.h>
> > >>>    #include <linux/platform_device.h>
> > >>>    #include <linux/power_supply.h>
> > >>> +#include <linux/sysfs.h>
> > >>>    #include <acpi/battery.h>
> > >>>
> > >>>    #define AYANEO_PWM_ENABLE_REG        0x4A
> > >>> @@ -32,9 +34,17 @@
> > >>>    #define AYANEO_CHARGE_VAL_AUTO              0xaa
> > >>>    #define AYANEO_CHARGE_VAL_INHIBIT   0x55
> > >>>
> > >>> +#define AYANEO_POWER_REG     0x2d
> > >>> +#define AYANEO_POWER_OFF     0xfe
> > >>> +#define AYANEO_POWER_ON              0xff
> > >>> +#define AYANEO_MODULE_REG    0x2f
> > >>> +#define AYANEO_MODULE_LEFT   BIT(0)
> > >>> +#define AYANEO_MODULE_RIGHT  BIT(1)
> > >> Using GENMASK() would make sense here.
> > > Only a single bit is being used though? GENMASK is used for a contiguous series?
> >
> > I was thinking of using GENMASK() for both bits:
> >
> > #define AYANEO_MODULE_MASK      GENMASK(1, 0)
> >
> > You can then retrieve both bits using FIELD_GET() and simply use a switch statement
> > together with an enum in controller_modules_show().
> 
> I will look at it closer if I revise the first 5 patches. The logic of
> the register is complicated due to the flip, so I would not be eager
> to.
> 
> I could do #define AYANEO_MODULE_MASK (AYANEO_MODULE_LEFT |
> AYANEO_MODULE_RIGHT) for stylistic reasons.

Hi,

Quickly looking at the code below, a mask define certainly be useful to 
avoid having to embed that into the switch itself below.

> @Ilpo: for the first 5 of this series is there something missing other
> than perhaps the month? If not, I can respin the last patch on its own
> later today. I would like this driver + the asus stuff to go into 6.19
> if possible (there: i dealt with the asusctl bug by skipping the
> quirk), I am unsure for the timeline for that. I want to say merge
> window starts in two weeks?

I've been sick for almost a week so I'm a bit lost with all the progress 
and status with various series so it takes time to catch up until can 
say something when it comes to any particular series (I'm sorry).

However, we're only at -rc4 (since Nov 2) so there's 4 or 5 weeks until 
the merge window. And this is new driver so no regression potential other 
than build failures so I don't see big schedule pressure yet, tbh.

I try to take it into the consideration with my review scheduling even 
under normal circumstances that submitters have the opportunity to make it 
assuming one makes suggested changes (if any) in a reasonable time frame.

It generally helps to pay attention to the kernel cycle as closer to the 
merge window we get, it usually helps to prioritize things right, e.g., 
sending a series on the last week or close to it, getting trivial 
feedback, and not addressing it timely with an update will result in 
missing that kernel cycle; honestly, I always get a bit sad when I see 
people do that.


> Antheas
> 
> > Thanks,
> > Armin Wolf
> >
> > >> With those issues being fixed:
> > >> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> > >>
> > >>> +
> > >>>    struct ayaneo_ec_quirk {
> > >>>        bool has_fan_control;
> > >>>        bool has_charge_control;
> > >>> +     bool has_magic_modules;
> > >>>    };
> > >>>
> > >>>    struct ayaneo_ec_platform_data {
> > >>> @@ -46,6 +56,7 @@ struct ayaneo_ec_platform_data {
> > >>>    static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
> > >>>        .has_fan_control = true,
> > >>>        .has_charge_control = true,
> > >>> +     .has_magic_modules = true,
> > >>>    };
> > >>>
> > >>>    static const struct dmi_system_id dmi_table[] = {
> > >>> @@ -266,6 +277,100 @@ static int ayaneo_remove_battery(struct power_supply *battery,
> > >>>        return 0;
> > >>>    }
> > >>>
> > >>> +static ssize_t controller_power_store(struct device *dev,
> > >>> +                                   struct device_attribute *attr,
> > >>> +                                   const char *buf,
> > >>> +                                   size_t count)
> > >>> +{
> > >>> +     bool value;
> > >>> +     int ret;
> > >>> +
> > >>> +     ret = kstrtobool(buf, &value);
> > >>> +     if (ret)
> > >>> +             return ret;
> > >>> +
> > >>> +     ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
> > >>> +     if (ret)
> > >>> +             return ret;
> > >>> +
> > >>> +     return count;
> > >>> +}
> > >>> +
> > >>> +static ssize_t controller_power_show(struct device *dev,
> > >>> +                                  struct device_attribute *attr,
> > >>> +                                  char *buf)
> > >>> +{
> > >>> +     int ret;
> > >>> +     u8 val;
> > >>> +
> > >>> +     ret = ec_read(AYANEO_POWER_REG, &val);
> > >>> +     if (ret)
> > >>> +             return ret;
> > >>> +
> > >>> +     return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
> > >>> +}
> > >>> +
> > >>> +static DEVICE_ATTR_RW(controller_power);
> > >>> +
> > >>> +static ssize_t controller_modules_show(struct device *dev,
> > >>> +                                    struct device_attribute *attr, char *buf)
> > >>> +{
> > >>> +     char *out;
> > >>> +     int ret;
> > >>> +     u8 val;
> > >>> +
> > >>> +     ret = ec_read(AYANEO_MODULE_REG, &val);
> > >>> +     if (ret)
> > >>> +             return ret;
> > >>> +
> > >>> +     switch (~val & (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)) {

Why's the extra inversion here? Do those bits actually have the opposite 
meaning compared with their names?

--
 i.

> > >>> +     case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
> > >>> +             out = "both";
> > >>> +             break;
> > >>> +     case AYANEO_MODULE_LEFT:
> > >>> +             out = "left";
> > >>> +             break;
> > >>> +     case AYANEO_MODULE_RIGHT:
> > >>> +             out = "right";
> > >>> +             break;
> > >>> +     default:
> > >>> +             out = "none";
> > >>> +             break;
> > >>> +     }
> > >>> +
> > >>> +     return sysfs_emit(buf, "%s\n", out);
> > >>> +}
> > >>> +
> > >>> +static DEVICE_ATTR_RO(controller_modules);
> > >>> +
> > >>> +static struct attribute *aya_mm_attrs[] = {
> > >>> +     &dev_attr_controller_power.attr,
> > >>> +     &dev_attr_controller_modules.attr,
> > >>> +     NULL
> > >>> +};
> > >>> +
> > >>> +static umode_t aya_mm_is_visible(struct kobject *kobj,
> > >>> +                              struct attribute *attr, int n)
> > >>> +{
> > >>> +     struct device *dev = kobj_to_dev(kobj);
> > >>> +     struct platform_device *pdev = to_platform_device(dev);
> > >>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > >>> +
> > >>> +     if (data->quirks->has_magic_modules)
> > >>> +             return attr->mode;
> > >>> +     return 0;
> > >>> +}
> > >>> +
> > >>> +static const struct attribute_group aya_mm_attribute_group = {
> > >>> +     .is_visible = aya_mm_is_visible,
> > >>> +     .attrs = aya_mm_attrs,
> > >>> +};
> > >>> +
> > >>> +static const struct attribute_group *ayaneo_ec_groups[] = {
> > >>> +     &aya_mm_attribute_group,
> > >>> +     NULL
> > >>> +};
> > >>> +
> > >>>    static int ayaneo_ec_probe(struct platform_device *pdev)
> > >>>    {
> > >>>        const struct dmi_system_id *dmi_entry;
> > >>> @@ -307,6 +412,7 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> > >>>    static struct platform_driver ayaneo_platform_driver = {
> > >>>        .driver = {
> > >>>                .name = "ayaneo-ec",
> > >>> +             .dev_groups = ayaneo_ec_groups,
> > >>>        },
> > >>>        .probe = ayaneo_ec_probe,
> > >>>    };
> >
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-11-05 13:24           ` Ilpo Järvinen
@ 2025-11-05 13:38             ` Antheas Kapenekakis
  2025-11-05 22:20             ` Armin Wolf
  1 sibling, 0 replies; 30+ messages in thread
From: Antheas Kapenekakis @ 2025-11-05 13:38 UTC (permalink / raw)
  To: Ilpo Järvinen
  Cc: Armin Wolf, platform-driver-x86, LKML, linux-hwmon, Hans de Goede,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

On Wed, 5 Nov 2025 at 14:25, Ilpo Järvinen
<ilpo.jarvinen@linux.intel.com> wrote:
>
> On Wed, 5 Nov 2025, Antheas Kapenekakis wrote:
>
> > On Tue, 4 Nov 2025 at 21:04, Armin Wolf <W_Armin@gmx.de> wrote:
> > >
> > > Am 02.11.25 um 19:46 schrieb Antheas Kapenekakis:
> > >
> > > > On Sun, 2 Nov 2025 at 19:30, Armin Wolf <W_Armin@gmx.de> wrote:
> > > >> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
> > > >>
> > > >>> The Ayaneo 3 features hot-swappable controller modules. The ejection
> > > >>> and management is done through HID. However, after ejecting the modules,
> > > >>> the controller needs to be power cycled via the EC to re-initialize.
> > > >>>
> > > >>> For this, the EC provides a variable that holds whether the left or
> > > >>> right modules are connected, and a power control register to turn
> > > >>> the controller on or off. After ejecting the modules, the controller
> > > >>> should be turned off. Then, after both modules are reinserted,
> > > >>> the controller may be powered on again to re-initialize.
> > > >>>
> > > >>> This patch introduces two new sysfs attributes:
> > > >>>    - `controller_modules`: a read-only attribute that indicates whether
> > > >>>      the left and right modules are connected (none, left, right, both).
> > > >>>    - `controller_power`: a read-write attribute that allows the user
> > > >>>      to turn the controller on or off (with '1'/'0').
> > > >>>
> > > >>> Therefore, after ejection is complete, userspace can power off the
> > > >>> controller, then wait until both modules have been reinserted
> > > >>> (`controller_modules` will return 'both') to turn on the controller.
> > > >>>
> > > >>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> > > >>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > > >>> ---
> > > >>>    .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 ++++
> > > >>>    MAINTAINERS                                   |   1 +
> > > >>>    drivers/platform/x86/ayaneo-ec.c              | 106 ++++++++++++++++++
> > > >>>    3 files changed, 126 insertions(+)
> > > >>>    create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > > >>>
> > > >>> diff --git a/Documentation/ABI/testing/sysfs-platform-ayaneo-ec b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > > >>> new file mode 100644
> > > >>> index 000000000000..3c9c3580c685
> > > >>> --- /dev/null
> > > >>> +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > > >>> @@ -0,0 +1,19 @@
> > > >>> +What:                /sys/devices/platform/ayaneo-ec/controller_power
> > > >>> +Date:                Oct 2025
> > > >> I think you need to update those dates.
> > > >>
> > > >>> +KernelVersion:       6.19
> > > >>> +Contact:     "Antheas Kapenekakis" <lkml@antheas.dev>
> > > >>> +Description:
> > > >>> +             Current controller power state. Allows turning on and off
> > > >>> +             the controller power (e.g. for power savings). Write 1 to
> > > >>> +             turn on, 0 to turn off. File is readable and writable.
> > > >>> +
> > > >>> +What:                /sys/devices/platform/ayaneo-ec/controller_modules
> > > >>> +Date:                Oct 2025
> > > >>> +KernelVersion:       6.19
> > > >>> +Contact:     "Antheas Kapenekakis"  <lkml@antheas.dev>
> > > >>> +Description:
> > > >>> +             Shows which controller modules are currently connected to
> > > >>> +             the device. Possible values are "left", "right" and "both".
> > > >>> +             File is read-only. The Windows software for this device
> > > >>> +             will only set controller power to 1 if both module sides
> > > >>> +             are connected (i.e. this file returns "both").
> > > >>> diff --git a/MAINTAINERS b/MAINTAINERS
> > > >>> index da9498d8cc89..b4d62ea9a926 100644
> > > >>> --- a/MAINTAINERS
> > > >>> +++ b/MAINTAINERS
> > > >>> @@ -4191,6 +4191,7 @@ AYANEO PLATFORM EC DRIVER
> > > >>>    M:  Antheas Kapenekakis <lkml@antheas.dev>
> > > >>>    L:  platform-driver-x86@vger.kernel.org
> > > >>>    S:  Maintained
> > > >>> +F:   Documentation/ABI/testing/sysfs-platform-ayaneo
> > > >>>    F:  drivers/platform/x86/ayaneo-ec.c
> > > >>>
> > > >>>    AZ6007 DVB DRIVER
> > > >>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> > > >>> index 697bb053a7d6..0652c044ad76 100644
> > > >>> --- a/drivers/platform/x86/ayaneo-ec.c
> > > >>> +++ b/drivers/platform/x86/ayaneo-ec.c
> > > >>> @@ -8,6 +8,7 @@
> > > >>>     */
> > > >>>
> > > >>>    #include <linux/acpi.h>
> > > >>> +#include <linux/bits.h>
> > > >>>    #include <linux/dmi.h>
> > > >>>    #include <linux/err.h>
> > > >>>    #include <linux/hwmon.h>
> > > >>> @@ -16,6 +17,7 @@
> > > >>>    #include <linux/module.h>
> > > >>>    #include <linux/platform_device.h>
> > > >>>    #include <linux/power_supply.h>
> > > >>> +#include <linux/sysfs.h>
> > > >>>    #include <acpi/battery.h>
> > > >>>
> > > >>>    #define AYANEO_PWM_ENABLE_REG        0x4A
> > > >>> @@ -32,9 +34,17 @@
> > > >>>    #define AYANEO_CHARGE_VAL_AUTO              0xaa
> > > >>>    #define AYANEO_CHARGE_VAL_INHIBIT   0x55
> > > >>>
> > > >>> +#define AYANEO_POWER_REG     0x2d
> > > >>> +#define AYANEO_POWER_OFF     0xfe
> > > >>> +#define AYANEO_POWER_ON              0xff
> > > >>> +#define AYANEO_MODULE_REG    0x2f
> > > >>> +#define AYANEO_MODULE_LEFT   BIT(0)
> > > >>> +#define AYANEO_MODULE_RIGHT  BIT(1)
> > > >> Using GENMASK() would make sense here.
> > > > Only a single bit is being used though? GENMASK is used for a contiguous series?
> > >
> > > I was thinking of using GENMASK() for both bits:
> > >
> > > #define AYANEO_MODULE_MASK      GENMASK(1, 0)
> > >
> > > You can then retrieve both bits using FIELD_GET() and simply use a switch statement
> > > together with an enum in controller_modules_show().
> >
> > I will look at it closer if I revise the first 5 patches. The logic of
> > the register is complicated due to the flip, so I would not be eager
> > to.
> >
> > I could do #define AYANEO_MODULE_MASK (AYANEO_MODULE_LEFT |
> > AYANEO_MODULE_RIGHT) for stylistic reasons.
>
> Hi,
>
> Quickly looking at the code below, a mask define certainly be useful to
> avoid having to embed that into the switch itself below.
>
> > @Ilpo: for the first 5 of this series is there something missing other
> > than perhaps the month? If not, I can respin the last patch on its own
> > later today. I would like this driver + the asus stuff to go into 6.19
> > if possible (there: i dealt with the asusctl bug by skipping the
> > quirk), I am unsure for the timeline for that. I want to say merge
> > window starts in two weeks?
>
> I've been sick for almost a week so I'm a bit lost with all the progress
> and status with various series so it takes time to catch up until can
> say something when it comes to any particular series (I'm sorry).
>
> However, we're only at -rc4 (since Nov 2) so there's 4 or 5 weeks until
> the merge window. And this is new driver so no regression potential other
> than build failures so I don't see big schedule pressure yet, tbh.
>
> I try to take it into the consideration with my review scheduling even
> under normal circumstances that submitters have the opportunity to make it
> assuming one makes suggested changes (if any) in a reasonable time frame.
>
> It generally helps to pay attention to the kernel cycle as closer to the
> merge window we get, it usually helps to prioritize things right, e.g.,
> sending a series on the last week or close to it, getting trivial
> feedback, and not addressing it timely with an update will result in
> missing that kernel cycle; honestly, I always get a bit sad when I see
> people do that.
>

Good to know, so these are on track.

> > Antheas
> >
> > > Thanks,
> > > Armin Wolf
> > >
> > > >> With those issues being fixed:
> > > >> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
> > > >>
> > > >>> +
> > > >>>    struct ayaneo_ec_quirk {
> > > >>>        bool has_fan_control;
> > > >>>        bool has_charge_control;
> > > >>> +     bool has_magic_modules;
> > > >>>    };
> > > >>>
> > > >>>    struct ayaneo_ec_platform_data {
> > > >>> @@ -46,6 +56,7 @@ struct ayaneo_ec_platform_data {
> > > >>>    static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
> > > >>>        .has_fan_control = true,
> > > >>>        .has_charge_control = true,
> > > >>> +     .has_magic_modules = true,
> > > >>>    };
> > > >>>
> > > >>>    static const struct dmi_system_id dmi_table[] = {
> > > >>> @@ -266,6 +277,100 @@ static int ayaneo_remove_battery(struct power_supply *battery,
> > > >>>        return 0;
> > > >>>    }
> > > >>>
> > > >>> +static ssize_t controller_power_store(struct device *dev,
> > > >>> +                                   struct device_attribute *attr,
> > > >>> +                                   const char *buf,
> > > >>> +                                   size_t count)
> > > >>> +{
> > > >>> +     bool value;
> > > >>> +     int ret;
> > > >>> +
> > > >>> +     ret = kstrtobool(buf, &value);
> > > >>> +     if (ret)
> > > >>> +             return ret;
> > > >>> +
> > > >>> +     ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
> > > >>> +     if (ret)
> > > >>> +             return ret;
> > > >>> +
> > > >>> +     return count;
> > > >>> +}
> > > >>> +
> > > >>> +static ssize_t controller_power_show(struct device *dev,
> > > >>> +                                  struct device_attribute *attr,
> > > >>> +                                  char *buf)
> > > >>> +{
> > > >>> +     int ret;
> > > >>> +     u8 val;
> > > >>> +
> > > >>> +     ret = ec_read(AYANEO_POWER_REG, &val);
> > > >>> +     if (ret)
> > > >>> +             return ret;
> > > >>> +
> > > >>> +     return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
> > > >>> +}
> > > >>> +
> > > >>> +static DEVICE_ATTR_RW(controller_power);
> > > >>> +
> > > >>> +static ssize_t controller_modules_show(struct device *dev,
> > > >>> +                                    struct device_attribute *attr, char *buf)
> > > >>> +{
> > > >>> +     char *out;
> > > >>> +     int ret;
> > > >>> +     u8 val;
> > > >>> +
> > > >>> +     ret = ec_read(AYANEO_MODULE_REG, &val);
> > > >>> +     if (ret)
> > > >>> +             return ret;
> > > >>> +
> > > >>> +     switch (~val & (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)) {
>
> Why's the extra inversion here? Do those bits actually have the opposite
> meaning compared with their names?

The bits are active-low. So the register begins as 0xff, and when the
modules are plugged in, the respective bit turns to 0.

MODULE_LEFT/RIGHT are a bitmask so the defines are correct. They
cannot be flipped the other way.

It was a bit quirky to convert them into a switch statement.

Antheas

> --
>  i.
>
> > > >>> +     case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
> > > >>> +             out = "both";
> > > >>> +             break;
> > > >>> +     case AYANEO_MODULE_LEFT:
> > > >>> +             out = "left";
> > > >>> +             break;
> > > >>> +     case AYANEO_MODULE_RIGHT:
> > > >>> +             out = "right";
> > > >>> +             break;
> > > >>> +     default:
> > > >>> +             out = "none";
> > > >>> +             break;
> > > >>> +     }
> > > >>> +
> > > >>> +     return sysfs_emit(buf, "%s\n", out);
> > > >>> +}
> > > >>> +
> > > >>> +static DEVICE_ATTR_RO(controller_modules);
> > > >>> +
> > > >>> +static struct attribute *aya_mm_attrs[] = {
> > > >>> +     &dev_attr_controller_power.attr,
> > > >>> +     &dev_attr_controller_modules.attr,
> > > >>> +     NULL
> > > >>> +};
> > > >>> +
> > > >>> +static umode_t aya_mm_is_visible(struct kobject *kobj,
> > > >>> +                              struct attribute *attr, int n)
> > > >>> +{
> > > >>> +     struct device *dev = kobj_to_dev(kobj);
> > > >>> +     struct platform_device *pdev = to_platform_device(dev);
> > > >>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > > >>> +
> > > >>> +     if (data->quirks->has_magic_modules)
> > > >>> +             return attr->mode;
> > > >>> +     return 0;
> > > >>> +}
> > > >>> +
> > > >>> +static const struct attribute_group aya_mm_attribute_group = {
> > > >>> +     .is_visible = aya_mm_is_visible,
> > > >>> +     .attrs = aya_mm_attrs,
> > > >>> +};
> > > >>> +
> > > >>> +static const struct attribute_group *ayaneo_ec_groups[] = {
> > > >>> +     &aya_mm_attribute_group,
> > > >>> +     NULL
> > > >>> +};
> > > >>> +
> > > >>>    static int ayaneo_ec_probe(struct platform_device *pdev)
> > > >>>    {
> > > >>>        const struct dmi_system_id *dmi_entry;
> > > >>> @@ -307,6 +412,7 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> > > >>>    static struct platform_driver ayaneo_platform_driver = {
> > > >>>        .driver = {
> > > >>>                .name = "ayaneo-ec",
> > > >>> +             .dev_groups = ayaneo_ec_groups,
> > > >>>        },
> > > >>>        .probe = ayaneo_ec_probe,
> > > >>>    };
> > >
> >
>


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-11-05 13:24           ` Ilpo Järvinen
  2025-11-05 13:38             ` Antheas Kapenekakis
@ 2025-11-05 22:20             ` Armin Wolf
  1 sibling, 0 replies; 30+ messages in thread
From: Armin Wolf @ 2025-11-05 22:20 UTC (permalink / raw)
  To: Ilpo Järvinen, Antheas Kapenekakis
  Cc: platform-driver-x86, LKML, linux-hwmon, Hans de Goede,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 05.11.25 um 14:24 schrieb Ilpo Järvinen:

> On Wed, 5 Nov 2025, Antheas Kapenekakis wrote:
>
>> On Tue, 4 Nov 2025 at 21:04, Armin Wolf <W_Armin@gmx.de> wrote:
>>> Am 02.11.25 um 19:46 schrieb Antheas Kapenekakis:
>>>
>>>> On Sun, 2 Nov 2025 at 19:30, Armin Wolf <W_Armin@gmx.de> wrote:
>>>>> Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
>>>>>
>>>>>> The Ayaneo 3 features hot-swappable controller modules. The ejection
>>>>>> and management is done through HID. However, after ejecting the modules,
>>>>>> the controller needs to be power cycled via the EC to re-initialize.
>>>>>>
>>>>>> For this, the EC provides a variable that holds whether the left or
>>>>>> right modules are connected, and a power control register to turn
>>>>>> the controller on or off. After ejecting the modules, the controller
>>>>>> should be turned off. Then, after both modules are reinserted,
>>>>>> the controller may be powered on again to re-initialize.
>>>>>>
>>>>>> This patch introduces two new sysfs attributes:
>>>>>>     - `controller_modules`: a read-only attribute that indicates whether
>>>>>>       the left and right modules are connected (none, left, right, both).
>>>>>>     - `controller_power`: a read-write attribute that allows the user
>>>>>>       to turn the controller on or off (with '1'/'0').
>>>>>>
>>>>>> Therefore, after ejection is complete, userspace can power off the
>>>>>> controller, then wait until both modules have been reinserted
>>>>>> (`controller_modules` will return 'both') to turn on the controller.
>>>>>>
>>>>>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>>>>>> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
>>>>>> ---
>>>>>>     .../ABI/testing/sysfs-platform-ayaneo-ec      |  19 ++++
>>>>>>     MAINTAINERS                                   |   1 +
>>>>>>     drivers/platform/x86/ayaneo-ec.c              | 106 ++++++++++++++++++
>>>>>>     3 files changed, 126 insertions(+)
>>>>>>     create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
>>>>>>
>>>>>> diff --git a/Documentation/ABI/testing/sysfs-platform-ayaneo-ec b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
>>>>>> new file mode 100644
>>>>>> index 000000000000..3c9c3580c685
>>>>>> --- /dev/null
>>>>>> +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
>>>>>> @@ -0,0 +1,19 @@
>>>>>> +What:                /sys/devices/platform/ayaneo-ec/controller_power
>>>>>> +Date:                Oct 2025
>>>>> I think you need to update those dates.
>>>>>
>>>>>> +KernelVersion:       6.19
>>>>>> +Contact:     "Antheas Kapenekakis" <lkml@antheas.dev>
>>>>>> +Description:
>>>>>> +             Current controller power state. Allows turning on and off
>>>>>> +             the controller power (e.g. for power savings). Write 1 to
>>>>>> +             turn on, 0 to turn off. File is readable and writable.
>>>>>> +
>>>>>> +What:                /sys/devices/platform/ayaneo-ec/controller_modules
>>>>>> +Date:                Oct 2025
>>>>>> +KernelVersion:       6.19
>>>>>> +Contact:     "Antheas Kapenekakis"  <lkml@antheas.dev>
>>>>>> +Description:
>>>>>> +             Shows which controller modules are currently connected to
>>>>>> +             the device. Possible values are "left", "right" and "both".
>>>>>> +             File is read-only. The Windows software for this device
>>>>>> +             will only set controller power to 1 if both module sides
>>>>>> +             are connected (i.e. this file returns "both").
>>>>>> diff --git a/MAINTAINERS b/MAINTAINERS
>>>>>> index da9498d8cc89..b4d62ea9a926 100644
>>>>>> --- a/MAINTAINERS
>>>>>> +++ b/MAINTAINERS
>>>>>> @@ -4191,6 +4191,7 @@ AYANEO PLATFORM EC DRIVER
>>>>>>     M:  Antheas Kapenekakis <lkml@antheas.dev>
>>>>>>     L:  platform-driver-x86@vger.kernel.org
>>>>>>     S:  Maintained
>>>>>> +F:   Documentation/ABI/testing/sysfs-platform-ayaneo
>>>>>>     F:  drivers/platform/x86/ayaneo-ec.c
>>>>>>
>>>>>>     AZ6007 DVB DRIVER
>>>>>> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
>>>>>> index 697bb053a7d6..0652c044ad76 100644
>>>>>> --- a/drivers/platform/x86/ayaneo-ec.c
>>>>>> +++ b/drivers/platform/x86/ayaneo-ec.c
>>>>>> @@ -8,6 +8,7 @@
>>>>>>      */
>>>>>>
>>>>>>     #include <linux/acpi.h>
>>>>>> +#include <linux/bits.h>
>>>>>>     #include <linux/dmi.h>
>>>>>>     #include <linux/err.h>
>>>>>>     #include <linux/hwmon.h>
>>>>>> @@ -16,6 +17,7 @@
>>>>>>     #include <linux/module.h>
>>>>>>     #include <linux/platform_device.h>
>>>>>>     #include <linux/power_supply.h>
>>>>>> +#include <linux/sysfs.h>
>>>>>>     #include <acpi/battery.h>
>>>>>>
>>>>>>     #define AYANEO_PWM_ENABLE_REG        0x4A
>>>>>> @@ -32,9 +34,17 @@
>>>>>>     #define AYANEO_CHARGE_VAL_AUTO              0xaa
>>>>>>     #define AYANEO_CHARGE_VAL_INHIBIT   0x55
>>>>>>
>>>>>> +#define AYANEO_POWER_REG     0x2d
>>>>>> +#define AYANEO_POWER_OFF     0xfe
>>>>>> +#define AYANEO_POWER_ON              0xff
>>>>>> +#define AYANEO_MODULE_REG    0x2f
>>>>>> +#define AYANEO_MODULE_LEFT   BIT(0)
>>>>>> +#define AYANEO_MODULE_RIGHT  BIT(1)
>>>>> Using GENMASK() would make sense here.
>>>> Only a single bit is being used though? GENMASK is used for a contiguous series?
>>> I was thinking of using GENMASK() for both bits:
>>>
>>> #define AYANEO_MODULE_MASK      GENMASK(1, 0)
>>>
>>> You can then retrieve both bits using FIELD_GET() and simply use a switch statement
>>> together with an enum in controller_modules_show().
>> I will look at it closer if I revise the first 5 patches. The logic of
>> the register is complicated due to the flip, so I would not be eager
>> to.
>>
>> I could do #define AYANEO_MODULE_MASK (AYANEO_MODULE_LEFT |
>> AYANEO_MODULE_RIGHT) for stylistic reasons.
> Hi,
>
> Quickly looking at the code below, a mask define certainly be useful to
> avoid having to embed that into the switch itself below.
>
>> @Ilpo: for the first 5 of this series is there something missing other
>> than perhaps the month? If not, I can respin the last patch on its own
>> later today. I would like this driver + the asus stuff to go into 6.19
>> if possible (there: i dealt with the asusctl bug by skipping the
>> quirk), I am unsure for the timeline for that. I want to say merge
>> window starts in two weeks?
> I've been sick for almost a week so I'm a bit lost with all the progress
> and status with various series so it takes time to catch up until can
> say something when it comes to any particular series (I'm sorry).

I wish you a speedy recovery :)

Thanks,
Armin Wolf

> However, we're only at -rc4 (since Nov 2) so there's 4 or 5 weeks until
> the merge window. And this is new driver so no regression potential other
> than build failures so I don't see big schedule pressure yet, tbh.
>
> I try to take it into the consideration with my review scheduling even
> under normal circumstances that submitters have the opportunity to make it
> assuming one makes suggested changes (if any) in a reasonable time frame.
>
> It generally helps to pay attention to the kernel cycle as closer to the
> merge window we get, it usually helps to prioritize things right, e.g.,
> sending a series on the last week or close to it, getting trivial
> feedback, and not addressing it timely with an update will result in
> missing that kernel cycle; honestly, I always get a bit sad when I see
> people do that.
>
>
>> Antheas
>>
>>> Thanks,
>>> Armin Wolf
>>>
>>>>> With those issues being fixed:
>>>>> Reviewed-by: Armin Wolf <W_Armin@gmx.de>
>>>>>
>>>>>> +
>>>>>>     struct ayaneo_ec_quirk {
>>>>>>         bool has_fan_control;
>>>>>>         bool has_charge_control;
>>>>>> +     bool has_magic_modules;
>>>>>>     };
>>>>>>
>>>>>>     struct ayaneo_ec_platform_data {
>>>>>> @@ -46,6 +56,7 @@ struct ayaneo_ec_platform_data {
>>>>>>     static const struct ayaneo_ec_quirk quirk_ayaneo3 = {
>>>>>>         .has_fan_control = true,
>>>>>>         .has_charge_control = true,
>>>>>> +     .has_magic_modules = true,
>>>>>>     };
>>>>>>
>>>>>>     static const struct dmi_system_id dmi_table[] = {
>>>>>> @@ -266,6 +277,100 @@ static int ayaneo_remove_battery(struct power_supply *battery,
>>>>>>         return 0;
>>>>>>     }
>>>>>>
>>>>>> +static ssize_t controller_power_store(struct device *dev,
>>>>>> +                                   struct device_attribute *attr,
>>>>>> +                                   const char *buf,
>>>>>> +                                   size_t count)
>>>>>> +{
>>>>>> +     bool value;
>>>>>> +     int ret;
>>>>>> +
>>>>>> +     ret = kstrtobool(buf, &value);
>>>>>> +     if (ret)
>>>>>> +             return ret;
>>>>>> +
>>>>>> +     ret = ec_write(AYANEO_POWER_REG, value ? AYANEO_POWER_ON : AYANEO_POWER_OFF);
>>>>>> +     if (ret)
>>>>>> +             return ret;
>>>>>> +
>>>>>> +     return count;
>>>>>> +}
>>>>>> +
>>>>>> +static ssize_t controller_power_show(struct device *dev,
>>>>>> +                                  struct device_attribute *attr,
>>>>>> +                                  char *buf)
>>>>>> +{
>>>>>> +     int ret;
>>>>>> +     u8 val;
>>>>>> +
>>>>>> +     ret = ec_read(AYANEO_POWER_REG, &val);
>>>>>> +     if (ret)
>>>>>> +             return ret;
>>>>>> +
>>>>>> +     return sysfs_emit(buf, "%d\n", val == AYANEO_POWER_ON);
>>>>>> +}
>>>>>> +
>>>>>> +static DEVICE_ATTR_RW(controller_power);
>>>>>> +
>>>>>> +static ssize_t controller_modules_show(struct device *dev,
>>>>>> +                                    struct device_attribute *attr, char *buf)
>>>>>> +{
>>>>>> +     char *out;
>>>>>> +     int ret;
>>>>>> +     u8 val;
>>>>>> +
>>>>>> +     ret = ec_read(AYANEO_MODULE_REG, &val);
>>>>>> +     if (ret)
>>>>>> +             return ret;
>>>>>> +
>>>>>> +     switch (~val & (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)) {
> Why's the extra inversion here? Do those bits actually have the opposite
> meaning compared with their names?
>
> --
>   i.
>
>>>>>> +     case AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT:
>>>>>> +             out = "both";
>>>>>> +             break;
>>>>>> +     case AYANEO_MODULE_LEFT:
>>>>>> +             out = "left";
>>>>>> +             break;
>>>>>> +     case AYANEO_MODULE_RIGHT:
>>>>>> +             out = "right";
>>>>>> +             break;
>>>>>> +     default:
>>>>>> +             out = "none";
>>>>>> +             break;
>>>>>> +     }
>>>>>> +
>>>>>> +     return sysfs_emit(buf, "%s\n", out);
>>>>>> +}
>>>>>> +
>>>>>> +static DEVICE_ATTR_RO(controller_modules);
>>>>>> +
>>>>>> +static struct attribute *aya_mm_attrs[] = {
>>>>>> +     &dev_attr_controller_power.attr,
>>>>>> +     &dev_attr_controller_modules.attr,
>>>>>> +     NULL
>>>>>> +};
>>>>>> +
>>>>>> +static umode_t aya_mm_is_visible(struct kobject *kobj,
>>>>>> +                              struct attribute *attr, int n)
>>>>>> +{
>>>>>> +     struct device *dev = kobj_to_dev(kobj);
>>>>>> +     struct platform_device *pdev = to_platform_device(dev);
>>>>>> +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
>>>>>> +
>>>>>> +     if (data->quirks->has_magic_modules)
>>>>>> +             return attr->mode;
>>>>>> +     return 0;
>>>>>> +}
>>>>>> +
>>>>>> +static const struct attribute_group aya_mm_attribute_group = {
>>>>>> +     .is_visible = aya_mm_is_visible,
>>>>>> +     .attrs = aya_mm_attrs,
>>>>>> +};
>>>>>> +
>>>>>> +static const struct attribute_group *ayaneo_ec_groups[] = {
>>>>>> +     &aya_mm_attribute_group,
>>>>>> +     NULL
>>>>>> +};
>>>>>> +
>>>>>>     static int ayaneo_ec_probe(struct platform_device *pdev)
>>>>>>     {
>>>>>>         const struct dmi_system_id *dmi_entry;
>>>>>> @@ -307,6 +412,7 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>>>>>>     static struct platform_driver ayaneo_platform_driver = {
>>>>>>         .driver = {
>>>>>>                 .name = "ayaneo-ec",
>>>>>> +             .dev_groups = ayaneo_ec_groups,
>>>>>>         },
>>>>>>         .probe = ayaneo_ec_probe,
>>>>>>     };

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-11-02 18:58     ` Antheas Kapenekakis
@ 2025-11-10 12:11       ` Ilpo Järvinen
  0 siblings, 0 replies; 30+ messages in thread
From: Ilpo Järvinen @ 2025-11-10 12:11 UTC (permalink / raw)
  To: Antheas Kapenekakis
  Cc: Armin Wolf, platform-driver-x86, LKML, linux-hwmon, Hans de Goede,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

On Sun, 2 Nov 2025, Antheas Kapenekakis wrote:

> On Sun, 2 Nov 2025 at 19:35, Armin Wolf <W_Armin@gmx.de> wrote:
> >
> > Am 31.10.25 um 17:36 schrieb Antheas Kapenekakis:
> >
> > > The Ayaneo EC resets after hibernation, losing the charge control state.
> > > Add a small PM hook to restore this state on hibernation resume.
> > >
> > > The fan speed is also lost during hibernation, but since hibernation
> > > failures are common with this class of devices, setting a low fan speed
> > > when the userspace program controlling the fan will potentially not
> > > take over could cause the device to overheat, so it is not restored.
> >
> > Please update the patch description.
> >
> > >
> > > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > > ---
> > >   drivers/platform/x86/ayaneo-ec.c | 73 ++++++++++++++++++++++++++++++++
> > >   1 file changed, 73 insertions(+)
> > >
> > > diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> > > index 9548e3d22093..e1ad5968d3b4 100644
> > > --- a/drivers/platform/x86/ayaneo-ec.c
> > > +++ b/drivers/platform/x86/ayaneo-ec.c
> > > @@ -41,6 +41,8 @@
> > >   #define AYANEO_MODULE_LEFT  BIT(0)
> > >   #define AYANEO_MODULE_RIGHT BIT(1)
> > >
> > > +#define AYANEO_CACHE_LEN     1
> > > +
> > >   struct ayaneo_ec_quirk {
> > >       bool has_fan_control;
> > >       bool has_charge_control;
> > > @@ -51,6 +53,9 @@ struct ayaneo_ec_platform_data {
> > >       struct platform_device *pdev;
> > >       struct ayaneo_ec_quirk *quirks;
> > >       struct acpi_battery_hook battery_hook;
> > > +
> > > +     bool restore_charge_limit;
> > > +     bool restore_pwm;
> > >   };
> > >
> > >   static const struct ayaneo_ec_quirk quirk_fan = {
> > > @@ -207,10 +212,14 @@ static int ayaneo_ec_read(struct device *dev, enum hwmon_sensor_types type,
> > >   static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> > >                          u32 attr, int channel, long val)
> > >   {
> > > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(
> > > +             to_platform_device(dev));
> > > +     int ret;
> > >       switch (type) {
> > >       case hwmon_pwm:
> > >               switch (attr) {
> > >               case hwmon_pwm_enable:
> > > +                     data->restore_pwm = false;
> > >                       switch (val) {
> > >                       case 1:
> > >                               return ec_write(AYANEO_PWM_ENABLE_REG,
> > > @@ -224,6 +233,15 @@ static int ayaneo_ec_write(struct device *dev, enum hwmon_sensor_types type,
> > >               case hwmon_pwm_input:
> > >                       if (val < 0 || val > 255)
> > >                               return -EINVAL;
> > > +                     if (data->restore_pwm) {
> > > +                             // Defer restoring PWM control to after
> > > +                             // userspace resumes successfully
> > > +                             ret = ec_write(AYANEO_PWM_ENABLE_REG,
> > > +                                            AYANEO_PWM_MODE_MANUAL);
> > > +                             if (ret)
> > > +                                     return ret;
> > > +                             data->restore_pwm = false;
> >
> > I suspect that you need to use a mutex to protect the restore sequence.
> 
> This is indeed true. I can respin the last patch with a mutex and fix
> the description.
> 
> If the date on the control modules patch is the only issue, I can skip
> re-sending the first 5.

As usual, please send next version of the series once you've addressed the 
review comments.

If you want some patches to not delay the first part of the series, send 
those separately.

--
 i.

> 
> 
> > Thanks,
> > Armin Wolf
> >
> > > +                     }
> > >                       return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
> > >               default:
> > >                       break;
> > > @@ -474,10 +492,65 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> > >       return 0;
> > >   }
> > >
> > > +static int ayaneo_freeze(struct device *dev)
> > > +{
> > > +     struct platform_device *pdev = to_platform_device(dev);
> > > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > > +     int ret;
> > > +     u8 tmp;
> > > +
> > > +     if (data->quirks->has_charge_control) {
> > > +             ret = ec_read(AYANEO_CHARGE_REG, &tmp);
> > > +             if (ret)
> > > +                     return ret;
> > > +
> > > +             data->restore_charge_limit = tmp == AYANEO_CHARGE_VAL_INHIBIT;
> > > +     }
> > > +
> > > +     if (data->quirks->has_fan_control) {
> > > +             ret = ec_read(AYANEO_PWM_ENABLE_REG, &tmp);
> > > +             if (ret)
> > > +                     return ret;
> > > +
> > > +             data->restore_pwm = tmp == AYANEO_PWM_MODE_MANUAL;
> > > +
> > > +             // Release the fan when entering hibernation to avoid
> > > +             // overheating if hibernation fails and hangs
> > > +             if (data->restore_pwm) {
> > > +                     ret = ec_write(AYANEO_PWM_ENABLE_REG, AYANEO_PWM_MODE_AUTO);
> > > +                     if (ret)
> > > +                             return ret;
> > > +             }
> > > +     }
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static int ayaneo_restore(struct device *dev)
> > > +{
> > > +     struct platform_device *pdev = to_platform_device(dev);
> > > +     struct ayaneo_ec_platform_data *data = platform_get_drvdata(pdev);
> > > +     int ret;
> > > +
> > > +     if (data->quirks->has_charge_control && data->restore_charge_limit) {
> > > +             ret = ec_write(AYANEO_CHARGE_REG, AYANEO_CHARGE_VAL_INHIBIT);
> > > +             if (ret)
> > > +                     return ret;
> > > +     }
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static const struct dev_pm_ops ayaneo_pm_ops = {
> > > +     .freeze = ayaneo_freeze,
> > > +     .restore = ayaneo_restore,
> > > +};
> > > +
> > >   static struct platform_driver ayaneo_platform_driver = {
> > >       .driver = {
> > >               .name = "ayaneo-ec",
> > >               .dev_groups = ayaneo_ec_groups,
> > > +             .pm = &ayaneo_pm_ops,
> > >       },
> > >       .probe = ayaneo_ec_probe,
> > >   };
> >
> 
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2025-11-10 12:11 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-31 16:36 [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
2025-10-31 16:36 ` [PATCH v3 1/6] " Antheas Kapenekakis
2025-11-02 18:21   ` Armin Wolf
2025-11-02 18:35     ` Armin Wolf
     [not found]     ` <CAGwozwGO=sZjGro7NaKH=zC4x_GR8H7kaPSn8NP60H7EZ4s3+g@mail.gmail.com>
2025-11-04 19:58       ` Armin Wolf
2025-10-31 16:36 ` [PATCH v3 2/6] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
2025-11-02 18:22   ` Armin Wolf
2025-10-31 16:36 ` [PATCH v3 3/6] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
2025-11-02 18:26   ` Armin Wolf
2025-11-02 18:37     ` Antheas Kapenekakis
2025-11-04 20:00       ` Armin Wolf
2025-10-31 16:36 ` [PATCH v3 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
2025-11-02 18:30   ` Armin Wolf
2025-11-02 18:46     ` Antheas Kapenekakis
2025-11-04 20:03       ` Armin Wolf
2025-11-05 12:33         ` Antheas Kapenekakis
2025-11-05 13:24           ` Ilpo Järvinen
2025-11-05 13:38             ` Antheas Kapenekakis
2025-11-05 22:20             ` Armin Wolf
2025-10-31 16:36 ` [PATCH v3 5/6] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
2025-10-31 16:36 ` [PATCH v3 6/6] platform/x86: ayaneo-ec: Add suspend hook Antheas Kapenekakis
2025-11-02 18:35   ` Armin Wolf
2025-11-02 18:58     ` Antheas Kapenekakis
2025-11-10 12:11       ` Ilpo Järvinen
2025-11-03 16:51   ` Mario Limonciello (AMD) (kernel.org)
2025-11-03 21:20     ` Antheas Kapenekakis
2025-11-03 21:33       ` Mario Limonciello (AMD) (kernel.org)
2025-11-03 22:09         ` Antheas Kapenekakis
2025-10-31 16:38 ` [PATCH v3 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
2025-10-31 16:39   ` Antheas Kapenekakis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).