platform-driver-x86.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
@ 2025-11-10 18:08 Antheas Kapenekakis
  2025-11-10 18:08 ` [PATCH v4 1/6] " Antheas Kapenekakis
                   ` (5 more replies)
  0 siblings, 6 replies; 13+ messages in thread
From: Antheas Kapenekakis @ 2025-11-10 18:08 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/20251031163651.1465981-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:
  - Move ABI date to November
  - Add mutex to suspend hook and cleanup
  - Fix data access from within hwmon in suspend hook

Changes since V2:
  - 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 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              | 591 ++++++++++++++++++
 drivers/platform/x86/oxpec.c                  | 115 +---
 6 files changed, 634 insertions(+), 117 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-platform-ayaneo-ec
 create mode 100644 drivers/platform/x86/ayaneo-ec.c


base-commit: e9a6fb0bcdd7609be6969112f3fbfcce3b1d4a7c
-- 
2.51.2



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

* [PATCH v4 1/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
  2025-11-10 18:08 [PATCH v4 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
@ 2025-11-10 18:08 ` Antheas Kapenekakis
  2025-11-11 10:18   ` Ilpo Järvinen
  2025-11-10 18:08 ` [PATCH v4 2/6] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 13+ messages in thread
From: Antheas Kapenekakis @ 2025-11-10 18:08 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 ddecf1ef3bed..c5bf7207c45f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4192,6 +4192,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 c122016d82f1..1a7f8ae793ba 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.2



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

* [PATCH v4 2/6] platform/x86: ayaneo-ec: Add hwmon support
  2025-11-10 18:08 [PATCH v4 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
  2025-11-10 18:08 ` [PATCH v4 1/6] " Antheas Kapenekakis
@ 2025-11-10 18:08 ` Antheas Kapenekakis
  2025-11-10 18:08 ` [PATCH v4 3/6] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Antheas Kapenekakis @ 2025-11-10 18:08 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

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 1a7f8ae793ba..974c174b1a7e 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.2



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

* [PATCH v4 3/6] platform/x86: ayaneo-ec: Add charge control support
  2025-11-10 18:08 [PATCH v4 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
  2025-11-10 18:08 ` [PATCH v4 1/6] " Antheas Kapenekakis
  2025-11-10 18:08 ` [PATCH v4 2/6] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
@ 2025-11-10 18:08 ` Antheas Kapenekakis
  2025-11-10 18:08 ` [PATCH v4 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 13+ messages in thread
From: Antheas Kapenekakis @ 2025-11-10 18:08 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 974c174b1a7e..d5be37db997c 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.2



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

* [PATCH v4 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-11-10 18:08 [PATCH v4 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (2 preceding siblings ...)
  2025-11-10 18:08 ` [PATCH v4 3/6] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
@ 2025-11-10 18:08 ` Antheas Kapenekakis
  2025-11-11 13:41   ` Ilpo Järvinen
  2025-11-10 18:08 ` [PATCH v4 5/6] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
  2025-11-10 18:08 ` [PATCH v4 6/6] platform/x86: ayaneo-ec: Add suspend hook Antheas Kapenekakis
  5 siblings, 1 reply; 13+ messages in thread
From: Antheas Kapenekakis @ 2025-11-10 18:08 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..4cffbf5fc7ca
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
@@ -0,0 +1,19 @@
+What:		/sys/devices/platform/ayaneo-ec/controller_power
+Date:		Nov 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:		Nov 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 c5bf7207c45f..f8ab009b6224 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4196,6 +4196,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.2



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

* [PATCH v4 5/6] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec
  2025-11-10 18:08 [PATCH v4 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (3 preceding siblings ...)
  2025-11-10 18:08 ` [PATCH v4 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
@ 2025-11-10 18:08 ` Antheas Kapenekakis
  2025-11-10 18:08 ` [PATCH v4 6/6] platform/x86: ayaneo-ec: Add suspend hook Antheas Kapenekakis
  5 siblings, 0 replies; 13+ messages in thread
From: Antheas Kapenekakis @ 2025-11-10 18:08 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 d5be37db997c..1927fe229085 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.2



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

* [PATCH v4 6/6] platform/x86: ayaneo-ec: Add suspend hook
  2025-11-10 18:08 [PATCH v4 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (4 preceding siblings ...)
  2025-11-10 18:08 ` [PATCH v4 5/6] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
@ 2025-11-10 18:08 ` Antheas Kapenekakis
  5 siblings, 0 replies; 13+ messages in thread
From: Antheas Kapenekakis @ 2025-11-10 18:08 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 | 84 +++++++++++++++++++++++++++++++-
 1 file changed, 83 insertions(+), 1 deletion(-)

diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
index 9548e3d22093..ee97c326c429 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -51,6 +51,11 @@ struct ayaneo_ec_platform_data {
 	struct platform_device *pdev;
 	struct ayaneo_ec_quirk *quirks;
 	struct acpi_battery_hook battery_hook;
+
+	// Protects access to restore_pwm
+	struct mutex hwmon_lock;
+	bool restore_charge_limit;
+	bool restore_pwm;
 };
 
 static const struct ayaneo_ec_quirk quirk_fan = {
@@ -207,10 +212,16 @@ 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 = dev_get_drvdata(dev);
+	int ret;
+
+	guard(mutex)(&data->hwmon_lock);
+
 	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 +235,17 @@ 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;
@@ -453,11 +475,14 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
 
 	data->pdev = pdev;
 	data->quirks = dmi_entry->driver_data;
+	ret = devm_mutex_init(&pdev->dev, &data->hwmon_lock);
+	if (ret)
+		return ret;
 	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);
+			"ayaneo_ec", data, &ayaneo_ec_chip_info, NULL);
 		if (IS_ERR(hwdev))
 			return PTR_ERR(hwdev);
 	}
@@ -474,10 +499,67 @@ 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.2



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

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

On Mon, 10 Nov 2025, Antheas Kapenekakis wrote:

> 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 ddecf1ef3bed..c5bf7207c45f 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4192,6 +4192,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 c122016d82f1..1a7f8ae793ba 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.
> +

It seems you've missed Armin's comment on v3 that this needs
depends on DMI.

-- 
 i.

>  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");
> 

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

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

On Mon, 10 Nov 2025, Antheas Kapenekakis wrote:

> 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..4cffbf5fc7ca
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> @@ -0,0 +1,19 @@
> +What:		/sys/devices/platform/ayaneo-ec/controller_power
> +Date:		Nov 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:		Nov 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 c5bf7207c45f..f8ab009b6224 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4196,6 +4196,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)) {

This too is constructing mask still here which is ugly.

> +	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,
>  };
> 

-- 
 i.


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

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

On Tue, 11 Nov 2025 at 14:41, Ilpo Järvinen
<ilpo.jarvinen@linux.intel.com> wrote:
>
> On Mon, 10 Nov 2025, Antheas Kapenekakis wrote:
>
> > 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..4cffbf5fc7ca
> > --- /dev/null
> > +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > @@ -0,0 +1,19 @@
> > +What:                /sys/devices/platform/ayaneo-ec/controller_power
> > +Date:                Nov 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:                Nov 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 c5bf7207c45f..f8ab009b6224 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -4196,6 +4196,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)) {
>
> This too is constructing mask still here which is ugly.

I can bring back the bools :-)

I agree but that's what I came up with to remove them

> > +     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,
> >  };
> >
>
> --
>  i.
>
>


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

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

[-- Attachment #1: Type: text/plain, Size: 9380 bytes --]

On Tue, 11 Nov 2025, Antheas Kapenekakis wrote:

> On Tue, 11 Nov 2025 at 14:41, Ilpo Järvinen
> <ilpo.jarvinen@linux.intel.com> wrote:
> >
> > On Mon, 10 Nov 2025, Antheas Kapenekakis wrote:
> >
> > > 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..4cffbf5fc7ca
> > > --- /dev/null
> > > +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > > @@ -0,0 +1,19 @@
> > > +What:                /sys/devices/platform/ayaneo-ec/controller_power
> > > +Date:                Nov 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:                Nov 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 c5bf7207c45f..f8ab009b6224 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -4196,6 +4196,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)) {
> >
> > This too is constructing mask still here which is ugly.
> 
> I can bring back the bools :-)
> 
> I agree but that's what I came up with to remove them

Just Add a define for the mask instead as was requested. There's no need 
to make this any harder than that.

-- 
 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,
> > >  };
> > >
> >
> > --
> >  i.
> >
> >
> 

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

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

On Wed, 12 Nov 2025 at 13:34, Ilpo Järvinen
<ilpo.jarvinen@linux.intel.com> wrote:
>
> On Tue, 11 Nov 2025, Antheas Kapenekakis wrote:
>
> > On Tue, 11 Nov 2025 at 14:41, Ilpo Järvinen
> > <ilpo.jarvinen@linux.intel.com> wrote:
> > >
> > > On Mon, 10 Nov 2025, Antheas Kapenekakis wrote:
> > >
> > > > 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..4cffbf5fc7ca
> > > > --- /dev/null
> > > > +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > > > @@ -0,0 +1,19 @@
> > > > +What:                /sys/devices/platform/ayaneo-ec/controller_power
> > > > +Date:                Nov 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:                Nov 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 c5bf7207c45f..f8ab009b6224 100644
> > > > --- a/MAINTAINERS
> > > > +++ b/MAINTAINERS
> > > > @@ -4196,6 +4196,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)) {
> > >
> > > This too is constructing mask still here which is ugly.
> >
> > I can bring back the bools :-)
> >
> > I agree but that's what I came up with to remove them
>
> Just Add a define for the mask instead as was requested. There's no need
> to make this any harder than that.

Should the mask be:

(AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)

or

GENMASK(2,0)ish

I find the latter a bit more error prone since it redefines. I can
move forward with the former one later today if there arent any other
comments

> --
>  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,
> > > >  };
> > > >
> > >
> > > --
> > >  i.
> > >
> > >
> >


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

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

[-- Attachment #1: Type: text/plain, Size: 8596 bytes --]

On Wed, 12 Nov 2025, Antheas Kapenekakis wrote:

> On Wed, 12 Nov 2025 at 13:34, Ilpo Järvinen
> <ilpo.jarvinen@linux.intel.com> wrote:
> >
> > On Tue, 11 Nov 2025, Antheas Kapenekakis wrote:
> >
> > > On Tue, 11 Nov 2025 at 14:41, Ilpo Järvinen
> > > <ilpo.jarvinen@linux.intel.com> wrote:
> > > >
> > > > On Mon, 10 Nov 2025, Antheas Kapenekakis wrote:
> > > >
> > > > > 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..4cffbf5fc7ca
> > > > > --- /dev/null
> > > > > +++ b/Documentation/ABI/testing/sysfs-platform-ayaneo-ec
> > > > > @@ -0,0 +1,19 @@
> > > > > +What:                /sys/devices/platform/ayaneo-ec/controller_power
> > > > > +Date:                Nov 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:                Nov 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 c5bf7207c45f..f8ab009b6224 100644
> > > > > --- a/MAINTAINERS
> > > > > +++ b/MAINTAINERS
> > > > > @@ -4196,6 +4196,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)) {
> > > >
> > > > This too is constructing mask still here which is ugly.
> > >
> > > I can bring back the bools :-)
> > >
> > > I agree but that's what I came up with to remove them
> >
> > Just Add a define for the mask instead as was requested. There's no need
> > to make this any harder than that.
> 
> Should the mask be:
> 
> (AYANEO_MODULE_LEFT | AYANEO_MODULE_RIGHT)
> 
> or
> 
> GENMASK(2,0)ish

This has a wrong bit index.

> I find the latter a bit more error prone since it redefines. I can
> move forward with the former one later today if there arent any other
> comments

Either way is fine with me, as long as it's not in the switch itself.
I think I lean a bit more to the former one.

-- 
 i.

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

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

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-10 18:08 [PATCH v4 0/6] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
2025-11-10 18:08 ` [PATCH v4 1/6] " Antheas Kapenekakis
2025-11-11 10:18   ` Ilpo Järvinen
2025-11-10 18:08 ` [PATCH v4 2/6] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
2025-11-10 18:08 ` [PATCH v4 3/6] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
2025-11-10 18:08 ` [PATCH v4 4/6] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
2025-11-11 13:41   ` Ilpo Järvinen
2025-11-11 21:43     ` Antheas Kapenekakis
2025-11-12 12:34       ` Ilpo Järvinen
2025-11-12 12:37         ` Antheas Kapenekakis
2025-11-12 12:48           ` Ilpo Järvinen
2025-11-10 18:08 ` [PATCH v4 5/6] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
2025-11-10 18:08 ` [PATCH v4 6/6] platform/x86: ayaneo-ec: Add suspend hook 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).