From: Konrad Dybcio <konradybcio@kernel.org>
To: "Rob Herring" <robh@kernel.org>,
"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Konrad Dybcio" <konradybcio@kernel.org>,
"Hans de Goede" <hansg@kernel.org>,
"Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>,
"Bryan O'Donoghue" <bryan.odonoghue@linaro.org>,
"Guenter Roeck" <linux@roeck-us.net>,
"Bjorn Andersson" <andersson@kernel.org>,
"Abel Vesa" <abelvesa@kernel.org>
Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
platform-driver-x86@vger.kernel.org,
linux-hwmon@vger.kernel.org, linux-arm-msm@vger.kernel.org,
Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Subject: [PATCH 2/3] platform: arm64: Add a driver for the EC found on ASUS Glymur machines
Date: Thu, 17 Sep 2026 12:54:57 +0200 [thread overview]
Message-ID: <20260917-topic-asus_ec-v1-2-373516d347ae@oss.qualcomm.com> (raw)
In-Reply-To: <20260917-topic-asus_ec-v1-0-373516d347ae@oss.qualcomm.com>
From: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Some Qualcomm Snapdragon X2 Elite (Extreme)-based ASUS laptops (at
least the Zenbook A14 and A16) expose a custom, somewhat
Qualcomm reference-derived EC interface. Introduce a driver to
communicate with said EC, allowing for fan speed reporting, keyboard
backlight setting and receiving of a number of miscellaneous sideband
events.
Assisted-by: LLM
Signed-off-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
---
drivers/platform/arm64/Kconfig | 16 +
drivers/platform/arm64/Makefile | 1 +
drivers/platform/arm64/asus-glymur-ec.c | 592 ++++++++++++++++++++++++++++++++
3 files changed, 609 insertions(+)
diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig
index e32e01b2a9bd..84c475c62990 100644
--- a/drivers/platform/arm64/Kconfig
+++ b/drivers/platform/arm64/Kconfig
@@ -33,6 +33,22 @@ config EC_ACER_ASPIRE1
laptop where this information is not properly exposed via the
standard ACPI devices.
+config EC_ASUS_GLYMUR
+ tristate "ASUS Zenbook A16 (Qualcomm) Embedded Controller driver"
+ depends on ARCH_QCOM || COMPILE_TEST
+ depends on I2C
+ depends on HWMON
+ depends on INPUT
+ depends on PM
+ depends on THERMAL || THERMAL=n
+ select NEW_LEDS
+ select LEDS_CLASS
+ help
+ Say M or Y here to enable the EC driver for the Qualcomm Snapdragon-
+ based ASUS Zenbook A16 (UX3607OA) and similar machines. It provides
+ temperature sensors readout, fan monitoring, keyboard and EC event
+ reporting.
+
config EC_HUAWEI_GAOKUN
tristate "Huawei Matebook E Go Embedded Controller driver"
depends on ARCH_QCOM || COMPILE_TEST
diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile
index 7681be4a46e9..7122e8ea325f 100644
--- a/drivers/platform/arm64/Makefile
+++ b/drivers/platform/arm64/Makefile
@@ -6,6 +6,7 @@
#
obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o
+obj-$(CONFIG_EC_ASUS_GLYMUR) += asus-glymur-ec.o
obj-$(CONFIG_EC_HUAWEI_GAOKUN) += huawei-gaokun-ec.o
obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o
obj-$(CONFIG_EC_LENOVO_THINKPAD_T14S) += lenovo-thinkpad-t14s.o
diff --git a/drivers/platform/arm64/asus-glymur-ec.c b/drivers/platform/arm64/asus-glymur-ec.c
new file mode 100644
index 000000000000..dec9b137b820
--- /dev/null
+++ b/drivers/platform/arm64/asus-glymur-ec.c
@@ -0,0 +1,592 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * The EC firmware on the ASUS Zenbook A16 looks to be derived from the
+ * 'Qualcomm Hamoa EC' reference base with many major modifications. Some of
+ * the original interface is reused, albeit many of its core invariants (such
+ * as the capabilities bitmap) are absent.
+ *
+ * It presents itself as two I2C devices:
+ * [0x5b] is the custom ASUS interface
+ * [0x76] is the Hamoa EC-derived interface
+ */
+
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+/* Qualcomm-derived registers (main device @ 0x76) */
+#define ASUS_QCOM_EC_EVENT_CMD 0x05
+
+#define ASUS_QCOM_EC_SUBCMD_FULL_VER 0x0f
+#define ASUS_QCOM_EC_FULL_VER_LEN 7
+
+/* This "read RAM" command isn't actually part of the Qualcomm standard */
+#define ASUS_QCOM_EC_RECM_OFFS 0x52
+#define ASUS_QCOM_EC_RECM_FRAME_LEN 5
+
+#define ASUS_QCOM_EC_RAM_FAN_CPU 0x0602
+#define ASUS_QCOM_EC_RAM_AC_STATUS 0x0609
+#define ASUS_QCOM_EC_RAM_FAN_GPU 0x0624
+
+#define ASUS_QCOM_EC_MODERN_STANDBY_CMD 0x23
+#define ASUS_QCOM_EC_MODERN_STANDBY_ENTER 0x07
+#define ASUS_QCOM_EC_MODERN_STANDBY_EXIT 0x08
+
+/* ASUS-specific registers (secondary device @ 0x5b) */
+#define ASUS_EC_SUBDEV_ADDR 0x5b
+
+#define ASUS_EC_REQ_REG 0x10
+#define ASUS_EC_RESP_REG 0x11
+
+#define ASUS_EC_PAGE_MAILBOX 0xc4
+ #define ASUS_EC_MBOX_REG_CMD 0x30
+ #define ASUS_EC_MBOX_REG_SUBCMD 0x31
+ #define ASUS_EC_MBOX_REG_DATA 0x32
+
+#define ASUS_EC_MBOX_CMD_KBD 0x01
+ #define ASUS_EC_MBOX_SUBCMD_KBD_EN 0x81
+ #define ASUS_EC_MBOX_DATA_KBD_EN 0x04
+ #define ASUS_EC_MBOX_SUBCMD_KBD_LVL 0x87
+
+#define ASUS_EC_MBOX_CMD_MISC 0x02
+ #define ASUS_EC_MBOX_SUBCMD_MISC_ENABLE 0x83
+
+#define ASUS_EC_PAGE_TEMPERATURE 0xc6
+ #define ASUS_EC_TEMP_REG_CPU 0xa6
+ #define ASUS_EC_TEMP_REG_SOC 0x2a
+ #define ASUS_EC_TEMP_MAX 0x7f
+
+#define ASUS_EC_KBD_MAX_BRIGHTNESS 3
+
+static const u8 asus_ec_kbd_levels[] = {
+ [0] = 0x00,
+ [1] = 0x55,
+ [2] = 0xaa,
+ [3] = 0xff,
+};
+
+enum asus_ec_event {
+ ASUS_EC_EVT_CHARGE_STATE = 0x1c,
+ ASUS_EC_EVT_AC_STATE = 0x1d,
+ ASUS_EC_EVT_CRIT_TRIP = 0x1e,
+ ASUS_EC_EVT_THERMISTOR = 0x4f,
+ ASUS_EC_EVT_HOTKEY = 0xda,
+ ASUS_EC_EVT_FAN_STATUS = 0xdb,
+ ASUS_EC_EVT_THERMAL_TRIP = 0xe0,
+ ASUS_EC_EVT_CHARGE_COMMIT = 0xe4,
+};
+
+struct asus_ec {
+ struct i2c_client *client;
+ struct i2c_client *subdev_client;
+ /* Serializes all EC access across both I2C devices */
+ struct mutex lock;
+ struct device *hwmon_dev;
+ struct led_classdev kbd_led;
+};
+
+static int asus_qcom_ec_write(struct asus_ec *ec, u8 offset,
+ const u8 *buf, u8 len)
+{
+ int ret;
+
+ lockdep_assert_held(&ec->lock);
+
+ ret = i2c_smbus_write_i2c_block_data(ec->client, offset, len, buf);
+ if (ret < 0)
+ dev_dbg(&ec->client->dev,
+ "Main device write off %#x len %u failed: %d\n",
+ offset, len, ret);
+
+ return ret;
+}
+
+static int asus_qcom_ec_read(struct asus_ec *ec, u8 offset, u8 *buf, u8 len)
+{
+ int ret;
+
+ lockdep_assert_held(&ec->lock);
+
+ ret = i2c_smbus_read_i2c_block_data(ec->client, offset, len, buf);
+ if (ret < 0)
+ return ret;
+ if (ret < len) {
+ dev_err(&ec->client->dev,
+ "Short read off %#x: got %d of %u bytes\n",
+ offset, ret, len);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int asus_qcom_ec_read_ram(struct asus_ec *ec, u16 addr, u8 count, u16 *out)
+{
+ u8 frame[ASUS_QCOM_EC_RECM_FRAME_LEN] = { 0, 0, count, 0, 0 };
+ u8 resp[ASUS_QCOM_EC_RECM_FRAME_LEN] = { };
+ int ret;
+
+ lockdep_assert_held(&ec->lock);
+
+ if (count != 1 && count != 2)
+ return -EINVAL;
+
+ put_unaligned_be16(addr, frame);
+
+ ret = asus_qcom_ec_write(ec, ASUS_QCOM_EC_RECM_OFFS, frame,
+ ASUS_QCOM_EC_RECM_FRAME_LEN);
+ if (ret < 0)
+ return ret;
+
+ ret = asus_qcom_ec_read(ec, ASUS_QCOM_EC_RECM_OFFS, resp,
+ ASUS_QCOM_EC_RECM_FRAME_LEN);
+ if (ret < 0)
+ return ret;
+
+ *out = (count == 2) ? get_unaligned_le16(resp) : resp[0];
+
+ return 0;
+}
+
+static int asus_ec_readb(struct asus_ec *ec, u8 page, u8 reg, u8 *out)
+{
+ u8 sel[2] = { page, reg };
+ int ret;
+
+ lockdep_assert_held(&ec->lock);
+
+ ret = i2c_smbus_write_i2c_block_data(ec->subdev_client,
+ ASUS_EC_REQ_REG,
+ sizeof(sel), sel);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(ec->subdev_client, ASUS_EC_RESP_REG);
+ if (ret < 0)
+ return ret;
+
+ *out = ret;
+
+ return 0;
+}
+
+static int asus_ec_writeb(struct asus_ec *ec, u8 page, u8 reg, u8 data)
+{
+ u8 sel[2] = { page, reg };
+ int ret;
+
+ lockdep_assert_held(&ec->lock);
+
+ ret = i2c_smbus_write_i2c_block_data(ec->subdev_client,
+ ASUS_EC_REQ_REG,
+ sizeof(sel), sel);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(ec->subdev_client,
+ ASUS_EC_RESP_REG, data);
+}
+
+static int asus_ec_mailbox_cmd(struct asus_ec *ec, u8 cmd, u8 sub, u8 data)
+{
+ int ret, tries;
+ u8 status;
+
+ lockdep_assert_held(&ec->lock);
+
+ for (tries = 5; tries > 0; tries--) {
+ ret = asus_ec_readb(ec, ASUS_EC_PAGE_MAILBOX,
+ ASUS_EC_MBOX_REG_CMD, &status);
+ if (ret < 0)
+ return ret;
+ if (status == 0)
+ break;
+ usleep_range(5000, 6000);
+ }
+ if (tries == 0)
+ return -EBUSY;
+
+ ret = asus_ec_writeb(ec, ASUS_EC_PAGE_MAILBOX, ASUS_EC_MBOX_REG_SUBCMD, sub);
+ if (ret < 0)
+ return ret;
+
+ ret = asus_ec_writeb(ec, ASUS_EC_PAGE_MAILBOX, ASUS_EC_MBOX_REG_DATA, data);
+ if (ret < 0)
+ return ret;
+
+ ret = asus_ec_writeb(ec, ASUS_EC_PAGE_MAILBOX, ASUS_EC_MBOX_REG_CMD, cmd);
+ if (ret < 0)
+ return ret;
+
+ for (tries = 5; tries > 0; tries--) {
+ ret = asus_ec_readb(ec, ASUS_EC_PAGE_MAILBOX,
+ ASUS_EC_MBOX_REG_CMD, &status);
+ if (ret < 0)
+ return ret;
+ if (status == 0)
+ return 0;
+
+ usleep_range(5000, 6000);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int asus_ec_enable_writes(struct asus_ec *ec)
+{
+ guard(mutex)(&ec->lock);
+
+ return asus_ec_mailbox_cmd(ec, ASUS_EC_MBOX_CMD_MISC,
+ ASUS_EC_MBOX_SUBCMD_MISC_ENABLE, 1);
+}
+
+static int asus_ec_read_version(struct asus_ec *ec, u8 *major, u8 *minor, u8 *patch)
+{
+ u8 resp[ASUS_QCOM_EC_FULL_VER_LEN + 1];
+ int ret;
+
+ guard(mutex)(&ec->lock);
+
+ ret = asus_qcom_ec_read(ec, ASUS_QCOM_EC_SUBCMD_FULL_VER,
+ resp, sizeof(resp));
+ if (ret < 0)
+ return ret;
+ if (resp[0] < ASUS_QCOM_EC_FULL_VER_LEN)
+ return -EIO;
+
+ *major = resp[3];
+ *minor = resp[2];
+ *patch = resp[1];
+
+ return 0;
+}
+
+static int asus_ec_get_temp(struct asus_ec *ec, u8 reg, long *millideg)
+{
+ u8 raw;
+ int ret;
+
+ guard(mutex)(&ec->lock);
+
+ ret = asus_ec_readb(ec, ASUS_EC_PAGE_TEMPERATURE, reg, &raw);
+ if (ret < 0)
+ return ret;
+ if (raw > ASUS_EC_TEMP_MAX)
+ return -ENODATA;
+
+ *millideg = raw * MILLIDEGREE_PER_DEGREE;
+
+ return 0;
+}
+
+static int asus_ec_get_fan_rpm(struct asus_ec *ec, u16 base, long *rpm)
+{
+ u16 val;
+ int ret;
+
+ guard(mutex)(&ec->lock);
+
+ ret = asus_qcom_ec_read_ram(ec, base, 2, &val);
+ if (ret < 0)
+ return ret;
+
+ *rpm = val;
+
+ return 0;
+}
+
+static irqreturn_t asus_ec_irq(int irq, void *data)
+{
+ struct asus_ec *ec = data;
+ struct device *dev = &ec->client->dev;
+ int code;
+
+ scoped_guard(mutex, &ec->lock)
+ code = i2c_smbus_read_byte_data(ec->client, ASUS_QCOM_EC_EVENT_CMD);
+
+ if (code < 0) {
+ dev_err_ratelimited(dev, "Failed to read EC event: %d\n", code);
+ return IRQ_HANDLED;
+ }
+ if (code == 0 || code == 0xff)
+ return IRQ_HANDLED;
+
+ switch (code) {
+ case ASUS_EC_EVT_HOTKEY:
+ /* Present in DSDT, don't know what triggers it */
+ dev_dbg_ratelimited(dev, "EC hotkey event\n");
+ break;
+
+ case ASUS_EC_EVT_FAN_STATUS:
+ dev_dbg_ratelimited(dev, "Fan status changed\n");
+ break;
+
+ /* Maybe the temperature of the EC itself? */
+ case ASUS_EC_EVT_THERMAL_TRIP:
+ case ASUS_EC_EVT_CRIT_TRIP:
+ /* ASUS_EC_TEMP_SOC climbing above 91 *C or falling below 92 *C */
+ case ASUS_EC_EVT_THERMISTOR:
+ dev_dbg_ratelimited(dev, "Thermal event %#x\n", code);
+ break;
+
+ case ASUS_EC_EVT_AC_STATE: {
+ u16 ac = 0;
+ int ret;
+
+ /* TODO: ping SoCCP over the pmic_glink OEM-specific interface */
+ scoped_guard(mutex, &ec->lock)
+ ret = asus_qcom_ec_read_ram(ec, ASUS_QCOM_EC_RAM_AC_STATUS, 1, &ac);
+ if (ret < 0)
+ dev_warn_ratelimited(dev,
+ "Failed to read AC status: %d\n",
+ ret);
+ else
+ dev_info_ratelimited(dev, "AC event: 0x%x\n", ac);
+ break;
+ }
+ case ASUS_EC_EVT_CHARGE_STATE:
+ case ASUS_EC_EVT_CHARGE_COMMIT:
+ break;
+
+ default:
+ dev_info_ratelimited(dev, "unknown EC event: %#x\n", code);
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int asus_ec_kbd_led_set(struct led_classdev *led,
+ enum led_brightness brightness)
+{
+ struct asus_ec *ec = container_of(led, struct asus_ec, kbd_led);
+ int ret;
+
+ if (brightness > ASUS_EC_KBD_MAX_BRIGHTNESS)
+ brightness = ASUS_EC_KBD_MAX_BRIGHTNESS;
+
+ guard(mutex)(&ec->lock);
+
+ ret = asus_ec_mailbox_cmd(ec, ASUS_EC_MBOX_CMD_KBD, ASUS_EC_MBOX_SUBCMD_KBD_EN,
+ ASUS_EC_MBOX_DATA_KBD_EN);
+ if (ret < 0)
+ return ret;
+
+ return asus_ec_mailbox_cmd(ec, ASUS_EC_MBOX_CMD_KBD, ASUS_EC_MBOX_SUBCMD_KBD_LVL,
+ asus_ec_kbd_levels[brightness]);
+}
+
+enum asus_ec_temp_channel {
+ /* External sensor? */
+ ASUS_EC_TEMP_CPU,
+ /* Likely SoC-internal temp data communicated over a side channel */
+ ASUS_EC_TEMP_SOC,
+ ASUS_EC_TEMP_COUNT,
+};
+
+static umode_t asus_ec_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_fan:
+ case hwmon_temp:
+ return 0444;
+ default:
+ return 0;
+ }
+}
+
+static int asus_ec_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct asus_ec *ec = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_temp:
+ switch (channel) {
+ case ASUS_EC_TEMP_CPU:
+ return asus_ec_get_temp(ec, ASUS_EC_TEMP_REG_CPU, val);
+ case ASUS_EC_TEMP_SOC:
+ return asus_ec_get_temp(ec, ASUS_EC_TEMP_REG_SOC, val);
+ default:
+ return -EINVAL;
+ }
+ case hwmon_fan:
+ switch (channel) {
+ case 0:
+ return asus_ec_get_fan_rpm(ec, ASUS_QCOM_EC_RAM_FAN_CPU, val);
+ case 1:
+ return asus_ec_get_fan_rpm(ec, ASUS_QCOM_EC_RAM_FAN_GPU, val);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int asus_ec_hwmon_read_string(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **str)
+{
+ if (type != hwmon_temp || channel >= ASUS_EC_TEMP_COUNT)
+ return -EINVAL;
+
+ switch (channel) {
+ case ASUS_EC_TEMP_CPU:
+ *str = "CPU";
+ break;
+ case ASUS_EC_TEMP_SOC:
+ *str = "SoC";
+ break;
+ }
+
+ return 0;
+}
+
+static const struct hwmon_channel_info * const asus_ec_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT | HWMON_T_LABEL,
+ HWMON_T_INPUT | HWMON_T_LABEL),
+ HWMON_CHANNEL_INFO(fan,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops asus_ec_hwmon_ops = {
+ .is_visible = asus_ec_hwmon_is_visible,
+ .read = asus_ec_hwmon_read,
+ .read_string = asus_ec_hwmon_read_string,
+};
+
+static const struct hwmon_chip_info asus_ec_hwmon_chip_info = {
+ .ops = &asus_ec_hwmon_ops,
+ .info = asus_ec_hwmon_info,
+};
+
+static int asus_glymur_ec_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ u8 major, minor, patch;
+ struct asus_ec *ec;
+ int ret;
+
+ ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL);
+ if (!ec)
+ return -ENOMEM;
+
+ ec->client = client;
+ i2c_set_clientdata(client, ec);
+
+ ret = devm_mutex_init(dev, &ec->lock);
+ if (ret)
+ return ret;
+
+ ec->subdev_client = devm_i2c_new_dummy_device(dev, client->adapter,
+ ASUS_EC_SUBDEV_ADDR);
+ if (IS_ERR(ec->subdev_client))
+ return dev_err_probe(dev, PTR_ERR(ec->subdev_client),
+ "Failed to register 0x5b EC device\n");
+
+ ret = asus_ec_enable_writes(ec);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to enable EC direct access: %d\n", ret);
+
+ ret = asus_ec_read_version(ec, &major, &minor, &patch);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read EC version: %d\n", ret);
+
+ dev_dbg(dev, "ASUS Zenbook EC version %u.%u.%u\n", major, minor, patch);
+
+ ec->kbd_led.name = "asus::kbd_backlight";
+ ec->kbd_led.max_brightness = ASUS_EC_KBD_MAX_BRIGHTNESS;
+ ec->kbd_led.brightness_set_blocking = asus_ec_kbd_led_set;
+ ec->kbd_led.flags = LED_CORE_SUSPENDRESUME;
+
+ ret = devm_led_classdev_register(dev, &ec->kbd_led);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register keyboard LED\n");
+
+ ret = devm_request_threaded_irq(dev, client->irq, NULL, asus_ec_irq,
+ IRQF_ONESHOT, dev_name(dev), ec);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request IRQ\n");
+
+ ec->hwmon_dev = devm_hwmon_device_register_with_info(dev,
+ "asus_glymur_ec",
+ ec,
+ &asus_ec_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(ec->hwmon_dev))
+ return dev_err_probe(dev, PTR_ERR(ec->hwmon_dev),
+ "Failed to register hwmon\n");
+
+ return 0;
+}
+
+static int asus_glymur_ec_modern_standby(struct asus_ec *ec, u8 val)
+{
+ guard(mutex)(&ec->lock);
+
+ return i2c_smbus_write_byte_data(ec->client,
+ ASUS_QCOM_EC_MODERN_STANDBY_CMD, val);
+}
+
+static int asus_glymur_ec_suspend(struct device *dev)
+{
+ return asus_glymur_ec_modern_standby(dev_get_drvdata(dev),
+ ASUS_QCOM_EC_MODERN_STANDBY_ENTER);
+}
+
+static int asus_glymur_ec_resume(struct device *dev)
+{
+ return asus_glymur_ec_modern_standby(dev_get_drvdata(dev),
+ ASUS_QCOM_EC_MODERN_STANDBY_EXIT);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(asus_glymur_ec_pm_ops,
+ asus_glymur_ec_suspend,
+ asus_glymur_ec_resume);
+
+static const struct of_device_id asus_glymur_ec_of_match[] = {
+ { .compatible = "asus,zenbook-a14-ux3407na-ec" },
+ { .compatible = "asus,zenbook-a16-ux3607oa-ec" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, asus_glymur_ec_of_match);
+
+static const struct i2c_device_id asus_glymur_ec_id[] = {
+ { "asus-glymur-ec" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, asus_glymur_ec_id);
+
+static struct i2c_driver asus_glymur_ec_driver = {
+ .driver = {
+ .name = "asus-glymur-ec",
+ .of_match_table = asus_glymur_ec_of_match,
+ .pm = pm_sleep_ptr(&asus_glymur_ec_pm_ops),
+ },
+ .probe = asus_glymur_ec_probe,
+ .id_table = asus_glymur_ec_id,
+};
+module_i2c_driver(asus_glymur_ec_driver);
+
+MODULE_DESCRIPTION("ASUS Zenbook A16 (Qualcomm) Embedded Controller driver");
+MODULE_LICENSE("GPL");
--
2.55.0
next prev parent reply other threads:[~2026-09-17 10:55 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-17 10:54 [PATCH 0/3] Asus Zenbook A16/A14 (UX3607OA/UX3407NA) EC driver Konrad Dybcio
2026-09-17 10:54 ` [PATCH 1/3] dt-bindings: embedded-controller: Add ASUS Zenbook A16 EC Konrad Dybcio
2026-09-18 11:34 ` Krzysztof Kozlowski
2026-09-18 12:29 ` Konrad Dybcio
2026-09-18 13:21 ` Krzysztof Kozlowski
2026-09-18 13:23 ` Konrad Dybcio
2026-09-18 13:38 ` Krzysztof Kozlowski
2026-09-17 10:54 ` Konrad Dybcio [this message]
2026-09-18 11:35 ` [PATCH 2/3] platform: arm64: Add a driver for the EC found on ASUS Glymur machines Krzysztof Kozlowski
2026-09-18 12:18 ` Konrad Dybcio
2026-09-18 12:21 ` Krzysztof Kozlowski
2026-09-17 10:54 ` [PATCH 3/3] arm64: dts: qcom: glymur-zenbook-a16: Add Embedded Controller Konrad Dybcio
2026-09-17 15:20 ` Abel Vesa
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260917-topic-asus_ec-v1-2-373516d347ae@oss.qualcomm.com \
--to=konradybcio@kernel.org \
--cc=abelvesa@kernel.org \
--cc=andersson@kernel.org \
--cc=bryan.odonoghue@linaro.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=hansg@kernel.org \
--cc=ilpo.jarvinen@linux.intel.com \
--cc=konrad.dybcio@oss.qualcomm.com \
--cc=krzk+dt@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=platform-driver-x86@vger.kernel.org \
--cc=robh@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®