From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5301048B37D; Tue, 22 Sep 2026 09:49:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=192.198.163.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790070575; cv=none; b=s7Bberz9AASf2lI/jmEcMWHjvt6pPMwSga1Jj/bjv7bWtR4kzJynxpC/WbEf7+rQDSrVLO+EaGoVRrUPdTfCZ6Nzs94tQKsKkZ8tfmmyAz7H1uxiVusnwLOL49yvy33NGxf1scePRUuWjEdtNW0L+NCHopw3qgewmtzQnJQheIA= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1790070575; c=relaxed/simple; bh=HoG7AAJLdDLcNpdqkYRRbFZc1Lr/cnklcFVu5TOiME4=; h=From:Date:To:cc:Subject:In-Reply-To:Message-ID:References: MIME-Version:Content-Type; b=cKYg5zwYbbYi3bVxtAavQYyZo99PKEqHUmBwZmsqiaKMLHuonaUvJVrSHGm63chbgGgrh//SZF+S62AZ5U+K0D+s9nzK77AjBKZZFIJMNjGTnzntj83aTzufJLi9UMT6H0XS8JXhoCeVeOipylYccE6egto5XK8eYwDAopnoh9U= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com; spf=pass smtp.mailfrom=linux.intel.com; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b=e4MmGnMk; arc=none smtp.client-ip=192.198.163.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.intel.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=intel.com header.i=@intel.com header.b="e4MmGnMk" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1790070572; x=1821606572; h=from:date:to:cc:subject:in-reply-to:message-id: references:mime-version; bh=HoG7AAJLdDLcNpdqkYRRbFZc1Lr/cnklcFVu5TOiME4=; b=e4MmGnMkmqAAiJdQMRSR3rliOkkcJivE9m0WV++VRgQrfWloKxIS7xla mkgk5ab2Nc06JHQr9VGVyjWxELi1wKuJjBQ/Bf7C9jwWGQ/lrrtxRVcQV PMvqa9sn6aFzdkjEmSx+ZWhBRdF9GV18BUz3XhN5K2Q71CFIru+iphGWs MTOO6cuGT9+i8O7DyemqdoCJ75Opsdm1fAV9HvKRYevTp+1mAjh018uEw R6Yx9LgMK65+0VPV515Hj+ApgZq2ATzJxxhyDoY8SFY2fjYwB/GpLZmUY /xwXpBDArN4JRLfrlg6RV9YMwFxXA8+KlVzVck/V0QM9CeLyhGSwr/AP6 g==; X-CSE-ConnectionGUID: aeUMVZ9qQnSAe0l9LnIqvQ== X-CSE-MsgGUID: rldqFaJ4QlGKn/Zb2JByhg== X-IronPort-AV: E=McAfee;i="6800,10657,11912"; a="89792890" X-IronPort-AV: E=Sophos;i="6.27,116,1787036400"; d="scan'208";a="89792890" Received: from fmviesa003.fm.intel.com ([10.60.135.143]) by fmvoesa112.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 22 Sep 2026 02:49:31 -0700 X-CSE-ConnectionGUID: LC1STt7XSLK+blQJWmoVOg== X-CSE-MsgGUID: dSPxxmYERUyM2ewaASDVJg== X-ExtLoop1: 1 Received: from ijarvine-mobl1.ger.corp.intel.com (HELO localhost) ([10.245.245.80]) by fmviesa003-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 22 Sep 2026 02:49:26 -0700 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Tue, 22 Sep 2026 12:49:23 +0300 (EEST) To: Konrad Dybcio cc: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Hans de Goede , Bryan O'Donoghue , Guenter Roeck , Bjorn Andersson , Abel Vesa , devicetree@vger.kernel.org, LKML , platform-driver-x86@vger.kernel.org, linux-hwmon@vger.kernel.org, linux-arm-msm@vger.kernel.org, Konrad Dybcio Subject: Re: [PATCH v2 2/3] platform: arm64: Add a driver for the EC found on ASUS Glymur machines In-Reply-To: <20260922-topic-asus_ec-v2-2-0bd923d1b853@oss.qualcomm.com> Message-ID: References: <20260922-topic-asus_ec-v2-0-0bd923d1b853@oss.qualcomm.com> <20260922-topic-asus_ec-v2-2-0bd923d1b853@oss.qualcomm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII On Tue, 22 Sep 2026, Konrad Dybcio wrote: > From: Konrad Dybcio > > 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 > --- > 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* 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.