From: Guenter Roeck <linux@roeck-us.net>
To: Ethan Jimmy <duhxu0@gmail.com>
Cc: linux-hwmon@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH] hwmon: (acer-aspire14-ec) Add driver for Acer Aspire 14 52MT and 52M fan
Date: Mon, 26 Oct 2026 13:25:08 -0700 [thread overview]
Message-ID: <0dee51d4-c153-457a-a3aa-ebf1177df045@roeck-us.net> (raw)
In-Reply-To: <20261010041313.34153-1-duhxu0@gmail.com>
On Fri, Oct 09, 2026 at 09:13:13PM -0700, Ethan Jimmy wrote:
> The Aspire A14-52M and A14-52MT expose fan speed through an I2C HID
> embedded controller that also serves the keyboard. The same product id
> is used by other Acer keyboards, so the driver binds only when DMI
> matches these two models.
>
> Signed-off-by: Ethan Jimmy <duhxu0@gmail.com>
> ---
> Documentation/hwmon/acer-aspire14-ec.rst | 38 ++++
> Documentation/hwmon/index.rst | 1 +
> MAINTAINERS | 8 +
> drivers/hwmon/Kconfig | 20 ++
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/acer-aspire14-ec.c | 270 +++++++++++++++++++++++
> 6 files changed, 338 insertions(+)
> create mode 100644 Documentation/hwmon/acer-aspire14-ec.rst
> create mode 100644 drivers/hwmon/acer-aspire14-ec.c
>
> diff --git a/Documentation/hwmon/acer-aspire14-ec.rst b/Documentation/hwmon/acer-aspire14-ec.rst
> new file mode 100644
> index 000000000000..73acfb181c39
> --- /dev/null
> +++ b/Documentation/hwmon/acer-aspire14-ec.rst
> @@ -0,0 +1,38 @@
> +.. SPDX-License-Identifier: GPL-2.0-or-later
> +
> +Kernel driver acer-aspire14-ec
> +==============================
> +
> +Supported chips:
> +
> + * Acer Aspire 14 AI 52MT/52M embedded controller
> +
> + Prefix: 'aspire14_ec'
> +
> + Addresses scanned: none
> +
> + Datasheet: unavailable
> +
> +Author: Ethan Jimmy <duhxu0@gmail.com>
> +
> +Description
> +-----------
> +
> +This driver monitors the fan on the Acer Aspire 14 AI (Aspire A14-52M
> +and A14-52MT). The embedded controller is an I2C HID device (vendor
> +0x1025, product 0x174b) that also provides the keyboard.
> +
> +The device is autodetected on those models. The same product id is used
> +by other Acer keyboards; those machines stay with hid-generic. On the
> +Aspire A14-52M and A14-52MT this driver provides both the keyboard and
> +the fan sensor.
> +
> +Fan speed is reported in RPM through the standard hwmon attribute
> +``fan1_input``. The driver does not change fan speed.
> +
> +Sysfs entries
> +-------------
> +
> +======================= ======================================================
> +fan1_input Fan speed in RPM
> +======================= ======================================================
> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst
> index 199ca79621e9..9dd16e6ddabd 100644
> --- a/Documentation/hwmon/index.rst
> +++ b/Documentation/hwmon/index.rst
> @@ -22,6 +22,7 @@ Hardware Monitoring Kernel Drivers
> abituguru
> abituguru3
> acbel-fsg032
> + acer-aspire14-ec
> acpi_power_meter
> ad7314
> adc128d818
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 5ad12c57ca5f..b944f77c7a71 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -262,6 +262,14 @@ L: linux-gpio@vger.kernel.org
> S: Maintained
> F: drivers/gpio/gpio-pcie-idio-24.c
>
> +ACER ASPIRE 14 AI (A14-52M, A14-52MT) EC FAN DRIVER
> +M: Ethan Jimmy <duhxu0@gmail.com>
> +L: linux-hwmon@vger.kernel.org
> +L: linux-input@vger.kernel.org
> +S: Maintained
> +F: Documentation/hwmon/acer-aspire14-ec.rst
> +F: drivers/hwmon/acer-aspire14-ec.c
> +
> ACER ASPIRE ONE TEMPERATURE AND FAN DRIVER
> M: Peter Kaestle <peter@piie.net>
> L: platform-driver-x86@vger.kernel.org
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 18c682e1a475..84e9e9c115a1 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -67,6 +67,26 @@ config SENSORS_ABITUGURU3
> This driver can also be built as a module. If so, the module
> will be called abituguru3.
>
> +config SENSORS_ACER_ASPIRE14_EC
> + tristate "Acer Aspire 14 AI EC fan sensor"
> + depends on HID
> + depends on X86 || COMPILE_TEST
> + depends on DMI || COMPILE_TEST
> + depends on I2C_HID_ACPI || I2C_HID_OF || COMPILE_TEST
> + help
> + If you say yes here you get support for reading the fan
> + speed from the embedded controller on the Acer Aspire 14 AI
> + (Aspire A14-52M and A14-52MT). The controller is an I2C HID
> + device (vendor 0x1025, product 0x174b) that also provides the
> + keyboard. Fan speed is read-only.
> +
> + The same product id is used by other Acer keyboards. This driver
> + registers only on the Aspire A14-52M and A14-52MT, where it also
> + provides the keyboard. Other machines stay with hid-generic.
> +
> + This driver can also be built as a module. If so, the module
> + will be called acer-aspire14-ec.
> +
> config SENSORS_SMPRO
> tristate "Ampere's Altra SMpro hardware monitoring driver"
> depends on MFD_SMPRO
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index d4c345ed7261..ac5cec462c81 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -27,6 +27,7 @@ obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
>
> obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
> obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
> +obj-$(CONFIG_SENSORS_ACER_ASPIRE14_EC) += acer-aspire14-ec.o
> obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
> obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
> obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
> diff --git a/drivers/hwmon/acer-aspire14-ec.c b/drivers/hwmon/acer-aspire14-ec.c
> new file mode 100644
> index 000000000000..9d100f2a0b80
> --- /dev/null
> +++ b/drivers/hwmon/acer-aspire14-ec.c
> @@ -0,0 +1,270 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Hwmon driver for the Acer Aspire 14 AI embedded controller.
> + *
> + * Author: Ethan Jimmy <duhxu0@gmail.com>
> + *
> + * On the Aspire A14-52M and A14-52MT the EC is an I2C HID device
> + * (vendor 0x1025, product 0x174b) that also serves the keyboard. The same
> + * product id is used by other Acer keyboards. The HID driver is registered
> + * only when DMI matches these models, so those machines stay with hid-generic.
Doees this impact the actual keyboard functionality ?
> + *
> + * Fan speed is read with one feature report: A0 00 A0 08 00 02 02. The
> + * reply carries a little-endian status word, command id, and RPM.
> + */
> +
> +#include <linux/dmi.h>
> +#include <linux/hid.h>
> +#include <linux/hwmon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/string.h>
> +#include <linux/unaligned.h>
> +
> +#define ACER_ASPIRE14_VID 0x1025
> +#define ACER_ASPIRE14_PID 0x174b
> +#define ACER_ASPIRE14_REPORT_ID 0xa0
> +#define ACER_ASPIRE14_PREFIX 0xa0
> +#define ACER_ASPIRE14_CMD_MARK 0x02
> +#define ACER_ASPIRE14_RPM_OFFSET 8
> +#define ACER_ASPIRE14_MIN_REPLY (ACER_ASPIRE14_RPM_OFFSET + sizeof(u16))
> +#define ACER_ASPIRE14_STATUS_OK 0xe000
> +#define ACER_ASPIRE14_CMD_FAN_RPM 0x0008
> +
> +struct acer_aspire14_data {
> + struct hid_device *hdev;
> + struct device *hwmon_dev;
> + struct mutex lock; /* protects buf across feature report I/O */
There is no asynchronous call, meaning the lock is unnecessary
since requests are already serialised by the hwmon subsystem.
> + u8 *buf;
> + u32 report_len;
> +};
> +
> +static int acer_aspire14_fan_rpm(struct acer_aspire14_data *data, long *val)
> +{
> + u16 status, command_id;
> + int ret;
> +
> + mutex_lock(&data->lock);
> +
> + memset(data->buf, 0, data->report_len);
> + data->buf[0] = ACER_ASPIRE14_REPORT_ID;
> + data->buf[2] = ACER_ASPIRE14_PREFIX;
> + put_unaligned_le16(ACER_ASPIRE14_CMD_FAN_RPM, &data->buf[3]);
> + data->buf[5] = ACER_ASPIRE14_CMD_MARK;
> + data->buf[6] = ACER_ASPIRE14_CMD_MARK;
> +
> + ret = hid_hw_raw_request(data->hdev, ACER_ASPIRE14_REPORT_ID,
> + data->buf, data->report_len,
> + HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
> + if (ret < 0)
> + goto unlock;
> + if (!ret) {
Shouldn't that check for a specific length, such as data->report_len ?
> + ret = -EIO;
> + goto unlock;
> + }
> +
> + memset(data->buf, 0, data->report_len);
> + data->buf[0] = ACER_ASPIRE14_REPORT_ID;
> + ret = hid_hw_raw_request(data->hdev, ACER_ASPIRE14_REPORT_ID,
> + data->buf, data->report_len,
> + HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> + if (ret < 0)
> + goto unlock;
> + if (ret < ACER_ASPIRE14_MIN_REPLY) {
> + ret = -EIO;
> + goto unlock;
> + }
> +
> + status = get_unaligned_le16(&data->buf[1]);
> + command_id = get_unaligned_le16(&data->buf[3]);
> + if (status != ACER_ASPIRE14_STATUS_OK ||
> + command_id != ACER_ASPIRE14_CMD_FAN_RPM) {
> + ret = -EIO;
> + goto unlock;
> + }
> +
> + *val = get_unaligned_le16(&data->buf[ACER_ASPIRE14_RPM_OFFSET]);
> + ret = 0;
> +
> +unlock:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +static int acer_aspire14_read(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, long *val)
> +{
> + struct acer_aspire14_data *data = dev_get_drvdata(dev);
> +
> + if (type != hwmon_fan || attr != hwmon_fan_input || channel != 0)
> + return -EOPNOTSUPP;
Unnecessary check.
> +
> + return acer_aspire14_fan_rpm(data, val);
> +}
> +
> +static umode_t acer_aspire14_is_visible(const void *drvdata,
> + enum hwmon_sensor_types type,
> + u32 attr, int channel)
> +{
> + if (type == hwmon_fan && attr == hwmon_fan_input && channel == 0)
> + return 0444;
> +
Unnecessary check. Just return 0444.
> + return 0;
> +}
> +
> +static const struct hwmon_channel_info *const acer_aspire14_info[] = {
> + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
> + NULL
> +};
> +
> +static const struct hwmon_ops acer_aspire14_ops = {
> + .is_visible = acer_aspire14_is_visible,
> + .read = acer_aspire14_read,
> +};
> +
> +static const struct hwmon_chip_info acer_aspire14_chip_info = {
> + .ops = &acer_aspire14_ops,
> + .info = acer_aspire14_info,
> +};
> +
> +static const struct dmi_system_id acer_aspire14_dmi_table[] = {
> + {
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
> + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Aspire A14-52M"),
> + },
> + },
> + {
> + .matches = {
> + DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
> + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Aspire A14-52MT"),
> + },
> + },
> + { }
> +};
> +MODULE_DEVICE_TABLE(dmi, acer_aspire14_dmi_table);
> +
> +static u32 acer_aspire14_fan_report_len(struct hid_device *hdev)
> +{
> + struct hid_report *report;
> + u32 len;
> +
> + report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[ACER_ASPIRE14_REPORT_ID];
> + if (!report)
> + return 0;
> +
> + len = hid_report_len(report);
> + if (len < ACER_ASPIRE14_MIN_REPLY || len > HID_MAX_BUFFER_SIZE)
> + return 0;
> +
> + return len;
> +}
> +
> +static int acer_aspire14_probe(struct hid_device *hdev,
> + const struct hid_device_id *id)
> +{
> + struct acer_aspire14_data *data;
> + struct device *hwmon_dev;
> + long rpm;
> + u32 report_len;
> + int ret;
> +
> + hdev->quirks |= HID_QUIRK_INPUT_PER_APP;
> +
> + ret = hid_parse(hdev);
> + if (ret)
> + return ret;
> +
> + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT | HID_CONNECT_DRIVER);
> + if (ret)
> + return ret;
> +
> + report_len = acer_aspire14_fan_report_len(hdev);
> + if (!report_len)
> + return 0;
> +
> + data = devm_kzalloc(&hdev->dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return 0;
> +
> + data->buf = devm_kzalloc(&hdev->dev, report_len, GFP_KERNEL);
> + if (!data->buf)
> + return 0;
> + data->report_len = report_len;
> +
> + ret = devm_mutex_init(&hdev->dev, &data->lock);
> + if (ret) {
> + hid_warn(hdev, "failed to init mutex: %d\n", ret);
> + return 0;
> + }
> +
> + data->hdev = hdev;
> + hid_set_drvdata(hdev, data);
> +
> + ret = acer_aspire14_fan_rpm(data, &rpm);
> + if (ret)
> + hid_dbg(hdev, "fan report failed: %d\n", ret);
> + else
> + hid_dbg(hdev, "initial fan speed %ld RPM\n", rpm);
> +
> + hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "aspire14_ec",
> + data, &acer_aspire14_chip_info,
> + NULL);
> + if (IS_ERR(hwmon_dev)) {
> + hid_warn(hdev, "failed to register hwmon: %pe\n", hwmon_dev);
> + return 0;
That is a problem. Something is wrong here: If the driver fails to instantiate
the hwmon device, it should return an error. Same for all other "return 0"
instances above.
> + }
> + data->hwmon_dev = hwmon_dev;
> +
> + return 0;
> +}
> +
> +static void acer_aspire14_remove(struct hid_device *hdev)
> +{
> + struct acer_aspire14_data *data = hid_get_drvdata(hdev);
> +
> + if (data && data->hwmon_dev)
> + hwmon_device_unregister(data->hwmon_dev);
> + hid_hw_stop(hdev);
> +}
> +
> +static int acer_aspire14_reset_resume(struct hid_device *hdev)
> +{
> + if (hdev->claimed & HID_CLAIMED_INPUT)
> + hidinput_reset_resume(hdev);
> +
> + return 0;
> +}
> +
> +static const struct hid_device_id acer_aspire14_id_table[] = {
> + { HID_I2C_DEVICE(ACER_ASPIRE14_VID, ACER_ASPIRE14_PID) },
> + { }
> +};
> +
> +static struct hid_driver acer_aspire14_driver = {
> + .name = "acer-aspire14-ec",
> + .id_table = acer_aspire14_id_table,
> + .probe = acer_aspire14_probe,
> + .remove = acer_aspire14_remove,
> + .reset_resume = acer_aspire14_reset_resume,
> +};
> +
> +static int __init acer_aspire14_init(void)
> +{
> + if (!dmi_check_system(acer_aspire14_dmi_table))
> + return -ENODEV;
> +
> + return hid_register_driver(&acer_aspire14_driver);
> +}
> +
> +static void __exit acer_aspire14_exit(void)
> +{
> + hid_unregister_driver(&acer_aspire14_driver);
> +}
> +
> +module_init(acer_aspire14_init);
> +module_exit(acer_aspire14_exit);
> +
> +MODULE_AUTHOR("Ethan Jimmy <duhxu0@gmail.com>");
> +MODULE_DESCRIPTION("Acer Aspire 14 AI EC fan driver");
> +MODULE_LICENSE("GPL");
prev parent reply other threads:[~2026-10-26 20:25 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-10 4:13 Ethan Jimmy
2026-10-26 20:25 ` Guenter Roeck [this message]
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=0dee51d4-c153-457a-a3aa-ebf1177df045@roeck-us.net \
--to=linux@roeck-us.net \
--cc=duhxu0@gmail.com \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-kernel@vger.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®