Re: [PATCH v2 2/3] platform: arm64: Add a driver for the EC found on ASUS Glymur machines
From: Ilpo Järvinen
Date: Tue Sep 22 2026 - 06:02:15 EST
On Tue, 22 Sep 2026, Konrad Dybcio wrote:
> From: Konrad Dybcio <konrad.dybcio@xxxxxxxxxxxxxxxx>
>
> 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@xxxxxxxxxxxxxxxx>
> ---
> 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);
Add include.
> +
> + 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);
Add include for dev_dbg().
Please add braces to any multiline construct even if they're just a
single statement in the block.
> +
> + 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);
Fits to two lines.
> + 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);
Fits to two lines.
> + 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);
> + }
This waiting code seems to be duplicated so it should be moved into own
function.
> +
> + 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)
Could these literals be named with defines?
> + 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);
Add braces to if/else.
Usually, the error handling is done on the same level as the call, even if
it wouldn't strictly need to hold the mutex.
> + else
> + dev_info_ratelimited(dev, "AC event: 0x%x\n", ac);
How is this print useful for a normal user?
> + 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);
Please add include for container_of().
> + 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");
Add braces to multiline statements.
> + 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");
Ditto.
> +
> + 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");
>
>
--
i.