linux-hwmon.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v1 0/5] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
@ 2025-08-20 16:06 Antheas Kapenekakis
  2025-08-20 16:06 ` [PATCH v1 1/5] " Antheas Kapenekakis
                   ` (4 more replies)
  0 siblings, 5 replies; 15+ messages in thread
From: Antheas Kapenekakis @ 2025-08-20 16:06 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

Antheas Kapenekakis (5):
  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

 MAINTAINERS                      |   6 +
 drivers/platform/x86/Kconfig     |  17 +-
 drivers/platform/x86/Makefile    |   3 +
 drivers/platform/x86/ayaneo-ec.c | 631 +++++++++++++++++++++++++++++++
 drivers/platform/x86/oxpec.c     | 115 +-----
 5 files changed, 655 insertions(+), 117 deletions(-)
 create mode 100644 drivers/platform/x86/ayaneo-ec.c


base-commit: c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9
-- 
2.50.1



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

* [PATCH v1 1/5] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver
  2025-08-20 16:06 [PATCH v1 0/5] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
@ 2025-08-20 16:06 ` Antheas Kapenekakis
  2025-08-20 16:06 ` [PATCH v1 2/5] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 15+ messages in thread
From: Antheas Kapenekakis @ 2025-08-20 16:06 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     | 10 ++++
 drivers/platform/x86/Makefile    |  3 ++
 drivers/platform/x86/ayaneo-ec.c | 89 ++++++++++++++++++++++++++++++++
 4 files changed, 108 insertions(+)
 create mode 100644 drivers/platform/x86/ayaneo-ec.c

diff --git a/MAINTAINERS b/MAINTAINERS
index daf520a13bdf..7afa7b8a3494 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4130,6 +4130,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 6d238e120dce..6d4a33791cc1 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -304,6 +304,16 @@ 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"
+	depends on X86
+	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 a0c5848513e3..f310e1233974 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -38,6 +38,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..90b86527ab0d
--- /dev/null
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -0,0 +1,89 @@
+// 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/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 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 *)&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.50.1



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

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

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

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

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 6d4a33791cc1..0a7ca2c78456 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -307,6 +307,8 @@ config ASUS_TF103C_DOCK
 config AYANEO_EC
 	tristate "Ayaneo EC platform control"
 	depends on X86
+	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 90b86527ab0d..8b1902706b81 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -7,13 +7,23 @@
  * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
  */
 
+#include <linux/acpi.h>
 #include <linux/dmi.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 {
@@ -22,6 +32,7 @@ struct ayaneo_ec_platform_data {
 };
 
 static const struct ayaneo_ec_quirk ayaneo3 = {
+	.has_fan_control = true,
 };
 
 static const struct dmi_system_id dmi_table[] = {
@@ -35,10 +46,124 @@ 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;
+			*val = (255 * tmp) / 100;
+			if (*val < 0 || *val > 255)
+				return -EINVAL;
+			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
+				*val = 2;
+			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:
+			if (val == 1)
+				return ec_write(AYANEO_PWM_ENABLE_REG,
+						AYANEO_PWM_MODE_MANUAL);
+			else if (val == 2)
+				return ec_write(AYANEO_PWM_ENABLE_REG,
+						AYANEO_PWM_MODE_AUTO);
+			else
+				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)
@@ -52,6 +177,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.50.1



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

* [PATCH v1 3/5] platform/x86: ayaneo-ec: Add charge control support
  2025-08-20 16:06 [PATCH v1 0/5] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
  2025-08-20 16:06 ` [PATCH v1 1/5] " Antheas Kapenekakis
  2025-08-20 16:06 ` [PATCH v1 2/5] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
@ 2025-08-20 16:06 ` Antheas Kapenekakis
  2025-08-22 12:38   ` Armin Wolf
  2025-08-20 16:06 ` [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
  2025-08-20 16:06 ` [PATCH v1 5/5] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
  4 siblings, 1 reply; 15+ messages in thread
From: Antheas Kapenekakis @ 2025-08-20 16:06 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

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.

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

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 0a7ca2c78456..c871a722e5ef 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -308,6 +308,7 @@ config AYANEO_EC
 	tristate "Ayaneo EC platform control"
 	depends on X86
 	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 8b1902706b81..a4bdc6ae7af7 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -14,6 +14,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <acpi/battery.h>
 
 #define AYANEO_PWM_ENABLE_REG	 0x4A
 #define AYANEO_PWM_REG		 0x4B
@@ -22,17 +23,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 ayaneo3 = {
 	.has_fan_control = true,
+	.has_charge_control = true,
 };
 
 static const struct dmi_system_id dmi_table[] = {
@@ -159,11 +170,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)
@@ -184,6 +286,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.50.1



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

* [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-08-20 16:06 [PATCH v1 0/5] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (2 preceding siblings ...)
  2025-08-20 16:06 ` [PATCH v1 3/5] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
@ 2025-08-20 16:06 ` Antheas Kapenekakis
  2025-08-21  8:41   ` kernel test robot
                     ` (2 more replies)
  2025-08-20 16:06 ` [PATCH v1 5/5] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
  4 siblings, 3 replies; 15+ messages in thread
From: Antheas Kapenekakis @ 2025-08-20 16:06 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 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 firmware 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 'on'/'off').

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.

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

diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
index a4bdc6ae7af7..eb7f9ae03b4f 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -16,6 +16,10 @@
 #include <linux/platform_device.h>
 #include <acpi/battery.h>
 
+#include "firmware_attributes_class.h"
+
+#define DRIVER_NAME "ayaneo-ec"
+
 #define AYANEO_PWM_ENABLE_REG	 0x4A
 #define AYANEO_PWM_REG		 0x4B
 #define AYANEO_PWM_MODE_AUTO	 0x00
@@ -30,20 +34,60 @@
 #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)
+
+enum ayaneo_fw_attr_id {
+	AYANEO_ATTR_CONTROLLER_MODULES,
+	AYANEO_ATTR_CONTROLLER_POWER,
+};
+
+static const char *const ayaneo_fw_attr_name[] = {
+	[AYANEO_ATTR_CONTROLLER_MODULES] = "controller_modules",
+	[AYANEO_ATTR_CONTROLLER_POWER] = "controller_power",
+};
+
+static const char *const ayaneo_fw_attr_desc[] = {
+	[AYANEO_ATTR_CONTROLLER_MODULES] =
+		"Which controller Magic Modules are connected (none, left, right, both)",
+	[AYANEO_ATTR_CONTROLLER_POWER] = "Controller power state (on, off)",
+};
+
+#define AYANEO_ATTR_ENUM_MAX_ATTRS 7
+#define AYANEO_ATTR_LANGUAGE_CODE "en_US.UTF-8"
+
 struct ayaneo_ec_quirk {
 	bool has_fan_control;
 	bool has_charge_control;
+	bool has_magic_modules;
+	bool has_controller_power;
 };
 
 struct ayaneo_ec_platform_data {
 	struct platform_device *pdev;
 	struct ayaneo_ec_quirk *quirks;
 	struct acpi_battery_hook battery_hook;
+	struct device *fw_attrs_dev;
+	struct kset *fw_attrs_kset;
+};
+
+struct ayaneo_fw_attr {
+	struct ayaneo_ec_platform_data *data;
+	enum ayaneo_fw_attr_id fw_attr_id;
+	struct attribute_group attr_group;
+	struct kobj_attribute display_name;
+	struct kobj_attribute current_value;
 };
 
 static const struct ayaneo_ec_quirk ayaneo3 = {
 	.has_fan_control = true,
 	.has_charge_control = true,
+	.has_magic_modules = true,
+	.has_controller_power = true,
 };
 
 static const struct dmi_system_id dmi_table[] = {
@@ -260,6 +304,159 @@ static int ayaneo_remove_battery(struct power_supply *battery,
 	return 0;
 }
 
+static void ayaneo_kset_unregister(void *data)
+{
+	struct kset *kset = data;
+
+	kset_unregister(kset);
+}
+
+static void ayaneo_fw_attrs_dev_unregister(void *data)
+{
+	struct device *fw_attrs_dev = data;
+
+	device_unregister(fw_attrs_dev);
+}
+
+static ssize_t display_name_language_code_show(struct kobject *kobj,
+					       struct kobj_attribute *attr,
+					       char *buf)
+{
+	return sysfs_emit(buf, "%s\n", AYANEO_ATTR_LANGUAGE_CODE);
+}
+
+static struct kobj_attribute fw_attr_display_name_language_code =
+	__ATTR_RO(display_name_language_code);
+
+static ssize_t display_name_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *buf)
+{
+	struct ayaneo_fw_attr *fw_attr =
+		container_of(attr, struct ayaneo_fw_attr, display_name);
+
+	return sysfs_emit(buf, "%s\n", ayaneo_fw_attr_desc[fw_attr->fw_attr_id]);
+}
+
+static ssize_t current_value_show(struct kobject *kobj,
+				  struct kobj_attribute *attr, char *buf)
+{
+	struct ayaneo_fw_attr *fw_attr =
+		container_of(attr, struct ayaneo_fw_attr, current_value);
+	bool left, right;
+	char *out;
+	int ret;
+	u8 tmp;
+
+	switch (fw_attr->fw_attr_id) {
+	case AYANEO_ATTR_CONTROLLER_MODULES:
+		ret = ec_read(AYANEO_MODULE_REG, &tmp);
+		if (ret)
+			return ret;
+		left = !(tmp & AYANEO_MODULE_LEFT);
+		right = !(tmp & AYANEO_MODULE_RIGHT);
+
+		if (left && right)
+			out = "both";
+		else if (left)
+			out = "left";
+		else if (right)
+			out = "right";
+		else
+			out = "none";
+
+		return sysfs_emit(buf, "%s\n", out);
+	case AYANEO_ATTR_CONTROLLER_POWER:
+		ret = ec_read(AYANEO_POWER_REG, &tmp);
+		if (ret)
+			return ret;
+
+		if (tmp == AYANEO_POWER_OFF)
+			out = "off";
+		else
+			out = "on";
+
+		return sysfs_emit(buf, "%s\n", out);
+	}
+	return -EINVAL;
+}
+
+static ssize_t current_value_store(struct kobject *kobj,
+				   struct kobj_attribute *attr, const char *buf,
+				   size_t count)
+{
+	struct ayaneo_fw_attr *fw_attr =
+		container_of(attr, struct ayaneo_fw_attr, current_value);
+	int ret;
+
+	switch (fw_attr->fw_attr_id) {
+	case AYANEO_ATTR_CONTROLLER_POWER:
+		if (sysfs_streq(buf, "on"))
+			ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_ON);
+		else if (sysfs_streq(buf, "off"))
+			ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_OFF);
+		if (ret)
+			return ret;
+		return count;
+	case AYANEO_ATTR_CONTROLLER_MODULES:
+		return -EINVAL;
+	}
+	return -EINVAL;
+}
+
+static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
+			 char *buf)
+{
+	return sysfs_emit(buf, "string\n");
+}
+
+static struct kobj_attribute fw_attr_type_string = {
+	.attr = { .name = "type", .mode = 0444 },
+	.show = type_show,
+};
+
+static int ayaneo_fw_attr_init(struct ayaneo_ec_platform_data *data,
+			       const enum ayaneo_fw_attr_id fw_attr_id,
+			       bool read_only)
+{
+	struct ayaneo_fw_attr *fw_attr;
+	struct attribute **attrs;
+	int idx = 0;
+
+	fw_attr = devm_kzalloc(&data->pdev->dev, sizeof(*fw_attr), GFP_KERNEL);
+	if (!fw_attr)
+		return -ENOMEM;
+
+	attrs = devm_kcalloc(&data->pdev->dev, AYANEO_ATTR_ENUM_MAX_ATTRS + 1,
+			     sizeof(*attrs), GFP_KERNEL);
+	if (!attrs)
+		return -ENOMEM;
+
+	fw_attr->data = data;
+	fw_attr->fw_attr_id = fw_attr_id;
+	fw_attr->attr_group.name = ayaneo_fw_attr_name[fw_attr_id];
+	fw_attr->attr_group.attrs = attrs;
+
+	attrs[idx++] = &fw_attr_type_string.attr;
+	attrs[idx++] = &fw_attr_display_name_language_code.attr;
+
+	sysfs_attr_init(&fw_attr->display_name.attr);
+	fw_attr->display_name.attr.name = "display_name";
+	fw_attr->display_name.attr.mode = 0444;
+	fw_attr->display_name.show = display_name_show;
+	attrs[idx++] = &fw_attr->display_name.attr;
+
+	sysfs_attr_init(&fw_attr->current_value.attr);
+	fw_attr->current_value.attr.name = "current_value";
+	fw_attr->current_value.attr.mode = read_only ? 0444 : 0644;
+	fw_attr->current_value.show = current_value_show;
+	fw_attr->current_value.store = current_value_store;
+	attrs[idx++] = &fw_attr->current_value.attr;
+
+	attrs[idx] = NULL;
+	return sysfs_create_group(&data->fw_attrs_kset->kobj,
+				  &fw_attr->attr_group);
+}
+
 static int ayaneo_ec_probe(struct platform_device *pdev)
 {
 	const struct dmi_system_id *dmi_entry;
@@ -295,12 +492,48 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
 			return ret;
 	}
 
+	if (data->quirks->has_magic_modules || data->quirks->has_controller_power) {
+		data->fw_attrs_dev = device_create(&firmware_attributes_class, NULL,
+						MKDEV(0, 0), NULL, "%s",
+						DRIVER_NAME);
+		if (IS_ERR(data->fw_attrs_dev))
+			return PTR_ERR(data->fw_attrs_dev);
+
+		ret = devm_add_action_or_reset(&data->pdev->dev,
+					ayaneo_fw_attrs_dev_unregister,
+					data->fw_attrs_dev);
+		if (ret)
+			return ret;
+
+		data->fw_attrs_kset = kset_create_and_add("attributes", NULL,
+							&data->fw_attrs_dev->kobj);
+		if (!data->fw_attrs_kset)
+			return -ENOMEM;
+
+		ret = devm_add_action_or_reset(&data->pdev->dev, ayaneo_kset_unregister,
+					data->fw_attrs_kset);
+
+		if (data->quirks->has_magic_modules) {
+			ret = ayaneo_fw_attr_init(
+				data, AYANEO_ATTR_CONTROLLER_MODULES, true);
+			if (ret)
+				return ret;
+		}
+
+		if (data->quirks->has_controller_power) {
+			ret = ayaneo_fw_attr_init(
+				data, AYANEO_ATTR_CONTROLLER_POWER, false);
+			if (ret)
+				return ret;
+		}
+	}
+
 	return 0;
 }
 
 static struct platform_driver ayaneo_platform_driver = {
 	.driver = {
-		.name = "ayaneo-ec",
+		.name = DRIVER_NAME,
 	},
 	.probe = ayaneo_ec_probe,
 };
-- 
2.50.1



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

* [PATCH v1 5/5] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec
  2025-08-20 16:06 [PATCH v1 0/5] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
                   ` (3 preceding siblings ...)
  2025-08-20 16:06 ` [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
@ 2025-08-20 16:06 ` Antheas Kapenekakis
  2025-08-23  0:07   ` Derek John Clark
  4 siblings, 1 reply; 15+ messages in thread
From: Antheas Kapenekakis @ 2025-08-20 16:06 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

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
Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
---
 drivers/platform/x86/Kconfig     |   4 +-
 drivers/platform/x86/ayaneo-ec.c |  66 ++++++++++++++++++
 drivers/platform/x86/oxpec.c     | 115 +------------------------------
 3 files changed, 68 insertions(+), 117 deletions(-)

diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index c871a722e5ef..06b53b0a3818 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -1031,9 +1031,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 eb7f9ae03b4f..d70fd0bd620e 100644
--- a/drivers/platform/x86/ayaneo-ec.c
+++ b/drivers/platform/x86/ayaneo-ec.c
@@ -83,6 +83,15 @@ struct ayaneo_fw_attr {
 	struct kobj_attribute current_value;
 };
 
+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 ayaneo3 = {
 	.has_fan_control = true,
 	.has_charge_control = true,
@@ -91,6 +100,63 @@ static const struct ayaneo_ec_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 eb076bb4099b..2074650f5ba0 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,
@@ -124,62 +114,6 @@ static const struct dmi_system_id dmi_table[] = {
 		},
 		.driver_data = (void *)aok_zoe_a1,
 	},
-	{
-		.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"),
@@ -658,13 +592,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:
@@ -685,14 +612,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:
@@ -713,14 +632,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:
@@ -760,14 +671,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:
@@ -796,14 +699,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] */
@@ -840,14 +735,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.50.1



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

* Re: [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-08-20 16:06 ` [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
@ 2025-08-21  8:41   ` kernel test robot
  2025-08-21  8:41   ` kernel test robot
  2025-08-22 12:40   ` Armin Wolf
  2 siblings, 0 replies; 15+ messages in thread
From: kernel test robot @ 2025-08-21  8:41 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: oe-kbuild-all, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck,
	Antheas Kapenekakis

Hi Antheas,

kernel test robot noticed the following build errors:

[auto build test ERROR on c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9]

url:    https://github.com/intel-lab-lkp/linux/commits/Antheas-Kapenekakis/platform-x86-ayaneo-ec-Add-Ayaneo-Embedded-Controller-platform-driver/20250821-002525
base:   c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9
patch link:    https://lore.kernel.org/r/20250820160628.99678-5-lkml%40antheas.dev
patch subject: [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes
config: i386-allmodconfig (https://download.01.org/0day-ci/archive/20250821/202508211626.9Cxkta6K-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14+deb12u1) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250821/202508211626.9Cxkta6K-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508211626.9Cxkta6K-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/platform/x86/ayaneo-ec.c: In function 'ayaneo_ec_probe':
>> drivers/platform/x86/ayaneo-ec.c:497:49: error: implicit declaration of function 'MKDEV' [-Werror=implicit-function-declaration]
     497 |                                                 MKDEV(0, 0), NULL, "%s",
         |                                                 ^~~~~
   cc1: some warnings being treated as errors


vim +/MKDEV +497 drivers/platform/x86/ayaneo-ec.c

   459	
   460	static int ayaneo_ec_probe(struct platform_device *pdev)
   461	{
   462		const struct dmi_system_id *dmi_entry;
   463		struct ayaneo_ec_platform_data *data;
   464		struct device *hwdev;
   465		int ret;
   466	
   467		dmi_entry = dmi_first_match(dmi_table);
   468		if (!dmi_entry)
   469			return -ENODEV;
   470	
   471		data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
   472		if (!data)
   473			return -ENOMEM;
   474	
   475		data->pdev = pdev;
   476		data->quirks = dmi_entry->driver_data;
   477		platform_set_drvdata(pdev, data);
   478	
   479		if (data->quirks->has_fan_control) {
   480			hwdev = devm_hwmon_device_register_with_info(
   481				&pdev->dev, "ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL);
   482			if (IS_ERR(hwdev))
   483				return PTR_ERR(hwdev);
   484		}
   485	
   486		if (data->quirks->has_charge_control) {
   487			data->battery_hook.add_battery = ayaneo_add_battery;
   488			data->battery_hook.remove_battery = ayaneo_remove_battery;
   489			data->battery_hook.name = "Ayaneo Battery";
   490			ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
   491			if (ret)
   492				return ret;
   493		}
   494	
   495		if (data->quirks->has_magic_modules || data->quirks->has_controller_power) {
   496			data->fw_attrs_dev = device_create(&firmware_attributes_class, NULL,
 > 497							MKDEV(0, 0), NULL, "%s",
   498							DRIVER_NAME);
   499			if (IS_ERR(data->fw_attrs_dev))
   500				return PTR_ERR(data->fw_attrs_dev);
   501	
   502			ret = devm_add_action_or_reset(&data->pdev->dev,
   503						ayaneo_fw_attrs_dev_unregister,
   504						data->fw_attrs_dev);
   505			if (ret)
   506				return ret;
   507	
   508			data->fw_attrs_kset = kset_create_and_add("attributes", NULL,
   509								&data->fw_attrs_dev->kobj);
   510			if (!data->fw_attrs_kset)
   511				return -ENOMEM;
   512	
   513			ret = devm_add_action_or_reset(&data->pdev->dev, ayaneo_kset_unregister,
   514						data->fw_attrs_kset);
   515	
   516			if (data->quirks->has_magic_modules) {
   517				ret = ayaneo_fw_attr_init(
   518					data, AYANEO_ATTR_CONTROLLER_MODULES, true);
   519				if (ret)
   520					return ret;
   521			}
   522	
   523			if (data->quirks->has_controller_power) {
   524				ret = ayaneo_fw_attr_init(
   525					data, AYANEO_ATTR_CONTROLLER_POWER, false);
   526				if (ret)
   527					return ret;
   528			}
   529		}
   530	
   531		return 0;
   532	}
   533	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-08-20 16:06 ` [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
  2025-08-21  8:41   ` kernel test robot
@ 2025-08-21  8:41   ` kernel test robot
  2025-08-22 12:40   ` Armin Wolf
  2 siblings, 0 replies; 15+ messages in thread
From: kernel test robot @ 2025-08-21  8:41 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: llvm, oe-kbuild-all, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck,
	Antheas Kapenekakis

Hi Antheas,

kernel test robot noticed the following build warnings:

[auto build test WARNING on c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9]

url:    https://github.com/intel-lab-lkp/linux/commits/Antheas-Kapenekakis/platform-x86-ayaneo-ec-Add-Ayaneo-Embedded-Controller-platform-driver/20250821-002525
base:   c17b750b3ad9f45f2b6f7e6f7f4679844244f0b9
patch link:    https://lore.kernel.org/r/20250820160628.99678-5-lkml%40antheas.dev
patch subject: [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes
config: x86_64-allyesconfig (https://download.01.org/0day-ci/archive/20250821/202508211640.8MtPueLL-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250821/202508211640.8MtPueLL-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202508211640.8MtPueLL-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/platform/x86/ayaneo-ec.c:395:12: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized]
     395 |                 else if (sysfs_streq(buf, "off"))
         |                          ^~~~~~~~~~~~~~~~~~~~~~~
   drivers/platform/x86/ayaneo-ec.c:397:7: note: uninitialized use occurs here
     397 |                 if (ret)
         |                     ^~~
   drivers/platform/x86/ayaneo-ec.c:395:8: note: remove the 'if' if its condition is always true
     395 |                 else if (sysfs_streq(buf, "off"))
         |                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
     396 |                         ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_OFF);
   drivers/platform/x86/ayaneo-ec.c:389:9: note: initialize the variable 'ret' to silence this warning
     389 |         int ret;
         |                ^
         |                 = 0
   1 warning generated.


vim +395 drivers/platform/x86/ayaneo-ec.c

   382	
   383	static ssize_t current_value_store(struct kobject *kobj,
   384					   struct kobj_attribute *attr, const char *buf,
   385					   size_t count)
   386	{
   387		struct ayaneo_fw_attr *fw_attr =
   388			container_of(attr, struct ayaneo_fw_attr, current_value);
   389		int ret;
   390	
   391		switch (fw_attr->fw_attr_id) {
   392		case AYANEO_ATTR_CONTROLLER_POWER:
   393			if (sysfs_streq(buf, "on"))
   394				ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_ON);
 > 395			else if (sysfs_streq(buf, "off"))
   396				ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_OFF);
   397			if (ret)
   398				return ret;
   399			return count;
   400		case AYANEO_ATTR_CONTROLLER_MODULES:
   401			return -EINVAL;
   402		}
   403		return -EINVAL;
   404	}
   405	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH v1 2/5] platform/x86: ayaneo-ec: Add hwmon support
  2025-08-20 16:06 ` [PATCH v1 2/5] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
@ 2025-08-22 12:35   ` Armin Wolf
  2025-08-22 20:56     ` Antheas Kapenekakis
  0 siblings, 1 reply; 15+ messages in thread
From: Armin Wolf @ 2025-08-22 12:35 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 20.08.25 um 18:06 schrieb Antheas Kapenekakis:

> Add hwmon single fan sensor reads and control for Ayaneo devices.
> The register and method of access is the same for all devices.
>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   drivers/platform/x86/Kconfig     |   2 +
>   drivers/platform/x86/ayaneo-ec.c | 132 +++++++++++++++++++++++++++++++
>   2 files changed, 134 insertions(+)
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 6d4a33791cc1..0a7ca2c78456 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -307,6 +307,8 @@ config ASUS_TF103C_DOCK
>   config AYANEO_EC
>   	tristate "Ayaneo EC platform control"
>   	depends on X86
> +	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 90b86527ab0d..8b1902706b81 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -7,13 +7,23 @@
>    * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
>    */
>   
> +#include <linux/acpi.h>
>   #include <linux/dmi.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 {
> @@ -22,6 +32,7 @@ struct ayaneo_ec_platform_data {
>   };
>   
>   static const struct ayaneo_ec_quirk ayaneo3 = {
> +	.has_fan_control = true,
>   };
>   
>   static const struct dmi_system_id dmi_table[] = {
> @@ -35,10 +46,124 @@ 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;
> +			*val = (255 * tmp) / 100;
> +			if (*val < 0 || *val > 255)
> +				return -EINVAL;
> +			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
> +				*val = 2;
> +			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:
> +			if (val == 1)
> +				return ec_write(AYANEO_PWM_ENABLE_REG,
> +						AYANEO_PWM_MODE_MANUAL);
> +			else if (val == 2)
> +				return ec_write(AYANEO_PWM_ENABLE_REG,
> +						AYANEO_PWM_MODE_AUTO);
> +			else
> +				return -EINVAL;

Consider using a switch statement here.

> +		case hwmon_pwm_input:
> +			if (val < 0 || val > 255)
> +				return -EINVAL;
> +			return ec_write(AYANEO_PWM_REG, (val * 100) / 255);

Better use fixp_linear_interpolate() for that. Also you need to restore those registers
after suspend. I suggest that you use regmap for that.

Thanks,
Armin Wolf

> +		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)
> @@ -52,6 +177,13 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>   	data->quirks = dmi_entry->driver_data;
>   	platform_set_drvdata(pdev, data);
>   
> +	if (data->quirks->has_fan_control) {
> +		hwdev = devm_hwmon_device_register_with_info(
> +			&pdev->dev, "ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL);
> +		if (IS_ERR(hwdev))
> +			return PTR_ERR(hwdev);
> +	}
> +
>   	return 0;
>   }
>   

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

* Re: [PATCH v1 3/5] platform/x86: ayaneo-ec: Add charge control support
  2025-08-20 16:06 ` [PATCH v1 3/5] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
@ 2025-08-22 12:38   ` Armin Wolf
  2025-08-22 21:04     ` Antheas Kapenekakis
  0 siblings, 1 reply; 15+ messages in thread
From: Armin Wolf @ 2025-08-22 12:38 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 20.08.25 um 18:06 schrieb Antheas Kapenekakis:

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

Does this include hibernation?

Thanks,
Armin Wolf

> The EC does not support charge threshold control. Instead, userspace
> software on Windows manually toggles charge inhibition depending on
> battery level.
>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   drivers/platform/x86/Kconfig     |   1 +
>   drivers/platform/x86/ayaneo-ec.c | 111 +++++++++++++++++++++++++++++++
>   2 files changed, 112 insertions(+)
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index 0a7ca2c78456..c871a722e5ef 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -308,6 +308,7 @@ config AYANEO_EC
>   	tristate "Ayaneo EC platform control"
>   	depends on X86
>   	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 8b1902706b81..a4bdc6ae7af7 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -14,6 +14,7 @@
>   #include <linux/kernel.h>
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
> +#include <acpi/battery.h>
>   
>   #define AYANEO_PWM_ENABLE_REG	 0x4A
>   #define AYANEO_PWM_REG		 0x4B
> @@ -22,17 +23,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 ayaneo3 = {
>   	.has_fan_control = true,
> +	.has_charge_control = true,
>   };
>   
>   static const struct dmi_system_id dmi_table[] = {
> @@ -159,11 +170,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)
> @@ -184,6 +286,15 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>   			return PTR_ERR(hwdev);
>   	}
>   
> +	if (data->quirks->has_charge_control) {
> +		data->battery_hook.add_battery = ayaneo_add_battery;
> +		data->battery_hook.remove_battery = ayaneo_remove_battery;
> +		data->battery_hook.name = "Ayaneo Battery";
> +		ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
> +		if (ret)
> +			return ret;
> +	}
> +
>   	return 0;
>   }
>   

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

* Re: [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes
  2025-08-20 16:06 ` [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
  2025-08-21  8:41   ` kernel test robot
  2025-08-21  8:41   ` kernel test robot
@ 2025-08-22 12:40   ` Armin Wolf
  2025-08-22 21:08     ` Antheas Kapenekakis
  2 siblings, 1 reply; 15+ messages in thread
From: Armin Wolf @ 2025-08-22 12:40 UTC (permalink / raw)
  To: Antheas Kapenekakis, platform-driver-x86
  Cc: linux-kernel, linux-hwmon, Hans de Goede, Ilpo Järvinen,
	Derek John Clark, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

Am 20.08.25 um 18:06 schrieb Antheas Kapenekakis:

> The Ayaneo 3 features hot-swappable controller modules. The ejection
> and management is done through HID. However, after ejecting the modules,
> the controller needs to be power cycled via the EC to re-initialize.
>
> For this, the EC provides a variable that holds whether the left or
> right modules are connected, and a power control register to turn
> the controller on or off. After ejecting the modules, the controller
> should be turned off. Then, after both modules are reinserted,
> the controller may be powered on again to re-initialize.
>
> This patch introduces two new firmware 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 'on'/'off').
>
> 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.

I do not think that those controls should be exposed as firmware attributes,
as they are live values not persistent across power cycles. Better use
sysfs attributes instead.

Thanks,
Armin Wolf

>
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>   drivers/platform/x86/ayaneo-ec.c | 235 ++++++++++++++++++++++++++++++-
>   1 file changed, 234 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> index a4bdc6ae7af7..eb7f9ae03b4f 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -16,6 +16,10 @@
>   #include <linux/platform_device.h>
>   #include <acpi/battery.h>
>   
> +#include "firmware_attributes_class.h"
> +
> +#define DRIVER_NAME "ayaneo-ec"
> +
>   #define AYANEO_PWM_ENABLE_REG	 0x4A
>   #define AYANEO_PWM_REG		 0x4B
>   #define AYANEO_PWM_MODE_AUTO	 0x00
> @@ -30,20 +34,60 @@
>   #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)
> +
> +enum ayaneo_fw_attr_id {
> +	AYANEO_ATTR_CONTROLLER_MODULES,
> +	AYANEO_ATTR_CONTROLLER_POWER,
> +};
> +
> +static const char *const ayaneo_fw_attr_name[] = {
> +	[AYANEO_ATTR_CONTROLLER_MODULES] = "controller_modules",
> +	[AYANEO_ATTR_CONTROLLER_POWER] = "controller_power",
> +};
> +
> +static const char *const ayaneo_fw_attr_desc[] = {
> +	[AYANEO_ATTR_CONTROLLER_MODULES] =
> +		"Which controller Magic Modules are connected (none, left, right, both)",
> +	[AYANEO_ATTR_CONTROLLER_POWER] = "Controller power state (on, off)",
> +};
> +
> +#define AYANEO_ATTR_ENUM_MAX_ATTRS 7
> +#define AYANEO_ATTR_LANGUAGE_CODE "en_US.UTF-8"
> +
>   struct ayaneo_ec_quirk {
>   	bool has_fan_control;
>   	bool has_charge_control;
> +	bool has_magic_modules;
> +	bool has_controller_power;
>   };
>   
>   struct ayaneo_ec_platform_data {
>   	struct platform_device *pdev;
>   	struct ayaneo_ec_quirk *quirks;
>   	struct acpi_battery_hook battery_hook;
> +	struct device *fw_attrs_dev;
> +	struct kset *fw_attrs_kset;
> +};
> +
> +struct ayaneo_fw_attr {
> +	struct ayaneo_ec_platform_data *data;
> +	enum ayaneo_fw_attr_id fw_attr_id;
> +	struct attribute_group attr_group;
> +	struct kobj_attribute display_name;
> +	struct kobj_attribute current_value;
>   };
>   
>   static const struct ayaneo_ec_quirk ayaneo3 = {
>   	.has_fan_control = true,
>   	.has_charge_control = true,
> +	.has_magic_modules = true,
> +	.has_controller_power = true,
>   };
>   
>   static const struct dmi_system_id dmi_table[] = {
> @@ -260,6 +304,159 @@ static int ayaneo_remove_battery(struct power_supply *battery,
>   	return 0;
>   }
>   
> +static void ayaneo_kset_unregister(void *data)
> +{
> +	struct kset *kset = data;
> +
> +	kset_unregister(kset);
> +}
> +
> +static void ayaneo_fw_attrs_dev_unregister(void *data)
> +{
> +	struct device *fw_attrs_dev = data;
> +
> +	device_unregister(fw_attrs_dev);
> +}
> +
> +static ssize_t display_name_language_code_show(struct kobject *kobj,
> +					       struct kobj_attribute *attr,
> +					       char *buf)
> +{
> +	return sysfs_emit(buf, "%s\n", AYANEO_ATTR_LANGUAGE_CODE);
> +}
> +
> +static struct kobj_attribute fw_attr_display_name_language_code =
> +	__ATTR_RO(display_name_language_code);
> +
> +static ssize_t display_name_show(struct kobject *kobj,
> +				 struct kobj_attribute *attr, char *buf)
> +{
> +	struct ayaneo_fw_attr *fw_attr =
> +		container_of(attr, struct ayaneo_fw_attr, display_name);
> +
> +	return sysfs_emit(buf, "%s\n", ayaneo_fw_attr_desc[fw_attr->fw_attr_id]);
> +}
> +
> +static ssize_t current_value_show(struct kobject *kobj,
> +				  struct kobj_attribute *attr, char *buf)
> +{
> +	struct ayaneo_fw_attr *fw_attr =
> +		container_of(attr, struct ayaneo_fw_attr, current_value);
> +	bool left, right;
> +	char *out;
> +	int ret;
> +	u8 tmp;
> +
> +	switch (fw_attr->fw_attr_id) {
> +	case AYANEO_ATTR_CONTROLLER_MODULES:
> +		ret = ec_read(AYANEO_MODULE_REG, &tmp);
> +		if (ret)
> +			return ret;
> +		left = !(tmp & AYANEO_MODULE_LEFT);
> +		right = !(tmp & AYANEO_MODULE_RIGHT);
> +
> +		if (left && right)
> +			out = "both";
> +		else if (left)
> +			out = "left";
> +		else if (right)
> +			out = "right";
> +		else
> +			out = "none";
> +
> +		return sysfs_emit(buf, "%s\n", out);
> +	case AYANEO_ATTR_CONTROLLER_POWER:
> +		ret = ec_read(AYANEO_POWER_REG, &tmp);
> +		if (ret)
> +			return ret;
> +
> +		if (tmp == AYANEO_POWER_OFF)
> +			out = "off";
> +		else
> +			out = "on";
> +
> +		return sysfs_emit(buf, "%s\n", out);
> +	}
> +	return -EINVAL;
> +}
> +
> +static ssize_t current_value_store(struct kobject *kobj,
> +				   struct kobj_attribute *attr, const char *buf,
> +				   size_t count)
> +{
> +	struct ayaneo_fw_attr *fw_attr =
> +		container_of(attr, struct ayaneo_fw_attr, current_value);
> +	int ret;
> +
> +	switch (fw_attr->fw_attr_id) {
> +	case AYANEO_ATTR_CONTROLLER_POWER:
> +		if (sysfs_streq(buf, "on"))
> +			ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_ON);
> +		else if (sysfs_streq(buf, "off"))
> +			ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_OFF);
> +		if (ret)
> +			return ret;
> +		return count;
> +	case AYANEO_ATTR_CONTROLLER_MODULES:
> +		return -EINVAL;
> +	}
> +	return -EINVAL;
> +}
> +
> +static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
> +			 char *buf)
> +{
> +	return sysfs_emit(buf, "string\n");
> +}
> +
> +static struct kobj_attribute fw_attr_type_string = {
> +	.attr = { .name = "type", .mode = 0444 },
> +	.show = type_show,
> +};
> +
> +static int ayaneo_fw_attr_init(struct ayaneo_ec_platform_data *data,
> +			       const enum ayaneo_fw_attr_id fw_attr_id,
> +			       bool read_only)
> +{
> +	struct ayaneo_fw_attr *fw_attr;
> +	struct attribute **attrs;
> +	int idx = 0;
> +
> +	fw_attr = devm_kzalloc(&data->pdev->dev, sizeof(*fw_attr), GFP_KERNEL);
> +	if (!fw_attr)
> +		return -ENOMEM;
> +
> +	attrs = devm_kcalloc(&data->pdev->dev, AYANEO_ATTR_ENUM_MAX_ATTRS + 1,
> +			     sizeof(*attrs), GFP_KERNEL);
> +	if (!attrs)
> +		return -ENOMEM;
> +
> +	fw_attr->data = data;
> +	fw_attr->fw_attr_id = fw_attr_id;
> +	fw_attr->attr_group.name = ayaneo_fw_attr_name[fw_attr_id];
> +	fw_attr->attr_group.attrs = attrs;
> +
> +	attrs[idx++] = &fw_attr_type_string.attr;
> +	attrs[idx++] = &fw_attr_display_name_language_code.attr;
> +
> +	sysfs_attr_init(&fw_attr->display_name.attr);
> +	fw_attr->display_name.attr.name = "display_name";
> +	fw_attr->display_name.attr.mode = 0444;
> +	fw_attr->display_name.show = display_name_show;
> +	attrs[idx++] = &fw_attr->display_name.attr;
> +
> +	sysfs_attr_init(&fw_attr->current_value.attr);
> +	fw_attr->current_value.attr.name = "current_value";
> +	fw_attr->current_value.attr.mode = read_only ? 0444 : 0644;
> +	fw_attr->current_value.show = current_value_show;
> +	fw_attr->current_value.store = current_value_store;
> +	attrs[idx++] = &fw_attr->current_value.attr;
> +
> +	attrs[idx] = NULL;
> +	return sysfs_create_group(&data->fw_attrs_kset->kobj,
> +				  &fw_attr->attr_group);
> +}
> +
>   static int ayaneo_ec_probe(struct platform_device *pdev)
>   {
>   	const struct dmi_system_id *dmi_entry;
> @@ -295,12 +492,48 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
>   			return ret;
>   	}
>   
> +	if (data->quirks->has_magic_modules || data->quirks->has_controller_power) {
> +		data->fw_attrs_dev = device_create(&firmware_attributes_class, NULL,
> +						MKDEV(0, 0), NULL, "%s",
> +						DRIVER_NAME);
> +		if (IS_ERR(data->fw_attrs_dev))
> +			return PTR_ERR(data->fw_attrs_dev);
> +
> +		ret = devm_add_action_or_reset(&data->pdev->dev,
> +					ayaneo_fw_attrs_dev_unregister,
> +					data->fw_attrs_dev);
> +		if (ret)
> +			return ret;
> +
> +		data->fw_attrs_kset = kset_create_and_add("attributes", NULL,
> +							&data->fw_attrs_dev->kobj);
> +		if (!data->fw_attrs_kset)
> +			return -ENOMEM;
> +
> +		ret = devm_add_action_or_reset(&data->pdev->dev, ayaneo_kset_unregister,
> +					data->fw_attrs_kset);
> +
> +		if (data->quirks->has_magic_modules) {
> +			ret = ayaneo_fw_attr_init(
> +				data, AYANEO_ATTR_CONTROLLER_MODULES, true);
> +			if (ret)
> +				return ret;
> +		}
> +
> +		if (data->quirks->has_controller_power) {
> +			ret = ayaneo_fw_attr_init(
> +				data, AYANEO_ATTR_CONTROLLER_POWER, false);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
>   	return 0;
>   }
>   
>   static struct platform_driver ayaneo_platform_driver = {
>   	.driver = {
> -		.name = "ayaneo-ec",
> +		.name = DRIVER_NAME,
>   	},
>   	.probe = ayaneo_ec_probe,
>   };

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

* Re: [PATCH v1 2/5] platform/x86: ayaneo-ec: Add hwmon support
  2025-08-22 12:35   ` Armin Wolf
@ 2025-08-22 20:56     ` Antheas Kapenekakis
  0 siblings, 0 replies; 15+ messages in thread
From: Antheas Kapenekakis @ 2025-08-22 20:56 UTC (permalink / raw)
  To: Armin Wolf
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

On Fri, 22 Aug 2025 at 14:35, Armin Wolf <W_Armin@gmx.de> wrote:
>
> Am 20.08.25 um 18:06 schrieb Antheas Kapenekakis:
>
> > Add hwmon single fan sensor reads and control for Ayaneo devices.
> > The register and method of access is the same for all devices.
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> >   drivers/platform/x86/Kconfig     |   2 +
> >   drivers/platform/x86/ayaneo-ec.c | 132 +++++++++++++++++++++++++++++++
> >   2 files changed, 134 insertions(+)
> >
> > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> > index 6d4a33791cc1..0a7ca2c78456 100644
> > --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -307,6 +307,8 @@ config ASUS_TF103C_DOCK
> >   config AYANEO_EC
> >       tristate "Ayaneo EC platform control"
> >       depends on X86
> > +     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 90b86527ab0d..8b1902706b81 100644
> > --- a/drivers/platform/x86/ayaneo-ec.c
> > +++ b/drivers/platform/x86/ayaneo-ec.c
> > @@ -7,13 +7,23 @@
> >    * Copyright (C) 2025 Antheas Kapenekakis <lkml@antheas.dev>
> >    */
> >
> > +#include <linux/acpi.h>
> >   #include <linux/dmi.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 {
> > @@ -22,6 +32,7 @@ struct ayaneo_ec_platform_data {
> >   };
> >
> >   static const struct ayaneo_ec_quirk ayaneo3 = {
> > +     .has_fan_control = true,
> >   };
> >
> >   static const struct dmi_system_id dmi_table[] = {
> > @@ -35,10 +46,124 @@ 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;
> > +                     *val = (255 * tmp) / 100;
> > +                     if (*val < 0 || *val > 255)
> > +                             return -EINVAL;
> > +                     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
> > +                             *val = 2;
> > +                     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:
> > +                     if (val == 1)
> > +                             return ec_write(AYANEO_PWM_ENABLE_REG,
> > +                                             AYANEO_PWM_MODE_MANUAL);
> > +                     else if (val == 2)
> > +                             return ec_write(AYANEO_PWM_ENABLE_REG,
> > +                                             AYANEO_PWM_MODE_AUTO);
> > +                     else
> > +                             return -EINVAL;
>
> Consider using a switch statement here.
>
> > +             case hwmon_pwm_input:
> > +                     if (val < 0 || val > 255)
> > +                             return -EINVAL;
> > +                     return ec_write(AYANEO_PWM_REG, (val * 100) / 255);
>
> Better use fixp_linear_interpolate() for that. Also you need to restore those registers
> after suspend. I suggest that you use regmap for that.

The registers appear to maintain their state after sleep. If I get a
report that some devices do not, I will amend the driver with a
suspend hook.

ack on the interpolate.

Antheas

> Thanks,
> Armin Wolf
>
> > +             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)
> > @@ -52,6 +177,13 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >       data->quirks = dmi_entry->driver_data;
> >       platform_set_drvdata(pdev, data);
> >
> > +     if (data->quirks->has_fan_control) {
> > +             hwdev = devm_hwmon_device_register_with_info(
> > +                     &pdev->dev, "ayaneo_ec", NULL, &ayaneo_ec_chip_info, NULL);
> > +             if (IS_ERR(hwdev))
> > +                     return PTR_ERR(hwdev);
> > +     }
> > +
> >       return 0;
> >   }
> >
>


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

* Re: [PATCH v1 3/5] platform/x86: ayaneo-ec: Add charge control support
  2025-08-22 12:38   ` Armin Wolf
@ 2025-08-22 21:04     ` Antheas Kapenekakis
  0 siblings, 0 replies; 15+ messages in thread
From: Antheas Kapenekakis @ 2025-08-22 21:04 UTC (permalink / raw)
  To: Armin Wolf
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Derek John Clark,
	Joaquín Ignacio Aramendía, Jean Delvare, Guenter Roeck

On Fri, 22 Aug 2025 at 14:38, Armin Wolf <W_Armin@gmx.de> wrote:
>
> Am 20.08.25 um 18:06 schrieb Antheas Kapenekakis:
>
> > Ayaneo devices support charge inhibition via the EC. This inhibition
> > only works while the device is powered on, and resets between restarts.
> > However, it is maintained across suspend/resume cycles.
>
> Does this include hibernation?

No. S4/S5 reset the state. Can you give a reference for a regmap
implementation? A hibernation hook would be a nice to have. Although I
am not sure how I feel about restoring the fan speed after
hibernation. It seems dangerous.

> Thanks,
> Armin Wolf
>
> > The EC does not support charge threshold control. Instead, userspace
> > software on Windows manually toggles charge inhibition depending on
> > battery level.
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> >   drivers/platform/x86/Kconfig     |   1 +
> >   drivers/platform/x86/ayaneo-ec.c | 111 +++++++++++++++++++++++++++++++
> >   2 files changed, 112 insertions(+)
> >
> > diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> > index 0a7ca2c78456..c871a722e5ef 100644
> > --- a/drivers/platform/x86/Kconfig
> > +++ b/drivers/platform/x86/Kconfig
> > @@ -308,6 +308,7 @@ config AYANEO_EC
> >       tristate "Ayaneo EC platform control"
> >       depends on X86
> >       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 8b1902706b81..a4bdc6ae7af7 100644
> > --- a/drivers/platform/x86/ayaneo-ec.c
> > +++ b/drivers/platform/x86/ayaneo-ec.c
> > @@ -14,6 +14,7 @@
> >   #include <linux/kernel.h>
> >   #include <linux/module.h>
> >   #include <linux/platform_device.h>
> > +#include <acpi/battery.h>
> >
> >   #define AYANEO_PWM_ENABLE_REG        0x4A
> >   #define AYANEO_PWM_REG               0x4B
> > @@ -22,17 +23,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 ayaneo3 = {
> >       .has_fan_control = true,
> > +     .has_charge_control = true,
> >   };
> >
> >   static const struct dmi_system_id dmi_table[] = {
> > @@ -159,11 +170,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)
> > @@ -184,6 +286,15 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >                       return PTR_ERR(hwdev);
> >       }
> >
> > +     if (data->quirks->has_charge_control) {
> > +             data->battery_hook.add_battery = ayaneo_add_battery;
> > +             data->battery_hook.remove_battery = ayaneo_remove_battery;
> > +             data->battery_hook.name = "Ayaneo Battery";
> > +             ret = devm_battery_hook_register(&pdev->dev, &data->battery_hook);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> >       return 0;
> >   }
> >
>


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

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

On Fri, 22 Aug 2025 at 14:41, Armin Wolf <W_Armin@gmx.de> wrote:
>
> Am 20.08.25 um 18:06 schrieb Antheas Kapenekakis:
>
> > The Ayaneo 3 features hot-swappable controller modules. The ejection
> > and management is done through HID. However, after ejecting the modules,
> > the controller needs to be power cycled via the EC to re-initialize.
> >
> > For this, the EC provides a variable that holds whether the left or
> > right modules are connected, and a power control register to turn
> > the controller on or off. After ejecting the modules, the controller
> > should be turned off. Then, after both modules are reinserted,
> > the controller may be powered on again to re-initialize.
> >
> > This patch introduces two new firmware 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 'on'/'off').
> >
> > 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.
>
> I do not think that those controls should be exposed as firmware attributes,
> as they are live values not persistent across power cycles. Better use
> sysfs attributes instead.

Isn't there precedent for this with SPL/SPPT/FPPT? I remember having a
long form discussion with you about how this interface is appropriate
for this, which is why I used it here.

The controller_modules attribute is read only and reports which
modules are connected, which is somewhat useful. As for power, it can
likewise be used by users to toggle the controller on and off. Indeed,
the attribute is not long lasting though.

Best,
Antheas

> Thanks,
> Armin Wolf
>
> >
> > Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> > ---
> >   drivers/platform/x86/ayaneo-ec.c | 235 ++++++++++++++++++++++++++++++-
> >   1 file changed, 234 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/platform/x86/ayaneo-ec.c b/drivers/platform/x86/ayaneo-ec.c
> > index a4bdc6ae7af7..eb7f9ae03b4f 100644
> > --- a/drivers/platform/x86/ayaneo-ec.c
> > +++ b/drivers/platform/x86/ayaneo-ec.c
> > @@ -16,6 +16,10 @@
> >   #include <linux/platform_device.h>
> >   #include <acpi/battery.h>
> >
> > +#include "firmware_attributes_class.h"
> > +
> > +#define DRIVER_NAME "ayaneo-ec"
> > +
> >   #define AYANEO_PWM_ENABLE_REG        0x4A
> >   #define AYANEO_PWM_REG               0x4B
> >   #define AYANEO_PWM_MODE_AUTO         0x00
> > @@ -30,20 +34,60 @@
> >   #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)
> > +
> > +enum ayaneo_fw_attr_id {
> > +     AYANEO_ATTR_CONTROLLER_MODULES,
> > +     AYANEO_ATTR_CONTROLLER_POWER,
> > +};
> > +
> > +static const char *const ayaneo_fw_attr_name[] = {
> > +     [AYANEO_ATTR_CONTROLLER_MODULES] = "controller_modules",
> > +     [AYANEO_ATTR_CONTROLLER_POWER] = "controller_power",
> > +};
> > +
> > +static const char *const ayaneo_fw_attr_desc[] = {
> > +     [AYANEO_ATTR_CONTROLLER_MODULES] =
> > +             "Which controller Magic Modules are connected (none, left, right, both)",
> > +     [AYANEO_ATTR_CONTROLLER_POWER] = "Controller power state (on, off)",
> > +};
> > +
> > +#define AYANEO_ATTR_ENUM_MAX_ATTRS 7
> > +#define AYANEO_ATTR_LANGUAGE_CODE "en_US.UTF-8"
> > +
> >   struct ayaneo_ec_quirk {
> >       bool has_fan_control;
> >       bool has_charge_control;
> > +     bool has_magic_modules;
> > +     bool has_controller_power;
> >   };
> >
> >   struct ayaneo_ec_platform_data {
> >       struct platform_device *pdev;
> >       struct ayaneo_ec_quirk *quirks;
> >       struct acpi_battery_hook battery_hook;
> > +     struct device *fw_attrs_dev;
> > +     struct kset *fw_attrs_kset;
> > +};
> > +
> > +struct ayaneo_fw_attr {
> > +     struct ayaneo_ec_platform_data *data;
> > +     enum ayaneo_fw_attr_id fw_attr_id;
> > +     struct attribute_group attr_group;
> > +     struct kobj_attribute display_name;
> > +     struct kobj_attribute current_value;
> >   };
> >
> >   static const struct ayaneo_ec_quirk ayaneo3 = {
> >       .has_fan_control = true,
> >       .has_charge_control = true,
> > +     .has_magic_modules = true,
> > +     .has_controller_power = true,
> >   };
> >
> >   static const struct dmi_system_id dmi_table[] = {
> > @@ -260,6 +304,159 @@ static int ayaneo_remove_battery(struct power_supply *battery,
> >       return 0;
> >   }
> >
> > +static void ayaneo_kset_unregister(void *data)
> > +{
> > +     struct kset *kset = data;
> > +
> > +     kset_unregister(kset);
> > +}
> > +
> > +static void ayaneo_fw_attrs_dev_unregister(void *data)
> > +{
> > +     struct device *fw_attrs_dev = data;
> > +
> > +     device_unregister(fw_attrs_dev);
> > +}
> > +
> > +static ssize_t display_name_language_code_show(struct kobject *kobj,
> > +                                            struct kobj_attribute *attr,
> > +                                            char *buf)
> > +{
> > +     return sysfs_emit(buf, "%s\n", AYANEO_ATTR_LANGUAGE_CODE);
> > +}
> > +
> > +static struct kobj_attribute fw_attr_display_name_language_code =
> > +     __ATTR_RO(display_name_language_code);
> > +
> > +static ssize_t display_name_show(struct kobject *kobj,
> > +                              struct kobj_attribute *attr, char *buf)
> > +{
> > +     struct ayaneo_fw_attr *fw_attr =
> > +             container_of(attr, struct ayaneo_fw_attr, display_name);
> > +
> > +     return sysfs_emit(buf, "%s\n", ayaneo_fw_attr_desc[fw_attr->fw_attr_id]);
> > +}
> > +
> > +static ssize_t current_value_show(struct kobject *kobj,
> > +                               struct kobj_attribute *attr, char *buf)
> > +{
> > +     struct ayaneo_fw_attr *fw_attr =
> > +             container_of(attr, struct ayaneo_fw_attr, current_value);
> > +     bool left, right;
> > +     char *out;
> > +     int ret;
> > +     u8 tmp;
> > +
> > +     switch (fw_attr->fw_attr_id) {
> > +     case AYANEO_ATTR_CONTROLLER_MODULES:
> > +             ret = ec_read(AYANEO_MODULE_REG, &tmp);
> > +             if (ret)
> > +                     return ret;
> > +             left = !(tmp & AYANEO_MODULE_LEFT);
> > +             right = !(tmp & AYANEO_MODULE_RIGHT);
> > +
> > +             if (left && right)
> > +                     out = "both";
> > +             else if (left)
> > +                     out = "left";
> > +             else if (right)
> > +                     out = "right";
> > +             else
> > +                     out = "none";
> > +
> > +             return sysfs_emit(buf, "%s\n", out);
> > +     case AYANEO_ATTR_CONTROLLER_POWER:
> > +             ret = ec_read(AYANEO_POWER_REG, &tmp);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             if (tmp == AYANEO_POWER_OFF)
> > +                     out = "off";
> > +             else
> > +                     out = "on";
> > +
> > +             return sysfs_emit(buf, "%s\n", out);
> > +     }
> > +     return -EINVAL;
> > +}
> > +
> > +static ssize_t current_value_store(struct kobject *kobj,
> > +                                struct kobj_attribute *attr, const char *buf,
> > +                                size_t count)
> > +{
> > +     struct ayaneo_fw_attr *fw_attr =
> > +             container_of(attr, struct ayaneo_fw_attr, current_value);
> > +     int ret;
> > +
> > +     switch (fw_attr->fw_attr_id) {
> > +     case AYANEO_ATTR_CONTROLLER_POWER:
> > +             if (sysfs_streq(buf, "on"))
> > +                     ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_ON);
> > +             else if (sysfs_streq(buf, "off"))
> > +                     ret = ec_write(AYANEO_POWER_REG, AYANEO_POWER_OFF);
> > +             if (ret)
> > +                     return ret;
> > +             return count;
> > +     case AYANEO_ATTR_CONTROLLER_MODULES:
> > +             return -EINVAL;
> > +     }
> > +     return -EINVAL;
> > +}
> > +
> > +static ssize_t type_show(struct kobject *kobj, struct kobj_attribute *attr,
> > +                      char *buf)
> > +{
> > +     return sysfs_emit(buf, "string\n");
> > +}
> > +
> > +static struct kobj_attribute fw_attr_type_string = {
> > +     .attr = { .name = "type", .mode = 0444 },
> > +     .show = type_show,
> > +};
> > +
> > +static int ayaneo_fw_attr_init(struct ayaneo_ec_platform_data *data,
> > +                            const enum ayaneo_fw_attr_id fw_attr_id,
> > +                            bool read_only)
> > +{
> > +     struct ayaneo_fw_attr *fw_attr;
> > +     struct attribute **attrs;
> > +     int idx = 0;
> > +
> > +     fw_attr = devm_kzalloc(&data->pdev->dev, sizeof(*fw_attr), GFP_KERNEL);
> > +     if (!fw_attr)
> > +             return -ENOMEM;
> > +
> > +     attrs = devm_kcalloc(&data->pdev->dev, AYANEO_ATTR_ENUM_MAX_ATTRS + 1,
> > +                          sizeof(*attrs), GFP_KERNEL);
> > +     if (!attrs)
> > +             return -ENOMEM;
> > +
> > +     fw_attr->data = data;
> > +     fw_attr->fw_attr_id = fw_attr_id;
> > +     fw_attr->attr_group.name = ayaneo_fw_attr_name[fw_attr_id];
> > +     fw_attr->attr_group.attrs = attrs;
> > +
> > +     attrs[idx++] = &fw_attr_type_string.attr;
> > +     attrs[idx++] = &fw_attr_display_name_language_code.attr;
> > +
> > +     sysfs_attr_init(&fw_attr->display_name.attr);
> > +     fw_attr->display_name.attr.name = "display_name";
> > +     fw_attr->display_name.attr.mode = 0444;
> > +     fw_attr->display_name.show = display_name_show;
> > +     attrs[idx++] = &fw_attr->display_name.attr;
> > +
> > +     sysfs_attr_init(&fw_attr->current_value.attr);
> > +     fw_attr->current_value.attr.name = "current_value";
> > +     fw_attr->current_value.attr.mode = read_only ? 0444 : 0644;
> > +     fw_attr->current_value.show = current_value_show;
> > +     fw_attr->current_value.store = current_value_store;
> > +     attrs[idx++] = &fw_attr->current_value.attr;
> > +
> > +     attrs[idx] = NULL;
> > +     return sysfs_create_group(&data->fw_attrs_kset->kobj,
> > +                               &fw_attr->attr_group);
> > +}
> > +
> >   static int ayaneo_ec_probe(struct platform_device *pdev)
> >   {
> >       const struct dmi_system_id *dmi_entry;
> > @@ -295,12 +492,48 @@ static int ayaneo_ec_probe(struct platform_device *pdev)
> >                       return ret;
> >       }
> >
> > +     if (data->quirks->has_magic_modules || data->quirks->has_controller_power) {
> > +             data->fw_attrs_dev = device_create(&firmware_attributes_class, NULL,
> > +                                             MKDEV(0, 0), NULL, "%s",
> > +                                             DRIVER_NAME);
> > +             if (IS_ERR(data->fw_attrs_dev))
> > +                     return PTR_ERR(data->fw_attrs_dev);
> > +
> > +             ret = devm_add_action_or_reset(&data->pdev->dev,
> > +                                     ayaneo_fw_attrs_dev_unregister,
> > +                                     data->fw_attrs_dev);
> > +             if (ret)
> > +                     return ret;
> > +
> > +             data->fw_attrs_kset = kset_create_and_add("attributes", NULL,
> > +                                                     &data->fw_attrs_dev->kobj);
> > +             if (!data->fw_attrs_kset)
> > +                     return -ENOMEM;
> > +
> > +             ret = devm_add_action_or_reset(&data->pdev->dev, ayaneo_kset_unregister,
> > +                                     data->fw_attrs_kset);
> > +
> > +             if (data->quirks->has_magic_modules) {
> > +                     ret = ayaneo_fw_attr_init(
> > +                             data, AYANEO_ATTR_CONTROLLER_MODULES, true);
> > +                     if (ret)
> > +                             return ret;
> > +             }
> > +
> > +             if (data->quirks->has_controller_power) {
> > +                     ret = ayaneo_fw_attr_init(
> > +                             data, AYANEO_ATTR_CONTROLLER_POWER, false);
> > +                     if (ret)
> > +                             return ret;
> > +             }
> > +     }
> > +
> >       return 0;
> >   }
> >
> >   static struct platform_driver ayaneo_platform_driver = {
> >       .driver = {
> > -             .name = "ayaneo-ec",
> > +             .name = DRIVER_NAME,
> >       },
> >       .probe = ayaneo_ec_probe,
> >   };
>


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

* Re: [PATCH v1 5/5] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec
  2025-08-20 16:06 ` [PATCH v1 5/5] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
@ 2025-08-23  0:07   ` Derek John Clark
  0 siblings, 0 replies; 15+ messages in thread
From: Derek John Clark @ 2025-08-23  0:07 UTC (permalink / raw)
  To: Antheas Kapenekakis
  Cc: platform-driver-x86, linux-kernel, linux-hwmon, Hans de Goede,
	Ilpo Järvinen, Joaquín Ignacio Aramendía,
	Jean Delvare, Guenter Roeck

On Wed, Aug 20, 2025 at 9:06 AM Antheas Kapenekakis <lkml@antheas.dev> wrote:
>
> Currently, the oxpec driver contains Ayaneo devices. Move them to the
> new ayaneo-ec driver, which is dedicated to them.
>
Hi Antheas,

Fan control is still functional under the ayaneo_ec driver in hwmon
with this patch. Tested on my AYANEO 2S.

> 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.
>

Works on my AYANEO AIR.

Tested-by: Derek J. Clark <derekjohn.clark@gmail.com>

Cheers,
Derek

> In addition, update the readmes of oxpec to reflect this change.
>
> Link: https://github.com/ShadowBlip/ayaneo-platform
> Signed-off-by: Antheas Kapenekakis <lkml@antheas.dev>
> ---
>  drivers/platform/x86/Kconfig     |   4 +-
>  drivers/platform/x86/ayaneo-ec.c |  66 ++++++++++++++++++
>  drivers/platform/x86/oxpec.c     | 115 +------------------------------
>  3 files changed, 68 insertions(+), 117 deletions(-)
>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index c871a722e5ef..06b53b0a3818 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -1031,9 +1031,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 eb7f9ae03b4f..d70fd0bd620e 100644
> --- a/drivers/platform/x86/ayaneo-ec.c
> +++ b/drivers/platform/x86/ayaneo-ec.c
> @@ -83,6 +83,15 @@ struct ayaneo_fw_attr {
>         struct kobj_attribute current_value;
>  };
>
> +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 ayaneo3 = {
>         .has_fan_control = true,
>         .has_charge_control = true,
> @@ -91,6 +100,63 @@ static const struct ayaneo_ec_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 eb076bb4099b..2074650f5ba0 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,
> @@ -124,62 +114,6 @@ static const struct dmi_system_id dmi_table[] = {
>                 },
>                 .driver_data = (void *)aok_zoe_a1,
>         },
> -       {
> -               .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"),
> @@ -658,13 +592,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:
> @@ -685,14 +612,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:
> @@ -713,14 +632,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:
> @@ -760,14 +671,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:
> @@ -796,14 +699,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] */
> @@ -840,14 +735,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.50.1
>
>

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

end of thread, other threads:[~2025-08-23  0:07 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-20 16:06 [PATCH v1 0/5] platform/x86: ayaneo-ec: Add Ayaneo Embedded Controller platform driver Antheas Kapenekakis
2025-08-20 16:06 ` [PATCH v1 1/5] " Antheas Kapenekakis
2025-08-20 16:06 ` [PATCH v1 2/5] platform/x86: ayaneo-ec: Add hwmon support Antheas Kapenekakis
2025-08-22 12:35   ` Armin Wolf
2025-08-22 20:56     ` Antheas Kapenekakis
2025-08-20 16:06 ` [PATCH v1 3/5] platform/x86: ayaneo-ec: Add charge control support Antheas Kapenekakis
2025-08-22 12:38   ` Armin Wolf
2025-08-22 21:04     ` Antheas Kapenekakis
2025-08-20 16:06 ` [PATCH v1 4/5] platform/x86: ayaneo-ec: Add controller power and modules attributes Antheas Kapenekakis
2025-08-21  8:41   ` kernel test robot
2025-08-21  8:41   ` kernel test robot
2025-08-22 12:40   ` Armin Wolf
2025-08-22 21:08     ` Antheas Kapenekakis
2025-08-20 16:06 ` [PATCH v1 5/5] platform/x86: ayaneo-ec: Move Ayaneo devices from oxpec to ayaneo-ec Antheas Kapenekakis
2025-08-23  0:07   ` Derek John Clark

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).