Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH v2 5/5] input: drv260x: Handle calibration timeout
From: Yauhen Kharuzhy @ 2026-02-15 14:14 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input; +Cc: linux-kernel, Hans de Goede
In-Reply-To: <20260215141435.727872-1-jekhor@gmail.com>

If something goes wrong during calibration (for instance, if the 'enable'
GPIO was not properly defined), the GO bit may not be cleared after some
time, causing the driver to get stuck.

To prevent this, add a timeout check to the waiting loop and return an
error if it times out.

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/input/misc/drv260x.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 71fbdabc6589..7ab891210fad 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -166,6 +166,12 @@
 #define DRV260X_AUTOCAL_TIME_500MS		(2 << 4)
 #define DRV260X_AUTOCAL_TIME_1000MS		(3 << 4)
 
+/*
+ * Timeout for waiting for the GO status bit, in seconds. Should be reasonably
+ * large to wait for a auto-calibration cycle completion.
+ */
+#define DRV260X_GO_TIMEOUT_S 5
+
 /**
  * struct drv260x_data -
  * @input_dev: Pointer to the input device
@@ -309,6 +315,7 @@ static int drv260x_init(struct drv260x_data *haptics)
 {
 	int error;
 	unsigned int cal_buf;
+	unsigned long timeout;
 
 	error = regmap_write(haptics->regmap,
 			     DRV260X_RATED_VOLT, haptics->rated_voltage);
@@ -398,6 +405,7 @@ static int drv260x_init(struct drv260x_data *haptics)
 		return error;
 	}
 
+	timeout = jiffies + DRV260X_GO_TIMEOUT_S * HZ;
 	do {
 		usleep_range(15000, 15500);
 		error = regmap_read(haptics->regmap, DRV260X_GO, &cal_buf);
@@ -407,6 +415,11 @@ static int drv260x_init(struct drv260x_data *haptics)
 				error);
 			return error;
 		}
+		if (time_after(jiffies, timeout)) {
+			dev_err(&haptics->client->dev,
+				"Calibration timeout. The device cannot be used.\n");
+			return -ETIMEDOUT;
+		}
 	} while (cal_buf == DRV260X_GO_BIT);
 
 	return 0;
-- 
2.51.0


^ permalink raw reply related

* [PATCH v2 4/5] input: drv260x: Fix unbalanced regulator_disable() call
From: Yauhen Kharuzhy @ 2026-02-15 14:14 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input; +Cc: linux-kernel, Hans de Goede
In-Reply-To: <20260215141435.727872-1-jekhor@gmail.com>

The driver acquires the 'vbat' regulator during probing but never enables
it. Consequently, in the suspend method, the driver disables the regulator
without enabling it first, causing an 'Unbalanced regulator_disable()'
error.

Enable the regulator in the probe() method and add the remove() method with
regulator disabling to fix this.

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/input/misc/drv260x.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index c4dd410c303e..71fbdabc6589 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -520,19 +520,37 @@ static int drv260x_probe(struct i2c_client *client)
 		return error;
 	}
 
+	error = regulator_enable(haptics->regulator);
+	if (error) {
+		dev_err(dev, "Failed to enable regulator\n");
+		return error;
+	}
+
 	error = drv260x_init(haptics);
 	if (error) {
 		dev_err(dev, "Device init failed: %d\n", error);
-		return error;
+		goto err_regulator_disable;
 	}
 
 	error = input_register_device(haptics->input_dev);
 	if (error) {
 		dev_err(dev, "couldn't register input device: %d\n", error);
-		return error;
+		goto err_regulator_disable;
 	}
 
 	return 0;
+
+err_regulator_disable:
+	regulator_disable(haptics->regulator);
+
+	return error;
+}
+
+static void drv260x_remove(struct i2c_client *client)
+{
+	struct drv260x_data *haptics = i2c_get_clientdata(client);
+
+	regulator_disable(haptics->regulator);
 }
 
 static int drv260x_suspend(struct device *dev)
@@ -626,6 +644,7 @@ MODULE_DEVICE_TABLE(of, drv260x_of_match);
 
 static struct i2c_driver drv260x_driver = {
 	.probe		= drv260x_probe,
+	.remove		= drv260x_remove,
 	.driver		= {
 		.name	= "drv260x-haptics",
 		.of_match_table = drv260x_of_match,
-- 
2.51.0


^ permalink raw reply related

* [PATCH v2 3/5] input: drv260x: Add support for ACPI-enumerated devices
From: Yauhen Kharuzhy @ 2026-02-15 14:14 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input; +Cc: linux-kernel, Hans de Goede
In-Reply-To: <20260215141435.727872-1-jekhor@gmail.com>

Add ACPI ids and GPIO lookup mapping for drv2604 haptics device.
Found in Lenovo Yoga Book YB1-X91L tablet.

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/input/misc/drv260x.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index c352ff2859e2..c4dd410c303e 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -7,6 +7,7 @@
  * Copyright:   (C) 2014 Texas Instruments, Inc.
  */
 
+#include <linux/acpi.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
@@ -606,6 +607,14 @@ static const struct i2c_device_id drv260x_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, drv260x_id);
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id drv260x_acpi_match[] = {
+	{ "DRV2604", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, drv260x_acpi_match);
+#endif
+
 static const struct of_device_id drv260x_of_match[] = {
 	{ .compatible = "ti,drv2604", },
 	{ .compatible = "ti,drv2604l", },
@@ -621,6 +630,7 @@ static struct i2c_driver drv260x_driver = {
 		.name	= "drv260x-haptics",
 		.of_match_table = drv260x_of_match,
 		.pm	= pm_sleep_ptr(&drv260x_pm_ops),
+		.acpi_match_table = ACPI_PTR(drv260x_acpi_match),
 	},
 	.id_table = drv260x_id,
 };
-- 
2.51.0


^ permalink raw reply related

* [PATCH v2 2/5] input: drv260x: Sort all #include alphabetically
From: Yauhen Kharuzhy @ 2026-02-15 14:14 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input; +Cc: linux-kernel, Hans de Goede
In-Reply-To: <20260215141435.727872-1-jekhor@gmail.com>

Sort all #include directives alphabetically before adding new entries.

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/input/misc/drv260x.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 18360bdfe877..c352ff2859e2 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -7,14 +7,14 @@
  * Copyright:   (C) 2014 Texas Instruments, Inc.
  */
 
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
 #include <linux/module.h>
 #include <linux/regmap.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/gpio/consumer.h>
 #include <linux/regulator/consumer.h>
+#include <linux/slab.h>
 
 #include <dt-bindings/input/ti-drv260x.h>
 
-- 
2.51.0


^ permalink raw reply related

* [PATCH v2 1/5] input: drv260x: Add I2C IDs for all device variants
From: Yauhen Kharuzhy @ 2026-02-15 14:14 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input; +Cc: linux-kernel, Hans de Goede
In-Reply-To: <20260215141435.727872-1-jekhor@gmail.com>

Add drv2604(L) and drv2605 to the list of supported I2C device IDs
for clarity.

Signed-off-by: Yauhen Kharuzhy <jekhor@gmail.com>
---
 drivers/input/misc/drv260x.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/input/misc/drv260x.c b/drivers/input/misc/drv260x.c
index 96cd6a078c8a..18360bdfe877 100644
--- a/drivers/input/misc/drv260x.c
+++ b/drivers/input/misc/drv260x.c
@@ -598,6 +598,9 @@ static int drv260x_resume(struct device *dev)
 static DEFINE_SIMPLE_DEV_PM_OPS(drv260x_pm_ops, drv260x_suspend, drv260x_resume);
 
 static const struct i2c_device_id drv260x_id[] = {
+	{ "drv2604" },
+	{ "drv2604l" },
+	{ "drv2605" },
 	{ "drv2605l" },
 	{ }
 };
-- 
2.51.0


^ permalink raw reply related

* [PATCH v2 0/5] DRV260x: Support ACPI-enumerated devices
From: Yauhen Kharuzhy @ 2026-02-15 14:14 UTC (permalink / raw)
  To: Dmitry Torokhov, linux-input; +Cc: linux-kernel, Hans de Goede

Lenovo Yoga Book YB1-X90 and YB1-X91 tablets use haptics controllers
DRV2604L. The X91 (Windows tablet) uses ACPI to define its configuration,
such as I2C address and GPIO connections. The X90 (Android tablet)
doesn't have it in the ACPI, but the device may be defined as an
i2c_board in the x86-android-tablets driver.

To support these variants, add an ACPI matching table and add additional
I2C IDs to the I2C matching table (the driver supports DRV2604(L),
DRV2605(L) devices).

Also, implement a timeout for waiting for calibration,
and fix the non-working suspend due to unbalanced regulator_disable() call.

Changes in v2:
- Header includes have been sorted alphabetically;
- ACPI GPIO mapping has been removed (supposed to be in the
  device-specific driver like x86-android-tablets);
- Old debug session artifacts removed;
- Checking of device ID has been removed; the driver uses the same logic
  for all drv260x(L) variants;
- Timeout during calibration reports an error; we don't expect that
  the chip will work properly in such a case;
- Patch regarding the regulator has been reworked; now it just adds
  regulator_enable()/regulator_disable() during device probing and removal.

Discussion of v1 is here:
Link: https://lore.kernel.org/all/20260211235902.4156624-1-jekhor@gmail.com/

Yauhen Kharuzhy (5):
  input: drv260x: Add I2C IDs for all device variants
  input: drv260x: Sort all #include alphabetically
  input: drv260x: Add support for ACPI-enumerated devices
  input: drv260x: Fix unbalanced regulator_disable() call
  input: drv260x: Handle calibration timeout

 drivers/input/misc/drv260x.c | 55 ++++++++++++++++++++++++++++++++----
 1 file changed, 50 insertions(+), 5 deletions(-)


base-commit: 9845cf73f7db6094c0d8419d6adb848028f4a921

-- 
2.51.0


^ permalink raw reply

* [dtor-input:for-linus] BUILD SUCCESS 273a171dee33cb77070d7259c469d9440548c7df
From: kernel test robot @ 2026-02-15 12:34 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus
branch HEAD: 273a171dee33cb77070d7259c469d9440548c7df  Merge branch 'next' into for-linus

elapsed time: 877m

configs tested: 241
configs skipped: 6

The following configs have been built successfully.
More configs may be tested in the coming days.

tested configs:
alpha                             allnoconfig    gcc-15.2.0
alpha                            allyesconfig    gcc-15.2.0
alpha                               defconfig    gcc-15.2.0
arc                              allmodconfig    clang-16
arc                              allmodconfig    gcc-15.2.0
arc                               allnoconfig    gcc-15.2.0
arc                              allyesconfig    clang-22
arc                              allyesconfig    gcc-15.2.0
arc                                 defconfig    gcc-15.2.0
arc                            hsdk_defconfig    clang-22
arc                        nsim_700_defconfig    gcc-15.2.0
arc                   randconfig-001-20260215    clang-20
arc                   randconfig-002-20260215    clang-20
arc                        vdk_hs38_defconfig    clang-22
arm                               allnoconfig    clang-22
arm                               allnoconfig    gcc-15.2.0
arm                              allyesconfig    clang-16
arm                              allyesconfig    gcc-15.2.0
arm                     am200epdkit_defconfig    gcc-15.2.0
arm                          collie_defconfig    gcc-15.2.0
arm                                 defconfig    gcc-15.2.0
arm                         nhk8815_defconfig    clang-22
arm                          pxa910_defconfig    gcc-15.2.0
arm                             pxa_defconfig    clang-22
arm                             pxa_defconfig    gcc-15.2.0
arm                   randconfig-001-20260215    clang-20
arm                   randconfig-002-20260215    clang-20
arm                   randconfig-003-20260215    clang-20
arm                   randconfig-004-20260215    clang-20
arm64                            allmodconfig    clang-22
arm64                             allnoconfig    gcc-15.2.0
arm64                               defconfig    gcc-15.2.0
arm64                 randconfig-001-20260215    gcc-8.5.0
arm64                 randconfig-002-20260215    gcc-8.5.0
arm64                 randconfig-003-20260215    gcc-8.5.0
arm64                 randconfig-004-20260215    gcc-8.5.0
csky                             allmodconfig    gcc-15.2.0
csky                              allnoconfig    gcc-15.2.0
csky                                defconfig    gcc-15.2.0
csky                  randconfig-001-20260215    gcc-8.5.0
csky                  randconfig-002-20260215    gcc-8.5.0
hexagon                          allmodconfig    clang-17
hexagon                          allmodconfig    gcc-15.2.0
hexagon                           allnoconfig    clang-22
hexagon                           allnoconfig    gcc-15.2.0
hexagon                             defconfig    gcc-15.2.0
hexagon               randconfig-001-20260215    clang-22
hexagon               randconfig-002-20260215    clang-22
i386                             allmodconfig    clang-20
i386                              allnoconfig    gcc-14
i386                              allnoconfig    gcc-15.2.0
i386                             allyesconfig    clang-20
i386                             allyesconfig    gcc-14
i386        buildonly-randconfig-001-20260215    gcc-14
i386        buildonly-randconfig-002-20260215    gcc-14
i386        buildonly-randconfig-003-20260215    gcc-14
i386        buildonly-randconfig-004-20260215    gcc-14
i386        buildonly-randconfig-005-20260215    gcc-14
i386        buildonly-randconfig-006-20260215    gcc-14
i386                                defconfig    gcc-15.2.0
i386                  randconfig-001-20260215    clang-20
i386                  randconfig-002-20260215    clang-20
i386                  randconfig-003-20260215    clang-20
i386                  randconfig-004-20260215    clang-20
i386                  randconfig-004-20260215    gcc-14
i386                  randconfig-005-20260215    clang-20
i386                  randconfig-005-20260215    gcc-14
i386                  randconfig-006-20260215    clang-20
i386                  randconfig-006-20260215    gcc-14
i386                  randconfig-007-20260215    clang-20
i386                  randconfig-007-20260215    gcc-14
i386                  randconfig-011-20260215    clang-20
i386                  randconfig-012-20260215    clang-20
i386                  randconfig-013-20260215    clang-20
i386                  randconfig-014-20260215    clang-20
i386                  randconfig-015-20260215    clang-20
i386                  randconfig-016-20260215    clang-20
i386                  randconfig-017-20260215    clang-20
loongarch                        allmodconfig    clang-19
loongarch                        allmodconfig    clang-22
loongarch                         allnoconfig    clang-22
loongarch                         allnoconfig    gcc-15.2.0
loongarch                           defconfig    clang-19
loongarch             randconfig-001-20260215    clang-22
loongarch             randconfig-002-20260215    clang-22
loongarch             randconfig-002-20260215    gcc-13.4.0
m68k                             allmodconfig    gcc-15.2.0
m68k                              allnoconfig    gcc-15.2.0
m68k                             allyesconfig    clang-16
m68k                             allyesconfig    gcc-15.2.0
m68k                         amcore_defconfig    gcc-15.2.0
m68k                                defconfig    clang-19
m68k                        m5307c3_defconfig    gcc-15.2.0
m68k                        mvme147_defconfig    gcc-15.2.0
microblaze                        allnoconfig    gcc-15.2.0
microblaze                       allyesconfig    gcc-15.2.0
microblaze                          defconfig    clang-19
mips                             allmodconfig    gcc-15.2.0
mips                              allnoconfig    gcc-15.2.0
mips                             allyesconfig    gcc-15.2.0
mips                          ath25_defconfig    clang-22
mips                        bcm47xx_defconfig    clang-22
mips                         bigsur_defconfig    clang-22
mips                 decstation_r4k_defconfig    clang-22
mips                           ip30_defconfig    gcc-15.2.0
mips                         rt305x_defconfig    gcc-15.2.0
nios2                            allmodconfig    clang-22
nios2                            allmodconfig    gcc-11.5.0
nios2                             allnoconfig    clang-22
nios2                             allnoconfig    gcc-11.5.0
nios2                               defconfig    clang-19
nios2                 randconfig-001-20260215    clang-22
nios2                 randconfig-001-20260215    gcc-8.5.0
nios2                 randconfig-002-20260215    clang-22
nios2                 randconfig-002-20260215    gcc-8.5.0
openrisc                         allmodconfig    clang-22
openrisc                         allmodconfig    gcc-15.2.0
openrisc                          allnoconfig    clang-22
openrisc                          allnoconfig    gcc-15.2.0
openrisc                            defconfig    gcc-15.2.0
parisc                           allmodconfig    gcc-15.2.0
parisc                            allnoconfig    clang-22
parisc                            allnoconfig    gcc-15.2.0
parisc                           allyesconfig    clang-19
parisc                           allyesconfig    gcc-15.2.0
parisc                              defconfig    gcc-15.2.0
parisc                randconfig-001-20260215    clang-22
parisc                randconfig-001-20260215    gcc-11.5.0
parisc                randconfig-002-20260215    clang-22
parisc                randconfig-002-20260215    gcc-10.5.0
parisc64                            defconfig    clang-19
powerpc                          allmodconfig    gcc-15.2.0
powerpc                           allnoconfig    clang-22
powerpc                           allnoconfig    gcc-15.2.0
powerpc                       ebony_defconfig    gcc-15.2.0
powerpc                    gamecube_defconfig    clang-22
powerpc                 mpc836x_rdk_defconfig    clang-22
powerpc                      ppc6xx_defconfig    clang-22
powerpc               randconfig-001-20260215    clang-22
powerpc               randconfig-001-20260215    gcc-11.5.0
powerpc               randconfig-002-20260215    clang-22
powerpc                     redwood_defconfig    gcc-15.2.0
powerpc                     stx_gp3_defconfig    gcc-15.2.0
powerpc64             randconfig-001-20260215    clang-22
powerpc64             randconfig-002-20260215    clang-22
powerpc64             randconfig-002-20260215    gcc-8.5.0
riscv                            allmodconfig    clang-22
riscv                             allnoconfig    clang-22
riscv                             allnoconfig    gcc-15.2.0
riscv                            allyesconfig    clang-16
riscv                               defconfig    gcc-15.2.0
riscv             nommu_k210_sdcard_defconfig    clang-22
riscv                 randconfig-001-20260215    gcc-13.4.0
riscv                 randconfig-002-20260215    gcc-13.4.0
s390                             allmodconfig    clang-18
s390                             allmodconfig    clang-19
s390                              allnoconfig    clang-22
s390                             allyesconfig    gcc-15.2.0
s390                                defconfig    gcc-15.2.0
s390                  randconfig-001-20260215    gcc-13.4.0
s390                  randconfig-002-20260215    gcc-13.4.0
sh                               allmodconfig    gcc-15.2.0
sh                                allnoconfig    clang-22
sh                                allnoconfig    gcc-15.2.0
sh                               allyesconfig    clang-19
sh                               allyesconfig    gcc-15.2.0
sh                                  defconfig    gcc-14
sh                                  defconfig    gcc-15.2.0
sh                               j2_defconfig    gcc-15.2.0
sh                          landisk_defconfig    clang-22
sh                    randconfig-001-20260215    gcc-13.4.0
sh                    randconfig-002-20260215    gcc-13.4.0
sh                            titan_defconfig    gcc-15.2.0
sparc                             allnoconfig    clang-22
sparc                             allnoconfig    gcc-15.2.0
sparc                               defconfig    gcc-15.2.0
sparc                 randconfig-001-20260215    gcc-9.5.0
sparc                 randconfig-002-20260215    gcc-9.5.0
sparc                       sparc32_defconfig    gcc-15.2.0
sparc64                          allmodconfig    clang-22
sparc64                             defconfig    clang-20
sparc64                             defconfig    gcc-14
sparc64               randconfig-001-20260215    gcc-9.5.0
sparc64               randconfig-002-20260215    gcc-9.5.0
um                               allmodconfig    clang-19
um                                allnoconfig    clang-22
um                               allyesconfig    gcc-14
um                               allyesconfig    gcc-15.2.0
um                                  defconfig    clang-22
um                                  defconfig    gcc-14
um                             i386_defconfig    gcc-14
um                    randconfig-001-20260215    gcc-9.5.0
um                    randconfig-002-20260215    gcc-9.5.0
um                           x86_64_defconfig    clang-22
um                           x86_64_defconfig    gcc-14
x86_64                           allmodconfig    clang-20
x86_64                            allnoconfig    clang-20
x86_64                            allnoconfig    clang-22
x86_64                           allyesconfig    clang-20
x86_64      buildonly-randconfig-001-20260215    clang-20
x86_64      buildonly-randconfig-002-20260215    clang-20
x86_64      buildonly-randconfig-002-20260215    gcc-14
x86_64      buildonly-randconfig-003-20260215    clang-20
x86_64      buildonly-randconfig-004-20260215    clang-20
x86_64      buildonly-randconfig-005-20260215    clang-20
x86_64      buildonly-randconfig-005-20260215    gcc-14
x86_64      buildonly-randconfig-006-20260215    clang-20
x86_64      buildonly-randconfig-006-20260215    gcc-14
x86_64                              defconfig    gcc-14
x86_64                                  kexec    clang-20
x86_64                randconfig-001-20260215    gcc-14
x86_64                randconfig-002-20260215    gcc-14
x86_64                randconfig-003-20260215    gcc-14
x86_64                randconfig-004-20260215    gcc-14
x86_64                randconfig-005-20260215    gcc-14
x86_64                randconfig-006-20260215    gcc-14
x86_64                randconfig-011-20260215    gcc-14
x86_64                randconfig-012-20260215    gcc-14
x86_64                randconfig-013-20260215    gcc-14
x86_64                randconfig-014-20260215    gcc-14
x86_64                randconfig-015-20260215    gcc-14
x86_64                randconfig-016-20260215    gcc-14
x86_64                randconfig-071-20260215    gcc-14
x86_64                randconfig-072-20260215    gcc-14
x86_64                randconfig-073-20260215    gcc-14
x86_64                randconfig-074-20260215    gcc-14
x86_64                randconfig-075-20260215    gcc-14
x86_64                randconfig-076-20260215    gcc-14
x86_64                               rhel-9.4    clang-20
x86_64                           rhel-9.4-bpf    gcc-14
x86_64                          rhel-9.4-func    clang-20
x86_64                    rhel-9.4-kselftests    clang-20
x86_64                         rhel-9.4-kunit    gcc-14
x86_64                           rhel-9.4-ltp    gcc-14
x86_64                          rhel-9.4-rust    clang-20
xtensa                            allnoconfig    clang-22
xtensa                            allnoconfig    gcc-15.2.0
xtensa                           allyesconfig    clang-22
xtensa                generic_kc705_defconfig    clang-22
xtensa                randconfig-001-20260215    gcc-9.5.0
xtensa                randconfig-002-20260215    gcc-9.5.0

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

^ permalink raw reply

* [dtor-input:next] BUILD SUCCESS ab2e361ca97a42b7af8be1d273646b30d3b75bf3
From: kernel test robot @ 2026-02-15 10:00 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: linux-input

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git next
branch HEAD: ab2e361ca97a42b7af8be1d273646b30d3b75bf3  dt-bindings: input: qcom,pm8941-pwrkey: Document PMM8654AU

elapsed time: 723m

configs tested: 256
configs skipped: 2

The following configs have been built successfully.
More configs may be tested in the coming days.

tested configs:
alpha                             allnoconfig    gcc-15.2.0
alpha                            allyesconfig    gcc-15.2.0
alpha                               defconfig    gcc-15.2.0
arc                              allmodconfig    clang-16
arc                              allmodconfig    gcc-15.2.0
arc                               allnoconfig    gcc-15.2.0
arc                              allyesconfig    clang-22
arc                              allyesconfig    gcc-15.2.0
arc                                 defconfig    gcc-15.2.0
arc                            hsdk_defconfig    clang-22
arc                   randconfig-001-20260215    clang-20
arc                   randconfig-002-20260215    clang-20
arc                        vdk_hs38_defconfig    clang-22
arm                               allnoconfig    clang-22
arm                               allnoconfig    gcc-15.2.0
arm                              allyesconfig    clang-16
arm                              allyesconfig    gcc-15.2.0
arm                          collie_defconfig    gcc-15.2.0
arm                                 defconfig    gcc-15.2.0
arm                         nhk8815_defconfig    clang-22
arm                             pxa_defconfig    clang-22
arm                   randconfig-001-20260215    clang-20
arm                   randconfig-002-20260215    clang-20
arm                   randconfig-003-20260215    clang-20
arm                   randconfig-004-20260215    clang-20
arm64                            allmodconfig    clang-19
arm64                            allmodconfig    clang-22
arm64                             allnoconfig    gcc-15.2.0
arm64                               defconfig    gcc-15.2.0
arm64                 randconfig-001-20260215    gcc-8.5.0
arm64                 randconfig-002-20260215    gcc-8.5.0
arm64                 randconfig-003-20260215    gcc-8.5.0
arm64                 randconfig-004-20260215    gcc-8.5.0
csky                             allmodconfig    gcc-15.2.0
csky                              allnoconfig    gcc-15.2.0
csky                                defconfig    gcc-15.2.0
csky                  randconfig-001-20260215    gcc-8.5.0
csky                  randconfig-002-20260215    gcc-8.5.0
hexagon                          allmodconfig    clang-17
hexagon                          allmodconfig    gcc-15.2.0
hexagon                           allnoconfig    clang-22
hexagon                           allnoconfig    gcc-15.2.0
hexagon                             defconfig    gcc-15.2.0
hexagon               randconfig-001-20260215    clang-22
hexagon               randconfig-002-20260215    clang-22
i386                             allmodconfig    clang-20
i386                             allmodconfig    gcc-14
i386                              allnoconfig    gcc-14
i386                              allnoconfig    gcc-15.2.0
i386                             allyesconfig    clang-20
i386                             allyesconfig    gcc-14
i386        buildonly-randconfig-001-20260215    gcc-14
i386        buildonly-randconfig-002-20260215    clang-20
i386        buildonly-randconfig-002-20260215    gcc-14
i386        buildonly-randconfig-003-20260215    gcc-14
i386        buildonly-randconfig-004-20260215    gcc-14
i386        buildonly-randconfig-005-20260215    gcc-14
i386        buildonly-randconfig-006-20260215    gcc-14
i386                                defconfig    gcc-15.2.0
i386                  randconfig-001-20260215    clang-20
i386                  randconfig-002-20260215    clang-20
i386                  randconfig-003-20260215    clang-20
i386                  randconfig-004-20260215    clang-20
i386                  randconfig-004-20260215    gcc-14
i386                  randconfig-005-20260215    clang-20
i386                  randconfig-005-20260215    gcc-14
i386                  randconfig-006-20260215    clang-20
i386                  randconfig-006-20260215    gcc-14
i386                  randconfig-007-20260215    clang-20
i386                  randconfig-007-20260215    gcc-14
i386                  randconfig-011-20260215    clang-20
i386                  randconfig-011-20260215    gcc-14
i386                  randconfig-012-20260215    clang-20
i386                  randconfig-013-20260215    clang-20
i386                  randconfig-013-20260215    gcc-14
i386                  randconfig-014-20260215    clang-20
i386                  randconfig-014-20260215    gcc-14
i386                  randconfig-015-20260215    clang-20
i386                  randconfig-016-20260215    clang-20
i386                  randconfig-016-20260215    gcc-13
i386                  randconfig-017-20260215    clang-20
i386                  randconfig-017-20260215    gcc-14
loongarch                        allmodconfig    clang-19
loongarch                        allmodconfig    clang-22
loongarch                         allnoconfig    clang-22
loongarch                         allnoconfig    gcc-15.2.0
loongarch                           defconfig    clang-19
loongarch             randconfig-001-20260215    clang-22
loongarch             randconfig-002-20260215    clang-22
loongarch             randconfig-002-20260215    gcc-13.4.0
m68k                             allmodconfig    gcc-15.2.0
m68k                              allnoconfig    gcc-15.2.0
m68k                             allyesconfig    clang-16
m68k                             allyesconfig    gcc-15.2.0
m68k                         amcore_defconfig    gcc-15.2.0
m68k                                defconfig    clang-19
m68k                        mvme147_defconfig    gcc-15.2.0
microblaze                        allnoconfig    gcc-15.2.0
microblaze                       allyesconfig    gcc-15.2.0
microblaze                          defconfig    clang-19
mips                             allmodconfig    gcc-15.2.0
mips                              allnoconfig    gcc-15.2.0
mips                             allyesconfig    gcc-15.2.0
mips                          ath25_defconfig    clang-22
mips                        bcm47xx_defconfig    clang-22
mips                         bigsur_defconfig    clang-22
mips                 decstation_r4k_defconfig    clang-22
mips                           ip30_defconfig    gcc-15.2.0
nios2                            allmodconfig    clang-22
nios2                            allmodconfig    gcc-11.5.0
nios2                             allnoconfig    clang-22
nios2                             allnoconfig    gcc-11.5.0
nios2                               defconfig    clang-19
nios2                 randconfig-001-20260215    clang-22
nios2                 randconfig-001-20260215    gcc-8.5.0
nios2                 randconfig-002-20260215    clang-22
nios2                 randconfig-002-20260215    gcc-8.5.0
openrisc                         allmodconfig    clang-22
openrisc                         allmodconfig    gcc-15.2.0
openrisc                          allnoconfig    clang-22
openrisc                          allnoconfig    gcc-15.2.0
openrisc                            defconfig    gcc-15.2.0
parisc                           allmodconfig    gcc-15.2.0
parisc                            allnoconfig    clang-22
parisc                            allnoconfig    gcc-15.2.0
parisc                           allyesconfig    clang-19
parisc                           allyesconfig    gcc-15.2.0
parisc                              defconfig    gcc-15.2.0
parisc                randconfig-001-20260215    clang-22
parisc                randconfig-001-20260215    gcc-11.5.0
parisc                randconfig-002-20260215    clang-22
parisc                randconfig-002-20260215    gcc-10.5.0
parisc64                            defconfig    clang-19
powerpc                          allmodconfig    gcc-15.2.0
powerpc                           allnoconfig    clang-22
powerpc                           allnoconfig    gcc-15.2.0
powerpc                    gamecube_defconfig    clang-22
powerpc                 mpc836x_rdk_defconfig    clang-22
powerpc                      ppc6xx_defconfig    clang-22
powerpc               randconfig-001-20260215    clang-22
powerpc               randconfig-001-20260215    gcc-11.5.0
powerpc               randconfig-002-20260215    clang-22
powerpc                     redwood_defconfig    gcc-15.2.0
powerpc64             randconfig-001-20260215    clang-22
powerpc64             randconfig-002-20260215    clang-22
powerpc64             randconfig-002-20260215    gcc-8.5.0
riscv                            allmodconfig    clang-22
riscv                             allnoconfig    clang-22
riscv                             allnoconfig    gcc-15.2.0
riscv                            allyesconfig    clang-16
riscv                               defconfig    gcc-15.2.0
riscv             nommu_k210_sdcard_defconfig    clang-22
riscv                 randconfig-001-20260215    gcc-13.4.0
riscv                 randconfig-001-20260215    gcc-8.5.0
riscv                 randconfig-002-20260215    gcc-11.5.0
riscv                 randconfig-002-20260215    gcc-13.4.0
s390                             allmodconfig    clang-18
s390                             allmodconfig    clang-19
s390                              allnoconfig    clang-22
s390                             allyesconfig    gcc-15.2.0
s390                                defconfig    gcc-15.2.0
s390                  randconfig-001-20260215    gcc-13.4.0
s390                  randconfig-002-20260215    gcc-13.4.0
s390                  randconfig-002-20260215    gcc-8.5.0
sh                               allmodconfig    gcc-15.2.0
sh                                allnoconfig    clang-22
sh                                allnoconfig    gcc-15.2.0
sh                               allyesconfig    clang-19
sh                               allyesconfig    gcc-15.2.0
sh                                  defconfig    gcc-14
sh                                  defconfig    gcc-15.2.0
sh                               j2_defconfig    gcc-15.2.0
sh                          landisk_defconfig    clang-22
sh                    randconfig-001-20260215    gcc-13.4.0
sh                    randconfig-001-20260215    gcc-15.2.0
sh                    randconfig-002-20260215    gcc-11.5.0
sh                    randconfig-002-20260215    gcc-13.4.0
sh                            titan_defconfig    gcc-15.2.0
sparc                             allnoconfig    clang-22
sparc                             allnoconfig    gcc-15.2.0
sparc                               defconfig    gcc-15.2.0
sparc                 randconfig-001-20260215    gcc-15.2.0
sparc                 randconfig-001-20260215    gcc-9.5.0
sparc                 randconfig-002-20260215    gcc-8.5.0
sparc                 randconfig-002-20260215    gcc-9.5.0
sparc                       sparc32_defconfig    gcc-15.2.0
sparc64                          allmodconfig    clang-22
sparc64                             defconfig    clang-20
sparc64                             defconfig    gcc-14
sparc64               randconfig-001-20260215    gcc-9.5.0
sparc64               randconfig-002-20260215    clang-22
sparc64               randconfig-002-20260215    gcc-9.5.0
um                               allmodconfig    clang-19
um                                allnoconfig    clang-22
um                               allyesconfig    gcc-14
um                               allyesconfig    gcc-15.2.0
um                                  defconfig    clang-22
um                                  defconfig    gcc-14
um                             i386_defconfig    gcc-14
um                    randconfig-001-20260215    clang-22
um                    randconfig-001-20260215    gcc-9.5.0
um                    randconfig-002-20260215    gcc-14
um                    randconfig-002-20260215    gcc-9.5.0
um                           x86_64_defconfig    clang-22
um                           x86_64_defconfig    gcc-14
x86_64                           allmodconfig    clang-20
x86_64                            allnoconfig    clang-20
x86_64                            allnoconfig    clang-22
x86_64                           allyesconfig    clang-20
x86_64      buildonly-randconfig-001-20260215    clang-20
x86_64      buildonly-randconfig-002-20260215    clang-20
x86_64      buildonly-randconfig-002-20260215    gcc-14
x86_64      buildonly-randconfig-003-20260215    clang-20
x86_64      buildonly-randconfig-004-20260215    clang-20
x86_64      buildonly-randconfig-005-20260215    clang-20
x86_64      buildonly-randconfig-005-20260215    gcc-14
x86_64      buildonly-randconfig-006-20260215    clang-20
x86_64      buildonly-randconfig-006-20260215    gcc-14
x86_64                              defconfig    gcc-14
x86_64                                  kexec    clang-20
x86_64                randconfig-001-20260215    clang-20
x86_64                randconfig-001-20260215    gcc-14
x86_64                randconfig-002-20260215    gcc-14
x86_64                randconfig-003-20260215    clang-20
x86_64                randconfig-003-20260215    gcc-14
x86_64                randconfig-004-20260215    gcc-14
x86_64                randconfig-005-20260215    gcc-14
x86_64                randconfig-006-20260215    clang-20
x86_64                randconfig-006-20260215    gcc-14
x86_64                randconfig-011-20260215    gcc-14
x86_64                randconfig-012-20260215    gcc-14
x86_64                randconfig-013-20260215    gcc-14
x86_64                randconfig-014-20260215    gcc-14
x86_64                randconfig-015-20260215    gcc-14
x86_64                randconfig-016-20260215    gcc-14
x86_64                randconfig-071-20260215    gcc-14
x86_64                randconfig-072-20260215    gcc-14
x86_64                randconfig-073-20260215    gcc-14
x86_64                randconfig-074-20260215    gcc-14
x86_64                randconfig-075-20260215    gcc-14
x86_64                randconfig-076-20260215    gcc-14
x86_64                               rhel-9.4    clang-20
x86_64                           rhel-9.4-bpf    gcc-14
x86_64                          rhel-9.4-func    clang-20
x86_64                    rhel-9.4-kselftests    clang-20
x86_64                         rhel-9.4-kunit    gcc-14
x86_64                           rhel-9.4-ltp    gcc-14
x86_64                          rhel-9.4-rust    clang-20
xtensa                            allnoconfig    clang-22
xtensa                            allnoconfig    gcc-15.2.0
xtensa                           allyesconfig    clang-22
xtensa                           allyesconfig    gcc-15.2.0
xtensa                generic_kc705_defconfig    clang-22
xtensa                randconfig-001-20260215    gcc-12.5.0
xtensa                randconfig-001-20260215    gcc-9.5.0
xtensa                randconfig-002-20260215    gcc-9.5.0

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

^ permalink raw reply

* [git pull] Input updates for v6.20-rc0
From: Dmitry Torokhov @ 2026-02-15  3:39 UTC (permalink / raw)
  To: Linus Torvalds; +Cc: linux-kernel, linux-input

Hi Linus,

Please pull from:

	git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git tags/input-for-v7.0-rc0

to receive updates for the input subsystem. You will get:

- support for FocalTech FT8112 added to i2c-hid driver

- support for FocalTech FT3518 added to edt-ft5x06 driver

- support for power buttons in TWL603x chips added to twl4030-pwrbutton
  driver

- an update to gpio-decoder driver to make it usable on non-OF
  platforms and to clean up the code

- an update to synaptics_i2c driver switching it to use managed
  resources and a fix to restarting polling after resume

- an update to gpio-keys driver to fall back to getting IRQ from
  resources if not specified using other means

- an update to ili210x driver to support polling mode

- a number of input drivers switched to scnprintf() to suppress
  truncation warnings

- a number of updates and conversions of device tree bindings to yaml
  format

- fixes to spelling in comments and messages in several drivers

- other assorted fixups.

Changelog:
---------

Andreas Kemnade (2):
      Input: twl4030 - add TWL603x power button
      Input: twl4030 - fix warnings without CONFIG_OF

Andy Shevchenko (19):
      Input: dynapro - switch to use scnprintf() to suppress truncation warning
      Input: egalax_ts_serial - switch to use scnprintf() to suppress truncation warning
      Input: elo - switch to use scnprintf() to suppress truncation warning
      Input: gunze - switch to use scnprintf() to suppress truncation warning
      Input: hampshire - switch to use scnprintf() to suppress truncation warning
      Input: fujitsu_ts - switch to use scnprintf() to suppress truncation warning
      Input: inexio - switch to use scnprintf() to suppress truncation warning
      Input: mtouch - switch to use scnprintf() to suppress truncation warning
      Input: penmount - switch to use scnprintf() to suppress truncation warning
      Input: touchit213 - switch to use scnprintf() to suppress truncation warning
      Input: touchright - switch to use scnprintf() to suppress truncation warning
      Input: touchwin - switch to use scnprintf() to suppress truncation warning
      Input: tsc40 - switch to use scnprintf() to suppress truncation warning
      Input: wdt87xx_i2c - switch to use dev_err_probe()
      Input: gpio_decoder - make use of device properties
      Input: gpio_decoder - unify messages with help of dev_err_probe()
      Input: gpio_decoder - replace custom loop by gpiod_get_array_value_cansleep()
      Input: gpio_decoder - make use of the macros from bits.h
      Input: gpio_decoder - don't use "proxy" headers

Daniel Peng (2):
      dt-bindings: input: i2c-hid: Introduce FocalTech FT8112
      HID: i2c-hid: Add FocalTech FT8112

David Heidelberg (1):
      Input: stmfts - correct wording for the warning message

Dmitry Torokhov (4):
      Input: ilitek_ts_i2c - switch mdelay() to fsleep()
      Input: synaptics_i2c - switch to using managed resources
      Input: appletouch - fix potential race between resume and open
      Input: gpio_keys - fall back to platform_get_irq() for interrupt-only keys

Fabio Baltieri (1):
      Input: cros_ec_keyb - clarify key event error message

Gianluca Boiano (1):
      Input: novatek-nvt-ts - drop wake_type check

Josua Mayer (1):
      Input: ilitek_ts_i2c - fix warning with gpio controllers that sleep

Kuan-Wei Chiu (1):
      dt-bindings: input: google,goldfish-events-keypad: Convert to DT schema

Lukas Bulwahn (1):
      MAINTAINERS: adjust file entry in HIMAX HX83112B TOUCHSCREEN SUPPORT

Marco Crivellari (4):
      Input: gpio_keys - replace use of system_wq with system_dfl_wq
      Input: palmas-pwrbutton - replace use of system_wq with system_dfl_wq
      Input: synaptics_i2c - replace use of system_wq with system_dfl_wq
      Input: psmouse-smbus - add WQ_UNBOUND to alloc_workqueue user

Marek Vasut (3):
      Input: ili210x - convert to dev_err_probe()
      dt-bindings: touchscreen: trivial-touch: Drop 'interrupts' requirement for old Ilitek
      Input: ili210x - add support for polling mode

Micah Ostrow (1):
      Input: apbps2 - fix comment style and typos

Minseong Kim (1):
      Input: synaptics_i2c - guard polling restart in resume

Petr Hodina (2):
      Input: stmfts - make comments correct
      Input: stmfts - use sysfs_emit() instead of sprintf()

Rakesh Kota (1):
      dt-bindings: input: qcom,pm8941-pwrkey: Document PMM8654AU

Raymond Hackley (1):
      dt-bindings: input: touchscreen: imagis: allow linux,keycodes for ist3038

Rob Herring (Arm) (1):
      dt-bindings: input: touchscreen: sitronix,st1232: Add Sitronix ST1624

Sakari Ailus (3):
      Input: omap4-keypad - remove redundant pm_runtime_mark_last_busy() calls
      Input: cs40l50 - remove redundant pm_runtime_mark_last_busy() calls
      Input: cyapa - remove redundant pm_runtime_mark_last_busy() calls

Svyatoslav Ryhel (1):
      dt-bindings: input: touchscreen: tsc2007: document '#io-channel-cells'

Vaibhav Gupta (1):
      Input: pf1550 - remove "defined but unused" warning

Vivek BalachandharTN (1):
      Input: byd - use %*ph for Z packet dump

Vladimir Zapolskiy (1):
      Input: adp5589 - remove a leftover header file

Wentong Tian (1):
      Input: serio - complete sizeof(*pointer) conversions

Yedaya Katsman (2):
      dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT3518
      Input: edt-ft5x06 - add support for FocalTech FT3518

Diffstat:
--------

 .../devicetree/bindings/goldfish/events.txt        |  17 --
 .../bindings/input/focaltech,ft8112.yaml           |  66 ++++++
 .../input/google,goldfish-events-keypad.yaml       |  41 ++++
 .../bindings/input/qcom,pm8941-pwrkey.yaml         |  17 +-
 .../bindings/input/touchscreen/edt-ft5x06.yaml     |   1 +
 .../bindings/input/touchscreen/ilitek,ili210x.yaml |  51 +++++
 .../input/touchscreen/imagis,ist3038c.yaml         |   4 +-
 .../input/touchscreen/sitronix,st1232.yaml         |  10 +-
 .../bindings/input/touchscreen/ti,tsc2007.yaml     |   3 +
 .../bindings/input/touchscreen/trivial-touch.yaml  |   4 -
 MAINTAINERS                                        |   2 +-
 drivers/hid/i2c-hid/i2c-hid-of-elan.c              |   8 +
 drivers/input/keyboard/cros_ec_keyb.c              |   3 +-
 drivers/input/keyboard/gpio_keys.c                 |  19 +-
 drivers/input/keyboard/omap4-keypad.c              |   4 -
 drivers/input/misc/cs40l50-vibra.c                 |   4 -
 drivers/input/misc/gpio_decoder.c                  |  74 ++++---
 drivers/input/misc/palmas-pwrbutton.c              |   2 +-
 drivers/input/misc/pf1550-onkey.c                  |   2 +-
 drivers/input/misc/twl4030-pwrbutton.c             |  67 ++++++-
 drivers/input/mouse/appletouch.c                   |   9 +-
 drivers/input/mouse/byd.c                          |   6 +-
 drivers/input/mouse/cyapa.c                        |   3 -
 drivers/input/mouse/cyapa_gen5.c                   |   1 -
 drivers/input/mouse/psmouse-smbus.c                |   2 +-
 drivers/input/mouse/synaptics_i2c.c                | 223 +++++++++------------
 drivers/input/serio/altera_ps2.c                   |   2 +-
 drivers/input/serio/apbps2.c                       |  14 +-
 drivers/input/serio/arc_ps2.c                      |   3 +-
 drivers/input/serio/olpc_apsp.c                    |   2 +-
 drivers/input/touchscreen/dynapro.c                |   4 +-
 drivers/input/touchscreen/edt-ft5x06.c             |   6 +
 drivers/input/touchscreen/egalax_ts_serial.c       |   3 +-
 drivers/input/touchscreen/elo.c                    |   2 +-
 drivers/input/touchscreen/fujitsu_ts.c             |   3 +-
 drivers/input/touchscreen/gunze.c                  |   2 +-
 drivers/input/touchscreen/hampshire.c              |   4 +-
 drivers/input/touchscreen/ili210x.c                |  96 +++++----
 drivers/input/touchscreen/ilitek_ts_i2c.c          |  10 +-
 drivers/input/touchscreen/inexio.c                 |   2 +-
 drivers/input/touchscreen/mtouch.c                 |   2 +-
 drivers/input/touchscreen/novatek-nvt-ts.c         |   5 -
 drivers/input/touchscreen/penmount.c               |   2 +-
 drivers/input/touchscreen/stmfts.c                 |  21 +-
 drivers/input/touchscreen/touchit213.c             |   4 +-
 drivers/input/touchscreen/touchright.c             |   2 +-
 drivers/input/touchscreen/touchwin.c               |   2 +-
 drivers/input/touchscreen/tsc40.c                  |   2 +-
 drivers/input/touchscreen/wdt87xx_i2c.c            |  14 +-
 include/linux/input/adp5589.h                      | 180 -----------------
 50 files changed, 518 insertions(+), 512 deletions(-)
 delete mode 100644 Documentation/devicetree/bindings/goldfish/events.txt
 create mode 100644 Documentation/devicetree/bindings/input/focaltech,ft8112.yaml
 create mode 100644 Documentation/devicetree/bindings/input/google,goldfish-events-keypad.yaml
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/ilitek,ili210x.yaml
 delete mode 100644 include/linux/input/adp5589.h

Thanks.


-- 
Dmitry

^ permalink raw reply

* [PATCH] iio: orientation: hid-sensor-rotation: use ext_scan_type
From: David Lechner @ 2026-02-14 21:25 UTC (permalink / raw)
  To: Jiri Kosina, Jonathan Cameron, Srinivas Pandruvada, Nuno Sá,
	Andy Shevchenko
  Cc: linux-input, linux-iio, linux-kernel, David Lechner

Make use of ext_scan_type to handle the dynamic realbits size of the
quaternion data. This lets us implement it using static data rather than
having to duplicate the channel info for each driver instance.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---
This is something I noticed we could do while looking at an unrelated
bug. I'm not sure I have hardware I could test this on, so it is only
compile-tested. It would be good to get a Tested-by: from someone before
applying this. Also, I'm not sure if 8, 16 and 32-bit data are all
possible, so if someone knows, please chime in.
---
 drivers/iio/orientation/hid-sensor-rotation.c | 81 ++++++++++++++++++---------
 1 file changed, 53 insertions(+), 28 deletions(-)

diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index e759f91a710a..2634ebf4eeb7 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -34,6 +34,35 @@ static const u32 rotation_sensitivity_addresses[] = {
 	HID_USAGE_SENSOR_ORIENT_QUATERNION,
 };
 
+enum {
+	DEV_ROT_SCAN_TYPE_8BIT,
+	DEV_ROT_SCAN_TYPE_16BIT,
+	DEV_ROT_SCAN_TYPE_32BIT,
+};
+
+static const struct iio_scan_type dev_rot_scan_types[] = {
+	[DEV_ROT_SCAN_TYPE_8BIT] = {
+		.sign = 's',
+		.realbits = 8,
+		/* Storage bits has to stay 32 to not break userspace. */
+		.storagebits = 32,
+		.repeat = 4,
+	},
+	[DEV_ROT_SCAN_TYPE_16BIT] = {
+		.sign = 's',
+		.realbits = 16,
+		/* Storage bits has to stay 32 to not break userspace. */
+		.storagebits = 32,
+		.repeat = 4,
+	},
+	[DEV_ROT_SCAN_TYPE_32BIT] = {
+		.sign = 's',
+		.realbits = 32,
+		.storagebits = 32,
+		.repeat = 4,
+	},
+};
+
 /* Channel definitions */
 static const struct iio_chan_spec dev_rot_channels[] = {
 	{
@@ -45,23 +74,14 @@ static const struct iio_chan_spec dev_rot_channels[] = {
 					BIT(IIO_CHAN_INFO_OFFSET) |
 					BIT(IIO_CHAN_INFO_SCALE) |
 					BIT(IIO_CHAN_INFO_HYSTERESIS),
-		.scan_index = 0
+		.scan_index = 0,
+		.has_ext_scan_type = 1,
+		.ext_scan_type = dev_rot_scan_types,
+		.num_ext_scan_type = ARRAY_SIZE(dev_rot_scan_types),
 	},
 	IIO_CHAN_SOFT_TIMESTAMP(1)
 };
 
-/* Adjust channel real bits based on report descriptor */
-static void dev_rot_adjust_channel_bit_mask(struct iio_chan_spec *chan,
-						int size)
-{
-	chan->scan_type.sign = 's';
-	/* Real storage bits will change based on the report desc. */
-	chan->scan_type.realbits = size * 8;
-	/* Maximum size of a sample to capture is u32 */
-	chan->scan_type.storagebits = sizeof(u32) * 8;
-	chan->scan_type.repeat = 4;
-}
-
 /* Channel read_raw handler */
 static int dev_rot_read_raw(struct iio_dev *indio_dev,
 				struct iio_chan_spec const *chan,
@@ -136,9 +156,27 @@ static int dev_rot_write_raw(struct iio_dev *indio_dev,
 	return ret;
 }
 
+static int dev_rot_get_current_scan_type(const struct iio_dev *indio_dev,
+					 const struct iio_chan_spec *chan)
+{
+	struct dev_rot_state *rot_state = iio_priv(indio_dev);
+
+	switch (rot_state->quaternion.size) {
+	case sizeof(s8) * 4:
+		return DEV_ROT_SCAN_TYPE_8BIT;
+	case sizeof(s16) * 4:
+		return DEV_ROT_SCAN_TYPE_16BIT;
+	case sizeof(s32) * 4:
+		return DEV_ROT_SCAN_TYPE_32BIT;
+	default:
+		return -EINVAL;
+	}
+}
+
 static const struct iio_info dev_rot_info = {
 	.read_raw_multi = &dev_rot_read_raw,
 	.write_raw = &dev_rot_write_raw,
+	.get_current_scan_type = &dev_rot_get_current_scan_type,
 };
 
 /* Callback handler to send event after all samples are received and captured */
@@ -196,7 +234,6 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev,
 /* Parse report which is specific to an usage id*/
 static int dev_rot_parse_report(struct platform_device *pdev,
 				struct hid_sensor_hub_device *hsdev,
-				struct iio_chan_spec *channels,
 				unsigned usage_id,
 				struct dev_rot_state *st)
 {
@@ -210,9 +247,6 @@ static int dev_rot_parse_report(struct platform_device *pdev,
 	if (ret)
 		return ret;
 
-	dev_rot_adjust_channel_bit_mask(&channels[0],
-		st->quaternion.size / 4);
-
 	dev_dbg(&pdev->dev, "dev_rot %x:%x\n", st->quaternion.index,
 		st->quaternion.report_id);
 
@@ -271,22 +305,13 @@ static int hid_dev_rot_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	indio_dev->channels = devm_kmemdup(&pdev->dev, dev_rot_channels,
-					   sizeof(dev_rot_channels),
-					   GFP_KERNEL);
-	if (!indio_dev->channels) {
-		dev_err(&pdev->dev, "failed to duplicate channels\n");
-		return -ENOMEM;
-	}
-
-	ret = dev_rot_parse_report(pdev, hsdev,
-				   (struct iio_chan_spec *)indio_dev->channels,
-					hsdev->usage, rot_state);
+	ret = dev_rot_parse_report(pdev, hsdev, hsdev->usage, rot_state);
 	if (ret) {
 		dev_err(&pdev->dev, "failed to setup attributes\n");
 		return ret;
 	}
 
+	indio_dev->channels = dev_rot_channels;
 	indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels);
 	indio_dev->info = &dev_rot_info;
 	indio_dev->name = name;

---
base-commit: 0f11bb7985ceef2aeeb5c45c3c7bfff3f5a16e03
change-id: 20260214-iio-hid-sensor-rotation-cleanup-84e8410926ef

Best regards,
-- 
David Lechner <dlechner@baylibre.com>


^ permalink raw reply related

* Re: [PATCH v2] bcm5974: recover from failed mode switch
From: Henrik Rydberg @ 2026-02-14 21:09 UTC (permalink / raw)
  To: Liam Mitchell, Henrik Rydberg, Dmitry Torokhov; +Cc: linux-input, linux-kernel
In-Reply-To: <20260213-bcm5974-reset-v2-1-1837851336b0@gmail.com>

Hi Liam,

thanks for seeing this through! Looks good to me.

Acked-by: Henrik Rydberg <rydberg@bitmath.org>

On 2/13/26 10:25 AM, Liam Mitchell wrote:
> Mode switches sent before control response are ignored.
> On receiving unknown 8-byte packets, assume that mode switch was ignored
> and reset by switching to normal mode, waiting then switching back to
> wellspring mode.
> 
> ---
> This patch addresses an issue where the bcm5974 driver switches modes
> before the device is ready, resulting in an unresponsive trackpad and
> "bcm5974: bad trackpad package, length: 8" repeated in logs.
> 
> Discussion of issue in the thread:
> https://lore.kernel.org/linux-input/CAOQ1CL4+DP1TuLAGNsz5GdFBTHvnTg=5q=Dr2Z1OQc6RXydSYA@mail.gmail.com/
> 
> This fix is conservative, avoiding changing existing mode-switch
> behavior because I cannot test all variations of hardware.
> 
> On receiving an unknown 8-byte packet, we assume the device is not in
> wellspring mode and schedule an asynchronous mode reset.
> 
> Signed-off-by: Liam Mitchell <mitchell.liam@gmail.com>
> Link: https://lore.kernel.org/linux-input/CAOQ1CL4+DP1TuLAGNsz5GdFBTHvnTg=5q=Dr2Z1OQc6RXydSYA@mail.gmail.com/
> ---
> Changes in v2:
> - mutex_lock -> guard(mutex)
> - dprintk -> dev_err
> - msleep -> fsleep
> - removed 0 init
> - cancel_work_sync -> disable_delayed_work_sync
> - work_struct -> delayed_work
> - Link to v1: https://lore.kernel.org/r/20260207-bcm5974-reset-v1-1-af7163903fa6@gmail.com
> ---
>   drivers/input/mouse/bcm5974.c | 40 +++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 39 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
> index dfdfb59cc8b5..6ee766ed8402 100644
> --- a/drivers/input/mouse/bcm5974.c
> +++ b/drivers/input/mouse/bcm5974.c
> @@ -286,6 +286,8 @@ struct bcm5974 {
>   	const struct tp_finger *index[MAX_FINGERS];	/* finger index data */
>   	struct input_mt_pos pos[MAX_FINGERS];		/* position array */
>   	int slots[MAX_FINGERS];				/* slot assignments */
> +	struct delayed_work mode_reset_work;
> +	unsigned long last_mode_reset;
>   };
>   
>   /* trackpad finger block data, le16-aligned */
> @@ -696,6 +698,32 @@ static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)
>   	return retval;
>   }
>   
> +/*
> + * Mode switches sent before the control response are ignored.
> + * Fixing this state requires switching to normal mode and waiting
> + * about 1ms before switching back to wellspring mode.
> + */
> +static void bcm5974_mode_reset_work(struct work_struct *work)
> +{
> +	int error;
> +	struct bcm5974 *dev = container_of(work, struct bcm5974, mode_reset_work.work);
> +
> +	guard(mutex)(&dev->pm_mutex);
> +	dev->last_mode_reset = jiffies;
> +
> +	error = bcm5974_wellspring_mode(dev, false);
> +	if (error) {
> +		dev_err(&dev->intf->dev, "reset to normal mode failed\n");
> +		return;
> +	}
> +
> +	fsleep(1000);
> +
> +	error = bcm5974_wellspring_mode(dev, true);
> +	if (error)
> +		dev_err(&dev->intf->dev, "mode switch after reset failed\n");
> +}
> +
>   static void bcm5974_irq_button(struct urb *urb)
>   {
>   	struct bcm5974 *dev = urb->context;
> @@ -752,10 +780,18 @@ static void bcm5974_irq_trackpad(struct urb *urb)
>   	if (dev->tp_urb->actual_length == 2)
>   		goto exit;
>   
> -	if (report_tp_state(dev, dev->tp_urb->actual_length))
> +	if (report_tp_state(dev, dev->tp_urb->actual_length)) {
>   		dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
>   			dev->tp_urb->actual_length);
>   
> +		/* HID packet means we aren't in wellspring mode */
> +		/* If we haven't tried a reset in the last second, try now */
> +		if (dev->tp_urb->actual_length == 8 &&
> +		    time_after(jiffies, dev->last_mode_reset + msecs_to_jiffies(1000))) {
> +			schedule_delayed_work(&dev->mode_reset_work, 0);
> +		}
> +	}
> +
>   exit:
>   	error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
>   	if (error)
> @@ -906,6 +942,7 @@ static int bcm5974_probe(struct usb_interface *iface,
>   	dev->intf = iface;
>   	dev->input = input_dev;
>   	dev->cfg = *cfg;
> +	INIT_DELAYED_WORK(&dev->mode_reset_work, bcm5974_mode_reset_work);
>   	mutex_init(&dev->pm_mutex);
>   
>   	/* setup urbs */
> @@ -998,6 +1035,7 @@ static void bcm5974_disconnect(struct usb_interface *iface)
>   {
>   	struct bcm5974 *dev = usb_get_intfdata(iface);
>   
> +	disable_delayed_work_sync(&dev->mode_reset_work);
>   	usb_set_intfdata(iface, NULL);
>   
>   	input_unregister_device(dev->input);
> 
> ---
> base-commit: 7d0a66e4bb9081d75c82ec4957c50034cb0ea449
> change-id: 20260207-bcm5974-reset-85ccdfca9641
> 
> Best regards,


^ permalink raw reply

* [PATCH 2/2] iio: orientation: hid-sensor-rotation: fix quaternion alignment
From: David Lechner @ 2026-02-14 21:00 UTC (permalink / raw)
  To: Jonathan Cameron, Nuno Sá, Andy Shevchenko, Jiri Kosina,
	Srinivas Pandruvada
  Cc: linux-iio, linux-kernel, Jonathan Cameron, linux-input,
	David Lechner, Lixu Zhang
In-Reply-To: <20260214-iio-fix-repeat-alignment-v1-0-47f01288c803@baylibre.com>

Restore the alignment of sampled_vals to 16 bytes by using
IIO_DECLARE_REPEATED_ELEMENT(). This field contains a quaternion value
which scan_type.repeat = 4 and storagebits = 32. So the alignment must
be 16 bytes to match the assumptions of iio_storage_bytes_for_si() and
also to not break userspace.

Reported-by: Lixu Zhang <lixu.zhang@intel.com>
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221077
Fixes: b31a74075cb4 ("iio: orientation: hid-sensor-rotation: remove unnecessary alignment")
Signed-off-by: David Lechner <dlechner@baylibre.com>
---
 drivers/iio/orientation/hid-sensor-rotation.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index e759f91a710a..9df955ecfbac 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -19,7 +19,7 @@ struct dev_rot_state {
 	struct hid_sensor_common common_attributes;
 	struct hid_sensor_hub_attribute_info quaternion;
 	struct {
-		s32 sampled_vals[4];
+		IIO_DECLARE_REPEATED_ELEMENT(s32, sampled_vals, 4);
 		aligned_s64 timestamp;
 	} scan;
 	int scale_pre_decml;

-- 
2.43.0


^ permalink raw reply related

* [PATCH 1/2] iio: add IIO_DECLARE_REPEATED_ELEMENT() macro
From: David Lechner @ 2026-02-14 21:00 UTC (permalink / raw)
  To: Jonathan Cameron, Nuno Sá, Andy Shevchenko, Jiri Kosina,
	Srinivas Pandruvada
  Cc: linux-iio, linux-kernel, Jonathan Cameron, linux-input,
	David Lechner
In-Reply-To: <20260214-iio-fix-repeat-alignment-v1-0-47f01288c803@baylibre.com>

Add a new IIO_DECLARE_REPEATED_ELEMENT() macro that is used to declare
the field in an IIO buffer struct that contains a repeated element.

There are only a few iio drivers that actually make use of the .repeat
feature of struct iio_scan_type. This has an implicit rule that the
element in the buffer must be aligned to the entire size of the repeated
element. This macro will make that requirement explicit.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---
 include/linux/iio/iio.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index 872ebdf0dd77..28b708166b9b 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -816,6 +816,19 @@ static inline void *iio_device_get_drvdata(const struct iio_dev *indio_dev)
 #define IIO_DECLARE_DMA_BUFFER_WITH_TS(type, name, count) \
 	__IIO_DECLARE_BUFFER_WITH_TS(type, name, count) __aligned(IIO_DMA_MINALIGN)
 
+/**
+ * IIO_DECLARE_REPEATED_ELEMENT() - Declare a repeated element
+ * @type: element type of the repeated element
+ * @name: identifier name of the repeated element
+ * @repeat: number of times the element is repeated
+ *
+ * For special cases with repeated elements, like IIO_MOT_QUATERNION, a multi-
+ * word element is treated as a single element of a larger size in the buffer.
+ * As such, it requires alignment to the size of the entire repeated element.
+ */
+#define IIO_DECLARE_REPEATED_ELEMENT(type, name, repeat) \
+	type name[repeat] __aligned(sizeof(type) * repeat)
+
 struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv);
 
 /* The information at the returned address is guaranteed to be cacheline aligned */

-- 
2.43.0


^ permalink raw reply related

* [PATCH 0/2] iio: orientation: hid-sensor-rotation: fix quaternion alignment
From: David Lechner @ 2026-02-14 21:00 UTC (permalink / raw)
  To: Jonathan Cameron, Nuno Sá, Andy Shevchenko, Jiri Kosina,
	Srinivas Pandruvada
  Cc: linux-iio, linux-kernel, Jonathan Cameron, linux-input,
	David Lechner, Lixu Zhang

The main point of this series is to fix a regression reported in
hid-sensor-rotation where the alignment of the quaternion field in the
data was inadvertently changed from 16 bytes to 8 bytes. This is an
unusually case (one of only 2 in the kernel) where the .repeat field of
struct iio_scan_type is used and we have such a requirement. (The other
case uses u16 instead of u32, so it wasn't affected.)

To make the reason for the alignment more explicit to future readers,
we introduce a new macro, IIO_DECLARE_REPEATED_ELEMENT, to declare the
array with proper allignment. This is meant to follow the pattern of
the similar IIO_DECLARE_BUFFER_WITH_TS() macro.

Signed-off-by: David Lechner <dlechner@baylibre.com>
---
David Lechner (2):
      iio: add IIO_DECLARE_REPEATED_ELEMENT() macro
      iio: orientation: hid-sensor-rotation: fix quaternion alignment

 drivers/iio/orientation/hid-sensor-rotation.c |  2 +-
 include/linux/iio/iio.h                       | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)
---
base-commit: 0f11bb7985ceef2aeeb5c45c3c7bfff3f5a16e03
change-id: 20260214-iio-fix-repeat-alignment-575b2c009e25

Best regards,
-- 
David Lechner <dlechner@baylibre.com>


^ permalink raw reply

* [PATCH] Input: libps2 - embed WARN_ON(1) macros into their enclosing if statements
From: Max Brener @ 2026-02-14 20:37 UTC (permalink / raw)
  To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Max Brener

Make WARN_ON(1) statements embedded inside their respective 'if' expressions,
to improve code clarity.

Signed-off-by: Max Brener <linmaxi@gmail.com>
---
 drivers/input/serio/libps2.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index 269df83a167d..05b64277aecd 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -154,10 +154,8 @@ EXPORT_SYMBOL(ps2_end_command);
  */
 void ps2_drain(struct ps2dev *ps2dev, size_t maxbytes, unsigned int timeout)
 {
-	if (maxbytes > sizeof(ps2dev->cmdbuf)) {
-		WARN_ON(1);
+	if (WARN_ON(maxbytes > sizeof(ps2dev->cmdbuf)))
 		maxbytes = sizeof(ps2dev->cmdbuf);
-	}
 
 	ps2_begin_command(ps2dev);
 
@@ -270,15 +268,11 @@ int __ps2_command(struct ps2dev *ps2dev, u8 *param, unsigned int command)
 	int i;
 	u8 send_param[16];
 
-	if (receive > sizeof(ps2dev->cmdbuf)) {
-		WARN_ON(1);
+	if (WARN_ON(receive > sizeof(ps2dev->cmdbuf)))
 		return -EINVAL;
-	}
 
-	if (send && !param) {
-		WARN_ON(1);
+	if (WARN_ON(send && !param))
 		return -EINVAL;
-	}
 
 	memcpy(send_param, param, send);
 
-- 
2.43.0


^ permalink raw reply related

* [PATCH] HID: magicmouse: fix battery reporting for Apple Magic Trackpad 2
From: Julius Lehmann @ 2026-02-14 19:34 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires; +Cc: linux-input, linux-kernel, Julius Lehmann

Battery reporting does not work for the Apple Magic Trackpad 2 if it is
connected via USB. The current hid descriptor fixup code checks for a
hid descriptor length of exactly 83 bytes. If the hid descriptor is
larger, which is the case for newer apple mice, the fixup is not
applied.

This fix checks for hid descriptor sizes greater/equal 83 bytes which
applies the fixup for newer devices as well.

---
Signed-off-by: Julius Lehmann <lehmanju@devpi.de>
---
 drivers/hid/hid-magicmouse.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 7d4a25c6de0eb7d36ad26a867004d58a1b6eae71..3fe191615a6d39fbe823e9e1a80fc65a7a12b9c4 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -985,7 +985,7 @@ static const __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
 	 */
 	if ((is_usb_magicmouse2(hdev->vendor, hdev->product) ||
 	     is_usb_magictrackpad2(hdev->vendor, hdev->product)) &&
-	    *rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) {
+	    *rsize >= 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) {
 		hid_info(hdev,
 			 "fixing up magicmouse battery report descriptor\n");
 		*rsize = *rsize - 1;

---
base-commit: d3eeb99bbc99cc5eb94a4a75ed4415a0272254ef
change-id: 20260214-magic-trackpad-usb-battery-5a31b8c62d4f

Best regards,
-- 
Julius Lehmann <lehmanju@devpi.de>


^ permalink raw reply related

* Re: [PATCH v2 1/1] HID: switch2: Add preliminary Switch 2 controller driver
From: Joshua Peisach @ 2026-02-14 19:34 UTC (permalink / raw)
  To: Vicki Pfau, Dmitry Torokhov, Jiri Kosina, Benjamin Tissoires,
	linux-input
  Cc: Luiz Augusto von Dentz, Bastien Nocera
In-Reply-To: <20260109034034.565630-2-vi@endrift.com>

On Thu Jan 8, 2026 at 10:40 PM EST, Vicki Pfau wrote:
> +static const struct switch2_ctlr_button_mapping right_joycon_button_mappings[] = {
> +	{ BTN_SOUTH,	0, NS2_BTNR_A,		},
> +	{ BTN_EAST,	0, NS2_BTNR_B,		},
> +	{ BTN_NORTH,	0, NS2_BTNR_X,		},
> +	{ BTN_WEST,	0, NS2_BTNR_Y,		},

Wouldn't it make more sense for South to be B, and East to be A (and
so on?)

--
Joshua Peisach

^ permalink raw reply

* [PATCH v3 7/7] power: supply: Add charger driver for Asus Transformers
From: Svyatoslav Ryhel @ 2026-02-14 18:09 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov,
	Lee Jones, Pavel Machek, Sebastian Reichel, Svyatoslav Ryhel,
	Ion Agorria, Michał Mirosław
  Cc: devicetree, linux-kernel, linux-input, linux-leds, linux-pm
In-Reply-To: <20260214180959.30714-1-clamor95@gmail.com>

From: Michał Mirosław <mirq-linux@rere.qmqm.pl>

Add support for charger detection capabilities found in the embedded
controller of ASUS Transformer devices.

Suggested-by: Maxim Schwalm <maxim.schwalm@gmail.com>
Suggested-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/power/supply/Kconfig                  |  11 +
 drivers/power/supply/Makefile                 |   1 +
 .../supply/asus-transformer-ec-charger.c      | 193 ++++++++++++++++++
 3 files changed, 205 insertions(+)
 create mode 100644 drivers/power/supply/asus-transformer-ec-charger.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 3c46b412632d..56800aab82f9 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -497,6 +497,17 @@ config CHARGER_88PM860X
 	help
 	  Say Y here to enable charger for Marvell 88PM860x chip.
 
+config CHARGER_ASUS_TRANSFORMER_EC
+	tristate "Asus Transformer's charger driver"
+	depends on MFD_ASUS_TRANSFORMER_EC
+	help
+	  Say Y here to enable support AC plug detection on Asus Transformer
+	  Dock.
+
+	  This sub-driver supports charger detection mechanism found in Asus
+	  Transformer tablets and mobile docks and controlled by special
+	  embedded controller.
+
 config CHARGER_PF1550
 	tristate "NXP PF1550 battery charger driver"
 	depends on MFD_PF1550
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index aa5e6b05b018..24679f09bb61 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_CHARGER_RT9471)	+= rt9471.o
 obj-$(CONFIG_CHARGER_RT9756)	+= rt9756.o
 obj-$(CONFIG_BATTERY_TWL4030_MADC)	+= twl4030_madc_battery.o
 obj-$(CONFIG_CHARGER_88PM860X)	+= 88pm860x_charger.o
+obj-$(CONFIG_CHARGER_ASUS_TRANSFORMER_EC)	+= asus-transformer-ec-charger.o
 obj-$(CONFIG_CHARGER_PF1550)	+= pf1550-charger.o
 obj-$(CONFIG_BATTERY_RX51)	+= rx51_battery.o
 obj-$(CONFIG_AB8500_BM)		+= ab8500_bmdata.o ab8500_charger.o ab8500_fg.o ab8500_btemp.o ab8500_chargalg.o
diff --git a/drivers/power/supply/asus-transformer-ec-charger.c b/drivers/power/supply/asus-transformer-ec-charger.c
new file mode 100644
index 000000000000..de01f0bf2fd7
--- /dev/null
+++ b/drivers/power/supply/asus-transformer-ec-charger.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/err.h>
+#include <linux/mfd/asus-transformer-ec.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/property.h>
+
+struct asus_ec_charger_data {
+	struct notifier_block nb;
+	const struct asusec_info *ec;
+	struct power_supply *psy;
+	struct power_supply_desc psy_desc;
+};
+
+static enum power_supply_property asus_ec_charger_properties[] = {
+	POWER_SUPPLY_PROP_USB_TYPE,
+	POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+static int asus_ec_charger_get_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					union power_supply_propval *val)
+{
+	struct asus_ec_charger_data *priv = power_supply_get_drvdata(psy);
+	enum power_supply_usb_type psu;
+	int ret;
+	u64 ctl;
+
+	ret = asus_ec_get_ctl(priv->ec, &ctl);
+	if (ret)
+		return ret;
+
+	switch (ctl & (ASUSEC_CTL_FULL_POWER_SOURCE | ASUSEC_CTL_DIRECT_POWER_SOURCE)) {
+	case ASUSEC_CTL_FULL_POWER_SOURCE:
+		psu = POWER_SUPPLY_USB_TYPE_CDP;	/* DOCK */
+		break;
+	case ASUSEC_CTL_DIRECT_POWER_SOURCE:
+		psu = POWER_SUPPLY_USB_TYPE_SDP;	/* USB */
+		break;
+	case 0:
+		psu = POWER_SUPPLY_USB_TYPE_UNKNOWN;	/* no power source connected */
+		break;
+	default:
+		psu = POWER_SUPPLY_USB_TYPE_ACA;	/* power adapter */
+		break;
+	}
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = psu != POWER_SUPPLY_USB_TYPE_UNKNOWN;
+		return 0;
+
+	case POWER_SUPPLY_PROP_USB_TYPE:
+		val->intval = psu;
+		return 0;
+
+	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+		if (ctl & ASUSEC_CTL_TEST_DISCHARGE)
+			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
+		else if (ctl & ASUSEC_CTL_USB_CHARGE)
+			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
+		else
+			val->intval = POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
+		return 0;
+
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = priv->ec->model;
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int asus_ec_charger_set_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					const union power_supply_propval *val)
+{
+	struct asus_ec_charger_data *priv = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+		switch ((enum power_supply_charge_behaviour)val->intval) {
+		case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
+			return asus_ec_update_ctl(priv->ec,
+				ASUSEC_CTL_TEST_DISCHARGE | ASUSEC_CTL_USB_CHARGE,
+				ASUSEC_CTL_USB_CHARGE);
+
+		case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
+			return asus_ec_clear_ctl_bits(priv->ec,
+				ASUSEC_CTL_TEST_DISCHARGE | ASUSEC_CTL_USB_CHARGE);
+
+		case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE:
+			return asus_ec_update_ctl(priv->ec,
+				ASUSEC_CTL_TEST_DISCHARGE | ASUSEC_CTL_USB_CHARGE,
+				ASUSEC_CTL_TEST_DISCHARGE);
+		default:
+			return -EINVAL;
+		}
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int asus_ec_charger_property_is_writeable(struct power_supply *psy,
+						 enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct power_supply_desc asus_ec_charger_desc = {
+	.name = "asus-ec-charger",
+	.type = POWER_SUPPLY_TYPE_USB,
+	.charge_behaviours = BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |
+			     BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) |
+			     BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE),
+	.usb_types = BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN) |
+		     BIT(POWER_SUPPLY_USB_TYPE_SDP) |
+		     BIT(POWER_SUPPLY_USB_TYPE_CDP) |
+		     BIT(POWER_SUPPLY_USB_TYPE_ACA),
+	.properties = asus_ec_charger_properties,
+	.num_properties = ARRAY_SIZE(asus_ec_charger_properties),
+	.get_property = asus_ec_charger_get_property,
+	.set_property = asus_ec_charger_set_property,
+	.property_is_writeable = asus_ec_charger_property_is_writeable,
+	.no_thermal = true,
+};
+
+static int asus_ec_charger_notify(struct notifier_block *nb,
+				  unsigned long action, void *data)
+{
+	struct asus_ec_charger_data *priv =
+		container_of(nb, struct asus_ec_charger_data, nb);
+
+	switch (action) {
+	case ASUSEC_SMI_ACTION(POWER_NOTIFY):
+	case ASUSEC_SMI_ACTION(ADAPTER_EVENT):
+		power_supply_changed(priv->psy);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int asus_ec_charger_probe(struct platform_device *pdev)
+{
+	struct asus_ec_charger_data *priv;
+	struct device *dev = &pdev->dev;
+	struct power_supply_config cfg = { };
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+	priv->ec = cell_to_ec(pdev);
+
+	cfg.fwnode = dev_fwnode(dev->parent);
+	cfg.drv_data = priv;
+
+	memcpy(&priv->psy_desc, &asus_ec_charger_desc, sizeof(priv->psy_desc));
+	priv->psy_desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s-charger",
+					     priv->ec->name);
+
+	priv->psy = devm_power_supply_register(dev, &priv->psy_desc, &cfg);
+	if (IS_ERR(priv->psy))
+		return dev_err_probe(dev, PTR_ERR(priv->psy),
+				     "Failed to register power supply\n");
+
+	priv->nb.notifier_call = asus_ec_charger_notify;
+
+	return devm_asus_ec_register_notifier(pdev, &priv->nb);
+}
+
+static struct platform_driver asus_ec_charger_driver = {
+	.driver.name = "asus-transformer-ec-charger",
+	.probe = asus_ec_charger_probe,
+};
+module_platform_driver(asus_ec_charger_driver);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_DESCRIPTION("ASUS Transformer Pad battery charger driver");
+MODULE_LICENSE("GPL");
-- 
2.51.0


^ permalink raw reply related

* [PATCH v3 6/7] power: supply: Add driver for ASUS Transformer battery
From: Svyatoslav Ryhel @ 2026-02-14 18:09 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov,
	Lee Jones, Pavel Machek, Sebastian Reichel, Svyatoslav Ryhel,
	Ion Agorria, Michał Mirosław
  Cc: devicetree, linux-kernel, linux-input, linux-leds, linux-pm
In-Reply-To: <20260214180959.30714-1-clamor95@gmail.com>

From: Michał Mirosław <mirq-linux@rere.qmqm.pl>

Driver implements one battery cell per EC controller and supports reading
of battery status for ASUS Transformer's pad and mobile dock.

Co-developed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
 drivers/power/supply/Kconfig                  |  11 +
 drivers/power/supply/Makefile                 |   1 +
 .../supply/asus-transformer-ec-battery.c      | 272 ++++++++++++++++++
 3 files changed, 284 insertions(+)
 create mode 100644 drivers/power/supply/asus-transformer-ec-battery.c

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 81fadb0695a9..3c46b412632d 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -122,6 +122,17 @@ config BATTERY_CHAGALL
 	  This driver can also be built as a module. If so, the module will be
 	  called chagall-battery.
 
+config BATTERY_ASUS_TRANSFORMER_EC
+	tristate "Asus Transformer's battery driver"
+	depends on MFD_ASUS_TRANSFORMER_EC
+	help
+	  Say Y here to enable support APM status emulation using
+	  battery class devices.
+
+	  This sub-driver supports battery cells found in Asus Transformer
+	  tablets and mobile docks and controlled by special embedded
+	  controller.
+
 config BATTERY_CPCAP
 	tristate "Motorola CPCAP PMIC battery driver"
 	depends on MFD_CPCAP && IIO
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 41c400bbf022..aa5e6b05b018 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_TEST_POWER)	+= test_power.o
 obj-$(CONFIG_BATTERY_88PM860X)	+= 88pm860x_battery.o
 obj-$(CONFIG_CHARGER_ADP5061)	+= adp5061.o
 obj-$(CONFIG_BATTERY_ACT8945A)	+= act8945a_charger.o
+obj-$(CONFIG_BATTERY_ASUS_TRANSFORMER_EC)	+= asus-transformer-ec-battery.o
 obj-$(CONFIG_BATTERY_AXP20X)	+= axp20x_battery.o
 obj-$(CONFIG_CHARGER_AXP20X)	+= axp20x_ac_power.o
 obj-$(CONFIG_BATTERY_CHAGALL)	+= chagall-battery.o
diff --git a/drivers/power/supply/asus-transformer-ec-battery.c b/drivers/power/supply/asus-transformer-ec-battery.c
new file mode 100644
index 000000000000..aefcd3fed6fe
--- /dev/null
+++ b/drivers/power/supply/asus-transformer-ec-battery.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/array_size.h>
+#include <linux/devm-helpers.h>
+#include <linux/err.h>
+#include <linux/mfd/asus-transformer-ec.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/property.h>
+#include <linux/unaligned.h>
+
+#define ASUSEC_BATTERY_DATA_FRESH_MSEC		5000
+
+#define ASUSEC_BATTERY_DISCHARGING		0x40
+#define ASUSEC_BATTERY_FULL_CHARGED		0x20
+#define ASUSEC_BATTERY_NOT_CHARGING		0x10
+
+#define TEMP_CELSIUS_OFFSET			2731
+
+struct asus_ec_battery_data {
+	const struct asusec_info *ec;
+	struct power_supply *battery;
+	struct power_supply_desc psy_desc;
+	struct delayed_work poll_work;
+	struct mutex battery_lock; /* for data refresh */
+	unsigned long batt_data_ts;
+	int last_state;
+	u8 batt_data[DOCKRAM_ENTRY_BUFSIZE];
+};
+
+static int asus_ec_battery_refresh(struct asus_ec_battery_data *priv)
+{
+	int ret = 0;
+
+	guard(mutex)(&priv->battery_lock);
+
+	if (time_before(jiffies, priv->batt_data_ts))
+		return ret;
+
+	ret = asus_dockram_read(priv->ec->dockram, ASUSEC_DOCKRAM_BATT_CTL,
+				priv->batt_data);
+	if (ret < 0)
+		return ret;
+
+	priv->batt_data_ts = jiffies +
+		msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC);
+
+	return ret;
+}
+
+static enum power_supply_property asus_ec_battery_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+	POWER_SUPPLY_PROP_PRESENT,
+};
+
+static const unsigned int asus_ec_battery_prop_offs[] = {
+	[POWER_SUPPLY_PROP_STATUS] = 1,
+	[POWER_SUPPLY_PROP_VOLTAGE_MAX] = 3,
+	[POWER_SUPPLY_PROP_CURRENT_MAX] = 5,
+	[POWER_SUPPLY_PROP_TEMP] = 7,
+	[POWER_SUPPLY_PROP_VOLTAGE_NOW] = 9,
+	[POWER_SUPPLY_PROP_CURRENT_NOW] = 11,
+	[POWER_SUPPLY_PROP_CAPACITY] = 13,
+	[POWER_SUPPLY_PROP_CHARGE_NOW] = 15,
+	[POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW] = 17,
+	[POWER_SUPPLY_PROP_TIME_TO_FULL_NOW] = 19,
+};
+
+static int asus_ec_battery_get_value(struct asus_ec_battery_data *priv,
+				     enum power_supply_property psp)
+{
+	int ret, offs;
+
+	if (psp >= ARRAY_SIZE(asus_ec_battery_prop_offs))
+		return -EINVAL;
+
+	offs = asus_ec_battery_prop_offs[psp];
+	if (!offs)
+		return -EINVAL;
+
+	ret = asus_ec_battery_refresh(priv);
+	if (ret < 0)
+		return ret;
+
+	if (offs >= priv->batt_data[0])
+		return -ENODATA;
+
+	return get_unaligned_le16(priv->batt_data + offs);
+}
+
+static int asus_ec_battery_get_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					union power_supply_propval *val)
+{
+	struct asus_ec_battery_data *priv = power_supply_get_drvdata(psy);
+	int ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+
+	default:
+		ret = asus_ec_battery_get_value(priv, psp);
+		if (ret < 0)
+			return ret;
+
+		val->intval = (s16)ret;
+
+		switch (psp) {
+		case POWER_SUPPLY_PROP_STATUS:
+			if (ret & ASUSEC_BATTERY_FULL_CHARGED)
+				val->intval = POWER_SUPPLY_STATUS_FULL;
+			else if (ret & ASUSEC_BATTERY_NOT_CHARGING)
+				val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+			else if (ret & ASUSEC_BATTERY_DISCHARGING)
+				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+			else
+				val->intval = POWER_SUPPLY_STATUS_CHARGING;
+			break;
+
+		case POWER_SUPPLY_PROP_TEMP:
+			val->intval -= TEMP_CELSIUS_OFFSET;
+			break;
+
+		case POWER_SUPPLY_PROP_CHARGE_NOW:
+		case POWER_SUPPLY_PROP_CURRENT_NOW:
+		case POWER_SUPPLY_PROP_CURRENT_MAX:
+		case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+			val->intval *= 1000;
+			break;
+
+		case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+		case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
+			val->intval *= 60;
+			break;
+
+		default:
+			break;
+		}
+
+		break;
+	}
+
+	return 0;
+}
+
+static void asus_ec_battery_poll_work(struct work_struct *work)
+{
+	struct asus_ec_battery_data *priv =
+		container_of(work, struct asus_ec_battery_data, poll_work.work);
+	int state;
+
+	state = asus_ec_battery_get_value(priv, POWER_SUPPLY_PROP_STATUS);
+	if (state < 0)
+		return;
+
+	if (state & ASUSEC_BATTERY_FULL_CHARGED)
+		state = POWER_SUPPLY_STATUS_FULL;
+	else if (state & ASUSEC_BATTERY_DISCHARGING)
+		state = POWER_SUPPLY_STATUS_DISCHARGING;
+	else
+		state = POWER_SUPPLY_STATUS_CHARGING;
+
+	if (priv->last_state != state) {
+		priv->last_state = state;
+		power_supply_changed(priv->battery);
+	}
+
+	/* continuously send uevent notification */
+	schedule_delayed_work(&priv->poll_work,
+			      msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC));
+}
+
+static const struct power_supply_desc asus_ec_battery_desc = {
+	.name = "asus-ec-battery",
+	.type = POWER_SUPPLY_TYPE_BATTERY,
+	.properties = asus_ec_battery_properties,
+	.num_properties = ARRAY_SIZE(asus_ec_battery_properties),
+	.get_property = asus_ec_battery_get_property,
+	.external_power_changed = power_supply_changed,
+};
+
+static int asus_ec_battery_probe(struct platform_device *pdev)
+{
+	struct asus_ec_battery_data *priv;
+	struct device *dev = &pdev->dev;
+	struct power_supply_config cfg = { };
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	mutex_init(&priv->battery_lock);
+
+	priv->ec = cell_to_ec(pdev);
+	priv->batt_data_ts = jiffies - 1;
+	priv->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
+
+	cfg.fwnode = dev_fwnode(dev->parent);
+	cfg.drv_data = priv;
+
+	memcpy(&priv->psy_desc, &asus_ec_battery_desc, sizeof(priv->psy_desc));
+	priv->psy_desc.name = devm_kasprintf(dev, GFP_KERNEL, "%s-battery",
+					     priv->ec->name);
+
+	priv->battery = devm_power_supply_register(dev, &priv->psy_desc, &cfg);
+	if (IS_ERR(priv->battery))
+		return dev_err_probe(dev, PTR_ERR(priv->battery),
+				     "Failed to register power supply\n");
+
+	ret = devm_delayed_work_autocancel(dev, &priv->poll_work,
+					   asus_ec_battery_poll_work);
+	if (ret)
+		return ret;
+
+	schedule_delayed_work(&priv->poll_work,
+			      msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC));
+
+	return 0;
+}
+
+static int __maybe_unused asus_ec_battery_suspend(struct device *dev)
+{
+	struct asus_ec_battery_data *priv = dev_get_drvdata(dev);
+
+	cancel_delayed_work_sync(&priv->poll_work);
+
+	return 0;
+}
+
+static int __maybe_unused asus_ec_battery_resume(struct device *dev)
+{
+	struct asus_ec_battery_data *priv = dev_get_drvdata(dev);
+
+	schedule_delayed_work(&priv->poll_work,
+			      msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC));
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(asus_ec_battery_pm_ops,
+			 asus_ec_battery_suspend, asus_ec_battery_resume);
+
+static struct platform_driver asus_ec_battery_driver = {
+	.driver = {
+		.name = "asus-transformer-ec-battery",
+		.pm = &asus_ec_battery_pm_ops,
+	},
+	.probe = asus_ec_battery_probe,
+};
+module_platform_driver(asus_ec_battery_driver);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("ASUS Transformer's battery driver");
+MODULE_LICENSE("GPL");
-- 
2.51.0


^ permalink raw reply related

* [PATCH v3 5/7] leds: Add driver for ASUS Transformer LEDs
From: Svyatoslav Ryhel @ 2026-02-14 18:09 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov,
	Lee Jones, Pavel Machek, Sebastian Reichel, Svyatoslav Ryhel,
	Ion Agorria, Michał Mirosław
  Cc: devicetree, linux-kernel, linux-input, linux-leds, linux-pm
In-Reply-To: <20260214180959.30714-1-clamor95@gmail.com>

From: Michał Mirosław <mirq-linux@rere.qmqm.pl>

ASUS Transformer tablets have a green and an amber LED on both the Pad
and the Dock. If both LEDs are enabled simultaneously, the emitted light
will be yellow.

Co-developed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
 drivers/leds/Kconfig                    | 11 ++++
 drivers/leds/Makefile                   |  1 +
 drivers/leds/leds-asus-transformer-ec.c | 79 +++++++++++++++++++++++++
 3 files changed, 91 insertions(+)
 create mode 100644 drivers/leds/leds-asus-transformer-ec.c

diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 597d7a79c988..bda06dc145d7 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -120,6 +120,17 @@ config LEDS_OSRAM_AMS_AS3668
 	  To compile this driver as a module, choose M here: the module
 	  will be called leds-as3668.
 
+config LEDS_ASUS_TRANSFORMER_EC
+	tristate "LED Support for Asus Transformer charging LED"
+	depends on LEDS_CLASS
+	depends on MFD_ASUS_TRANSFORMER_EC
+	help
+	  This option enables support for charging indicator on
+	  Asus Transformer's Pad and it's Dock.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called leds-asus-transformer-ec.
+
 config LEDS_AW200XX
 	tristate "LED support for Awinic AW20036/AW20054/AW20072/AW20108"
 	depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 8fdb45d5b439..d5395c3f1124 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_LEDS_AN30259A)		+= leds-an30259a.o
 obj-$(CONFIG_LEDS_APU)			+= leds-apu.o
 obj-$(CONFIG_LEDS_ARIEL)		+= leds-ariel.o
 obj-$(CONFIG_LEDS_AS3668)		+= leds-as3668.o
+obj-$(CONFIG_LEDS_ASUS_TRANSFORMER_EC)	+= leds-asus-transformer-ec.o
 obj-$(CONFIG_LEDS_AW200XX)		+= leds-aw200xx.o
 obj-$(CONFIG_LEDS_AW2013)		+= leds-aw2013.o
 obj-$(CONFIG_LEDS_BCM6328)		+= leds-bcm6328.o
diff --git a/drivers/leds/leds-asus-transformer-ec.c b/drivers/leds/leds-asus-transformer-ec.c
new file mode 100644
index 000000000000..3186038e3be7
--- /dev/null
+++ b/drivers/leds/leds-asus-transformer-ec.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/err.h>
+#include <linux/leds.h>
+#include <linux/mfd/asus-transformer-ec.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+static void asus_ec_led_set_brightness_amber(struct led_classdev *led,
+					     enum led_brightness brightness)
+{
+	const struct asusec_info *ec = dev_get_drvdata(led->dev->parent);
+
+	if (brightness)
+		asus_ec_set_ctl_bits(ec, ASUSEC_CTL_LED_AMBER);
+	else
+		asus_ec_clear_ctl_bits(ec, ASUSEC_CTL_LED_AMBER);
+}
+
+static void asus_ec_led_set_brightness_green(struct led_classdev *led,
+					     enum led_brightness brightness)
+{
+	const struct asusec_info *ec = dev_get_drvdata(led->dev->parent);
+
+	if (brightness)
+		asus_ec_set_ctl_bits(ec, ASUSEC_CTL_LED_GREEN);
+	else
+		asus_ec_clear_ctl_bits(ec, ASUSEC_CTL_LED_GREEN);
+}
+
+static int asus_ec_led_probe(struct platform_device *pdev)
+{
+	struct asusec_info *ec = cell_to_ec(pdev);
+	struct device *dev = &pdev->dev;
+	struct led_classdev *amber_led, *green_led;
+	int ret;
+
+	platform_set_drvdata(pdev, ec);
+
+	amber_led = devm_kzalloc(dev, sizeof(*amber_led), GFP_KERNEL);
+	if (!amber_led)
+		return -ENOMEM;
+
+	amber_led->name = devm_kasprintf(dev, GFP_KERNEL, "%s::amber", ec->name);
+	amber_led->max_brightness = 1;
+	amber_led->flags = LED_CORE_SUSPENDRESUME | LED_RETAIN_AT_SHUTDOWN;
+	amber_led->brightness_set = asus_ec_led_set_brightness_amber;
+
+	ret = devm_led_classdev_register(dev, amber_led);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register amber LED\n");
+
+	green_led = devm_kzalloc(dev, sizeof(*green_led), GFP_KERNEL);
+	if (!green_led)
+		return -ENOMEM;
+
+	green_led->name = devm_kasprintf(dev, GFP_KERNEL, "%s::green", ec->name);
+	green_led->max_brightness = 1;
+	green_led->flags = LED_CORE_SUSPENDRESUME | LED_RETAIN_AT_SHUTDOWN;
+	green_led->brightness_set = asus_ec_led_set_brightness_green;
+
+	ret = devm_led_classdev_register(dev, green_led);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register green LED\n");
+
+	return 0;
+}
+
+static struct platform_driver asus_ec_led_driver = {
+	.driver.name = "asus-transformer-ec-led",
+	.probe = asus_ec_led_probe,
+};
+module_platform_driver(asus_ec_led_driver);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("ASUS Transformer's charging LED driver");
+MODULE_LICENSE("GPL");
-- 
2.51.0


^ permalink raw reply related

* [PATCH v3 4/7] input: keyboard: Add driver for ASUS Transformer dock multimedia keys
From: Svyatoslav Ryhel @ 2026-02-14 18:09 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov,
	Lee Jones, Pavel Machek, Sebastian Reichel, Svyatoslav Ryhel,
	Ion Agorria, Michał Mirosław
  Cc: devicetree, linux-kernel, linux-input, linux-leds, linux-pm
In-Reply-To: <20260214180959.30714-1-clamor95@gmail.com>

From: Michał Mirosław <mirq-linux@rere.qmqm.pl>

Add support for multimedia top button row of ASUS Transformer's Mobile
Dock keyboard. Driver is made that function keys (F1-F12) are used by
default which suits average Linux use better and with pressing
ScreenLock + AltGr function keys layout is switched to multimedia keys.
Since this only modifies codes sent by asus-ec-keys it doesn't affect
normal keyboards at all.

Co-developed-by: Ion Agorria <ion@agorria.com>
Signed-off-by: Ion Agorria <ion@agorria.com>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/input/keyboard/Kconfig                |  10 +
 drivers/input/keyboard/Makefile               |   1 +
 .../input/keyboard/asus-transformer-ec-keys.c | 272 ++++++++++++++++++
 3 files changed, 283 insertions(+)
 create mode 100644 drivers/input/keyboard/asus-transformer-ec-keys.c

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 2ff4fef322c2..4e577e5cf216 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -89,6 +89,16 @@ config KEYBOARD_APPLESPI
 	  To compile this driver as a module, choose M here: the
 	  module will be called applespi.
 
+config KEYBOARD_ASUS_TRANSFORMER_EC
+	tristate "Asus Transformer's Mobile Dock multimedia keys"
+	depends on MFD_ASUS_TRANSFORMER_EC
+	help
+	  Say Y here if you want to use multimedia keys present on Asus
+	  Transformer's Mobile Dock.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called asus-transformer-ec-keys.
+
 config KEYBOARD_ATARI
 	tristate "Atari keyboard"
 	depends on ATARI
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 2d906e14f3e2..575edb0e8eb4 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_ADP5585)		+= adp5585-keys.o
 obj-$(CONFIG_KEYBOARD_ADP5588)		+= adp5588-keys.o
 obj-$(CONFIG_KEYBOARD_AMIGA)		+= amikbd.o
 obj-$(CONFIG_KEYBOARD_APPLESPI)		+= applespi.o
+obj-$(CONFIG_KEYBOARD_ASUS_TRANSFORMER_EC)	+= asus-transformer-ec-keys.o
 obj-$(CONFIG_KEYBOARD_ATARI)		+= atakbd.o
 obj-$(CONFIG_KEYBOARD_ATKBD)		+= atkbd.o
 obj-$(CONFIG_KEYBOARD_BCM)		+= bcm-keypad.o
diff --git a/drivers/input/keyboard/asus-transformer-ec-keys.c b/drivers/input/keyboard/asus-transformer-ec-keys.c
new file mode 100644
index 000000000000..4cdebce58988
--- /dev/null
+++ b/drivers/input/keyboard/asus-transformer-ec-keys.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/array_size.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/mfd/asus-transformer-ec.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define ASUSEC_EXT_KEY_CODES		0x20
+
+struct asus_ec_keys_data {
+	struct notifier_block nb;
+	struct asusec_info *ec;
+	struct input_dev *xidev;
+	bool special_key_pressed;
+	bool special_key_mode;
+	unsigned short keymap[ASUSEC_EXT_KEY_CODES * 2];
+};
+
+static void asus_ec_input_event(struct input_handle *handle,
+				unsigned int event_type,
+				unsigned int event_code, int value)
+{
+	struct asus_ec_keys_data *priv = handle->handler->private;
+
+	/* Store special key state */
+	if (event_type == EV_KEY && event_code == KEY_RIGHTALT)
+		priv->special_key_pressed = !!value;
+}
+
+static int asus_ec_input_connect(struct input_handler *handler, struct input_dev *dev,
+				 const struct input_device_id *id)
+{
+	struct input_handle *handle;
+	int error;
+
+	handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+	if (!handle)
+		return -ENOMEM;
+
+	handle->dev = dev;
+	handle->handler = handler;
+	handle->name = "asusec-media-handler";
+
+	error = input_register_handle(handle);
+	if (error)
+		goto err_free_handle;
+
+	error = input_open_device(handle);
+	if (error)
+		goto err_unregister_handle;
+
+	return 0;
+
+ err_unregister_handle:
+	input_unregister_handle(handle);
+ err_free_handle:
+	kfree(handle);
+
+	return error;
+}
+
+static void asus_ec_input_disconnect(struct input_handle *handle)
+{
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	kfree(handle);
+}
+
+static const struct input_device_id asus_ec_input_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
+		.evbit = { BIT_MASK(EV_KEY) },
+	},
+	{ }
+};
+
+static struct input_handler asus_ec_input_handler = {
+	.name =	"asusec-media-handler",
+	.event = asus_ec_input_event,
+	.connect = asus_ec_input_connect,
+	.disconnect = asus_ec_input_disconnect,
+	.id_table = asus_ec_input_ids,
+};
+
+static const unsigned short asus_ec_dock_ext_keys[] = {
+	/* Function keys [0x00 - 0x19] */
+	[0x01] = KEY_DELETE,
+	[0x02] = KEY_F1,
+	[0x03] = KEY_F2,
+	[0x04] = KEY_F3,
+	[0x05] = KEY_F4,
+	[0x06] = KEY_F5,
+	[0x07] = KEY_F6,
+	[0x08] = KEY_F7,
+	[0x10] = KEY_F8,
+	[0x11] = KEY_F9,
+	[0x12] = KEY_F10,
+	[0x13] = KEY_F11,
+	[0x14] = KEY_F12,
+	[0x15] = KEY_MUTE,
+	[0x16] = KEY_VOLUMEDOWN,
+	[0x17] = KEY_VOLUMEUP,
+	/* Multimedia keys [0x20 - 0x39] */
+	[0x21] = KEY_SCREENLOCK,
+	[0x22] = KEY_WLAN,
+	[0x23] = KEY_BLUETOOTH,
+	[0x24] = KEY_TOUCHPAD_TOGGLE,
+	[0x25] = KEY_BRIGHTNESSDOWN,
+	[0x26] = KEY_BRIGHTNESSUP,
+	[0x27] = KEY_BRIGHTNESS_AUTO,
+	[0x28] = KEY_PRINT,
+	[0x30] = KEY_WWW,
+	[0x31] = KEY_CONFIG,
+	[0x32] = KEY_PREVIOUSSONG,
+	[0x33] = KEY_PLAYPAUSE,
+	[0x34] = KEY_NEXTSONG,
+	[0x35] = KEY_MUTE,
+	[0x36] = KEY_VOLUMEDOWN,
+	[0x37] = KEY_VOLUMEUP,
+};
+
+static void asus_ec_keys_report_key(struct input_dev *dev, unsigned int code,
+				    unsigned int key, bool value)
+{
+	input_event(dev, EV_MSC, MSC_SCAN, code);
+	input_report_key(dev, key, value);
+	input_sync(dev);
+}
+
+static int asus_ec_keys_process_key(struct input_dev *dev, u8 code)
+{
+	struct asus_ec_keys_data *priv = dev_get_drvdata(dev->dev.parent);
+	unsigned int key = 0;
+
+	if (code == 0)
+		return NOTIFY_DONE;
+
+	/* Flip special key mode state when pressing key 1 with special key pressed */
+	if (priv->special_key_pressed && code == 1) {
+		priv->special_key_mode = !priv->special_key_mode;
+		return NOTIFY_DONE;
+	}
+
+	/*
+	 * Relocate code to second "page" if pressed state XOR's mode state
+	 * This way special key will invert the current mode
+	 */
+	if (priv->special_key_mode ^ priv->special_key_pressed)
+		code += ASUSEC_EXT_KEY_CODES;
+
+	if (code < dev->keycodemax) {
+		unsigned short *map = dev->keycode;
+
+		key = map[code];
+	}
+
+	if (!key)
+		key = KEY_UNKNOWN;
+
+	asus_ec_keys_report_key(dev, code, key, 1);
+	asus_ec_keys_report_key(dev, code, key, 0);
+
+	return NOTIFY_OK;
+}
+
+static int asus_ec_keys_notify(struct notifier_block *nb,
+			       unsigned long action, void *data_)
+{
+	struct asus_ec_keys_data *priv = container_of(nb, struct asus_ec_keys_data, nb);
+	u8 *data = data_;
+
+	if (action & ASUSEC_SMI_MASK)
+		return NOTIFY_DONE;
+
+	if (action & ASUSEC_SCI_MASK)
+		return asus_ec_keys_process_key(priv->xidev, data[2]);
+
+	return NOTIFY_DONE;
+}
+
+static void asus_ec_keys_setup_keymap(struct asus_ec_keys_data *priv)
+{
+	struct input_dev *dev = priv->xidev;
+	unsigned int i;
+
+	BUILD_BUG_ON(ARRAY_SIZE(priv->keymap) < ARRAY_SIZE(asus_ec_dock_ext_keys));
+
+	dev->keycode = priv->keymap;
+	dev->keycodesize = sizeof(*priv->keymap);
+	dev->keycodemax = ARRAY_SIZE(priv->keymap);
+
+	input_set_capability(dev, EV_MSC, MSC_SCAN);
+	input_set_capability(dev, EV_KEY, KEY_UNKNOWN);
+
+	for (i = 0; i < ARRAY_SIZE(asus_ec_dock_ext_keys); i++) {
+		unsigned int code = asus_ec_dock_ext_keys[i];
+
+		if (!code)
+			continue;
+
+		__set_bit(code, dev->keybit);
+		priv->keymap[i] = code;
+	}
+}
+
+static void asus_ec_input_handler_deregister(void *priv)
+{
+	input_unregister_handler(&asus_ec_input_handler);
+}
+
+static int asus_ec_keys_probe(struct platform_device *pdev)
+{
+	struct asusec_info *ec = cell_to_ec(pdev);
+	struct i2c_client *parent = to_i2c_client(pdev->dev.parent);
+	struct asus_ec_keys_data *priv;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+	priv->ec = ec;
+
+	priv->xidev = devm_input_allocate_device(&pdev->dev);
+	if (!priv->xidev)
+		return -ENOMEM;
+
+	priv->xidev->name = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+					   "%s Keyboard Ext", ec->model);
+	priv->xidev->phys = devm_kasprintf(&pdev->dev, GFP_KERNEL,
+					   "i2c-%u-%04x",
+					   i2c_adapter_id(parent->adapter),
+					   parent->addr);
+	asus_ec_keys_setup_keymap(priv);
+
+	ret = input_register_device(priv->xidev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to register extension keys: %d\n",
+			ret);
+		return ret;
+	}
+
+	asus_ec_input_handler.private = priv;
+
+	ret = input_register_handler(&asus_ec_input_handler);
+	if (ret)
+		return ret;
+
+	ret = devm_add_action_or_reset(&pdev->dev, asus_ec_input_handler_deregister,
+				       priv);
+	if (ret)
+		return ret;
+
+	priv->nb.notifier_call = asus_ec_keys_notify;
+
+	return devm_asus_ec_register_notifier(pdev, &priv->nb);
+}
+
+static struct platform_driver asus_ec_keys_driver = {
+	.driver.name = "asus-transformer-ec-keys",
+	.probe = asus_ec_keys_probe,
+};
+module_platform_driver(asus_ec_keys_driver);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_DESCRIPTION("ASUS Transformer's multimedia keys driver");
+MODULE_LICENSE("GPL");
-- 
2.51.0


^ permalink raw reply related

* [PATCH v3 2/7] mfd: Add driver for ASUS Transformer embedded controller
From: Svyatoslav Ryhel @ 2026-02-14 18:09 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov,
	Lee Jones, Pavel Machek, Sebastian Reichel, Svyatoslav Ryhel,
	Ion Agorria, Michał Mirosław
  Cc: devicetree, linux-kernel, linux-input, linux-leds, linux-pm
In-Reply-To: <20260214180959.30714-1-clamor95@gmail.com>

From: Michał Mirosław <mirq-linux@rere.qmqm.pl>

Support Nuvoton NPCE795-based ECs as used in Asus Transformer TF201,
TF300T, TF300TG, TF300TL and TF700T pad and dock, as well as TF101 dock
and TF600T, P1801-T and TF701T pad. This is a glue driver handling
detection and common operations for EC's functions.

Co-developed-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
---
 drivers/mfd/Kconfig                     |  14 +
 drivers/mfd/Makefile                    |   1 +
 drivers/mfd/asus-transformer-ec.c       | 763 ++++++++++++++++++++++++
 include/linux/mfd/asus-transformer-ec.h | 162 +++++
 4 files changed, 940 insertions(+)
 create mode 100644 drivers/mfd/asus-transformer-ec.c
 create mode 100644 include/linux/mfd/asus-transformer-ec.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7192c9d1d268..5aa4facfd2df 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -137,6 +137,20 @@ config MFD_AAT2870_CORE
 	  additional drivers must be enabled in order to use the
 	  functionality of the device.
 
+config MFD_ASUS_TRANSFORMER_EC
+	tristate "ASUS Transformer's embedded controller"
+	depends on I2C && OF
+	help
+	  Support ECs found in ASUS Transformer's Pad and Mobile Dock.
+
+	  This provides shared glue for functional part drivers:
+	    asus-transformer-ec-kbc, asus-transformer-ec-keys,
+	    leds-asus-transformer-ec, asus-transformer-ec-battery
+	    and asus-transformer-ec-charger.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called asus-transformer-ec.
+
 config MFD_AT91_USART
 	tristate "AT91 USART Driver"
 	select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e75e8045c28a..fd80088d8a9a 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_MFD_88PM805)	+= 88pm805.o 88pm80x.o
 obj-$(CONFIG_MFD_88PM886_PMIC)	+= 88pm886.o
 obj-$(CONFIG_MFD_ACT8945A)	+= act8945a.o
 obj-$(CONFIG_MFD_SM501)		+= sm501.o
+obj-$(CONFIG_MFD_ASUS_TRANSFORMER_EC)	+= asus-transformer-ec.o
 obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835-pm.o
 obj-$(CONFIG_MFD_BCM590XX)	+= bcm590xx.o
 obj-$(CONFIG_MFD_BD9571MWV)	+= bd9571mwv.o
diff --git a/drivers/mfd/asus-transformer-ec.c b/drivers/mfd/asus-transformer-ec.c
new file mode 100644
index 000000000000..9bc5dfb2dfad
--- /dev/null
+++ b/drivers/mfd/asus-transformer-ec.c
@@ -0,0 +1,763 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/array_size.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/asus-transformer-ec.h>
+#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/property.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#define ASUSEC_RSP_BUFFER_SIZE		8
+
+struct asus_ec_chip_data {
+	const char *name;
+	const struct mfd_cell *mfd_devices;
+	unsigned int num_devices;
+	bool clr_fmode; /* clear Factory Mode bit in EC control register */
+};
+
+struct asus_ec_data {
+	struct asusec_info info;
+	struct mutex ecreq_lock; /* prevent simultaneous access */
+	struct gpio_desc *ecreq;
+	struct i2c_client *self;
+	const struct asus_ec_chip_data *data;
+	char ec_data[DOCKRAM_ENTRY_BUFSIZE];
+	bool logging_disabled;
+};
+
+struct dockram_ec_data {
+	struct mutex ctl_lock; /* prevent simultaneous access */
+	char ctl_data[DOCKRAM_ENTRY_BUFSIZE];
+};
+
+#define to_ec_data(ec) \
+	container_of(ec, struct asus_ec_data, info)
+
+/**
+ * asus_dockram_read - Read a register from the DockRAM device.
+ * @client: Handle to the DockRAM device.
+ * @reg: Register to read.
+ * @buf: Byte array into which data will be read; must be large enough to
+ *	 hold the data returned by the DockRAM.
+ *
+ * This executes the DockRAM read based on the SMBus "block read" protocol
+ * or its emulation. It extracts DOCKRAM_ENTRY_SIZE bytes from the set
+ * register address.
+ *
+ * Returns a negative errno code else zero on success.
+ */
+int asus_dockram_read(struct i2c_client *client, int reg, char *buf)
+{
+	struct device *dev = &client->dev;
+	int ret;
+
+	memset(buf, 0, DOCKRAM_ENTRY_BUFSIZE);
+	ret = i2c_smbus_read_i2c_block_data(client, reg,
+					    DOCKRAM_ENTRY_BUFSIZE, buf);
+	if (ret < 0)
+		return ret;
+
+	if (buf[0] > DOCKRAM_ENTRY_SIZE) {
+		dev_err(dev, "bad data len; buffer: %*ph; ret: %d\n",
+			DOCKRAM_ENTRY_BUFSIZE, buf, ret);
+		return -EPROTO;
+	}
+
+	dev_dbg(dev, "got data; buffer: %*ph; ret: %d\n",
+		DOCKRAM_ENTRY_BUFSIZE, buf, ret);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(asus_dockram_read);
+
+/**
+ * asus_dockram_write - Write a byte array to a register of the DockRAM device.
+ * @client: Handle to the DockRAM device.
+ * @reg: Register to write to.
+ * @buf: Byte array to be written (up to DOCKRAM_ENTRY_SIZE bytes).
+ *
+ * This executes the DockRAM write based on the SMBus "block write"
+ * protocol or its emulation. It writes DOCKRAM_ENTRY_SIZE bytes to the
+ * specified register address.
+ *
+ * Returns a negative errno code else zero on success.
+ */
+int asus_dockram_write(struct i2c_client *client, int reg, const char *buf)
+{
+	if (buf[0] > DOCKRAM_ENTRY_SIZE)
+		return -EINVAL;
+
+	dev_dbg(&client->dev, "sending data; buffer: %*ph\n", buf[0] + 1, buf);
+
+	return i2c_smbus_write_i2c_block_data(client, reg, buf[0] + 1, buf);
+}
+EXPORT_SYMBOL_GPL(asus_dockram_write);
+
+/**
+ * asus_dockram_access_ctl - Read from or write to the DockRAM control register.
+ * @client: Handle to the DockRAM device.
+ * @out: Pointer to a variable where the register value will be stored.
+ * @mask: Bitmask of bits to be cleared.
+ * @xor: Bitmask of bits to be set (via XOR).
+ *
+ * This performs a control register read if @out is provided and both @mask
+ * and @xor are zero. Otherwise, it performs a control register update if
+ * @mask and @xor are provided.
+ *
+ * Returns a negative errno code else zero on success.
+ */
+int asus_dockram_access_ctl(struct i2c_client *client, u64 *out, u64 mask,
+			    u64 xor)
+{
+	struct dockram_ec_data *priv = i2c_get_clientdata(client);
+	char *buf = priv->ctl_data;
+	u64 val;
+	int ret = 0;
+
+	guard(mutex)(&priv->ctl_lock);
+
+	ret = asus_dockram_read(client, ASUSEC_DOCKRAM_CONTROL, buf);
+	if (ret < 0)
+		goto exit;
+
+	if (buf[0] != ASUSEC_CTL_SIZE) {
+		ret = -EPROTO;
+		goto exit;
+	}
+
+	val = get_unaligned_le64(buf + 1);
+
+	if (out)
+		*out = val;
+
+	if (mask || xor) {
+		put_unaligned_le64((val & ~mask) ^ xor, buf + 1);
+		ret = asus_dockram_write(client, ASUSEC_DOCKRAM_CONTROL, buf);
+	}
+
+exit:
+	if (ret < 0)
+		dev_err(&client->dev, "Failed to access control flags: %d\n",
+			ret);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(asus_dockram_access_ctl);
+
+static void asus_ec_remove_notifier(struct device *dev, void *res)
+{
+	struct asusec_info *ec = dev_get_drvdata(dev->parent);
+	struct notifier_block **nb = res;
+
+	blocking_notifier_chain_unregister(&ec->notify_list, *nb);
+}
+
+/**
+ * devm_asus_ec_register_notifier - Managed registration of notifier to an
+ *				    ASUS EC blocking notifier chain.
+ * @pdev: Device requesting the notifier (used for resource management).
+ * @nb: Notifier block to be registered.
+ *
+ * Register a notifier to the ASUS EC blocking notifier chain. The notifier
+ * will be automatically unregistered when the requesting device is detached.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int devm_asus_ec_register_notifier(struct platform_device *pdev,
+				   struct notifier_block *nb)
+{
+	struct asusec_info *ec = dev_get_drvdata(pdev->dev.parent);
+	struct notifier_block **res;
+	int ret;
+
+	res = devres_alloc(asus_ec_remove_notifier, sizeof(*res), GFP_KERNEL);
+	if (!res)
+		return -ENOMEM;
+
+	*res = nb;
+	ret = blocking_notifier_chain_register(&ec->notify_list, nb);
+	if (ret) {
+		devres_free(res);
+		return ret;
+	}
+
+	devres_add(&pdev->dev, res);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(devm_asus_ec_register_notifier);
+
+static int asus_ec_signal_request(const struct asusec_info *ec)
+{
+	struct asus_ec_data *priv = to_ec_data(ec);
+
+	guard(mutex)(&priv->ecreq_lock);
+
+	dev_dbg(&priv->self->dev, "EC request\n");
+
+	gpiod_set_value_cansleep(priv->ecreq, 1);
+	msleep(50);
+
+	gpiod_set_value_cansleep(priv->ecreq, 0);
+	msleep(200);
+
+	return 0;
+}
+
+static int asus_ec_write(struct asus_ec_data *priv, u16 data)
+{
+	int ret = i2c_smbus_write_word_data(priv->self, ASUSEC_WRITE_BUF, data);
+
+	dev_dbg(&priv->self->dev, "EC write: %04x, ret = %d\n", data, ret);
+	return ret;
+}
+
+static int asus_ec_read(struct asus_ec_data *priv, bool in_irq)
+{
+	int ret = i2c_smbus_read_i2c_block_data(priv->self, ASUSEC_READ_BUF,
+						sizeof(priv->ec_data),
+						priv->ec_data);
+
+	dev_dbg(&priv->self->dev, "EC read: %*ph, ret = %d%s\n",
+		sizeof(priv->ec_data), priv->ec_data,
+		ret, in_irq ? "; in irq" : "");
+
+	return ret;
+}
+
+/**
+ * asus_ec_i2c_command - Send a 16-bit command to the ASUS EC.
+ * @ec: Pointer to the shared ASUS EC structure.
+ * @data: The 16-bit command (word) to be sent.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int asus_ec_i2c_command(const struct asusec_info *ec, u16 data)
+{
+	return asus_ec_write(to_ec_data(ec), data);
+}
+EXPORT_SYMBOL_GPL(asus_ec_i2c_command);
+
+static void asus_ec_clear_buffer(struct asus_ec_data *priv)
+{
+	int retry = ASUSEC_RSP_BUFFER_SIZE;
+
+	while (retry--) {
+		if (asus_ec_read(priv, false) < 0)
+			continue;
+
+		if (priv->ec_data[1] & ASUSEC_OBF_MASK)
+			continue;
+
+		break;
+	}
+}
+
+static int asus_ec_log_info(struct asus_ec_data *priv, unsigned int reg,
+			    const char *name, char **out)
+{
+	char buf[DOCKRAM_ENTRY_BUFSIZE];
+	int ret;
+
+	ret = asus_dockram_read(priv->info.dockram, reg, buf);
+	if (ret < 0)
+		return ret;
+
+	if (!priv->logging_disabled)
+		dev_info(&priv->self->dev, "%-14s: %.*s\n", name,
+			 buf[0], buf + 1);
+
+	if (out)
+		*out = kstrndup(buf + 1, buf[0], GFP_KERNEL);
+
+	return 0;
+}
+
+static int asus_ec_reset(struct asus_ec_data *priv)
+{
+	int retry, ret;
+
+	for (retry = 0; retry < 3; retry++) {
+		ret = asus_ec_write(priv, 0);
+		if (!ret)
+			return 0;
+
+		msleep(300);
+	}
+
+	return ret;
+}
+
+static int asus_ec_magic_debug(struct asus_ec_data *priv)
+{
+	u64 flag;
+	int ret;
+
+	ret = asus_ec_get_ctl(&priv->info, &flag);
+	if (ret < 0)
+		return ret;
+
+	flag &= ASUSEC_CTL_SUSB_MODE;
+	dev_info(&priv->self->dev, "EC FW behaviour: %s\n",
+		 flag ? "susb on when receive ec_req" :
+		 "susb on when system wakeup");
+
+	return 0;
+}
+
+static int asus_ec_set_factory_mode(struct asus_ec_data *priv, bool on)
+{
+	dev_info(&priv->self->dev, "Entering %s mode.\n", on ? "factory" :
+		 "normal");
+	return asus_ec_update_ctl(&priv->info, ASUSEC_CTL_FACTORY_MODE,
+				  on ? ASUSEC_CTL_FACTORY_MODE : 0);
+}
+
+static void asus_ec_handle_smi(struct asus_ec_data *priv, unsigned int code);
+
+static irqreturn_t asus_ec_interrupt(int irq, void *dev_id)
+{
+	struct asus_ec_data *priv = dev_id;
+	unsigned long notify_action;
+	int ret;
+
+	ret = asus_ec_read(priv, true);
+	if (ret <= 0 || !(priv->ec_data[1] & ASUSEC_OBF_MASK))
+		return IRQ_NONE;
+
+	notify_action = priv->ec_data[1];
+	if (notify_action & ASUSEC_SMI_MASK) {
+		unsigned int code = priv->ec_data[2];
+
+		asus_ec_handle_smi(priv, code);
+
+		notify_action |= code << 8;
+		dev_dbg(&priv->self->dev, "SMI code: 0x%02x\n", code);
+	}
+
+	blocking_notifier_call_chain(&priv->info.notify_list,
+				     notify_action, priv->ec_data);
+
+	return IRQ_HANDLED;
+}
+
+static int asus_ec_detect(struct asus_ec_data *priv)
+{
+	char *model = NULL;
+	int ret;
+
+	ret = asus_ec_reset(priv);
+	if (ret)
+		goto err_exit;
+
+	asus_ec_clear_buffer(priv);
+
+	ret = asus_ec_log_info(priv, ASUSEC_DOCKRAM_INFO_MODEL, "model", &model);
+	if (ret)
+		goto err_exit;
+
+	ret = asus_ec_log_info(priv, ASUSEC_DOCKRAM_INFO_FW, "FW version", NULL);
+	if (ret)
+		goto err_exit;
+
+	ret = asus_ec_log_info(priv, ASUSEC_DOCKRAM_INFO_CFGFMT, "Config format", NULL);
+	if (ret)
+		goto err_exit;
+
+	ret = asus_ec_log_info(priv, ASUSEC_DOCKRAM_INFO_HW, "HW version", NULL);
+	if (ret)
+		goto err_exit;
+
+	priv->logging_disabled = true;
+
+	ret = asus_ec_magic_debug(priv);
+	if (ret)
+		goto err_exit;
+
+	priv->info.model = model;
+	priv->info.name = priv->data->name;
+
+	if (priv->data->clr_fmode)
+		asus_ec_set_factory_mode(priv, false);
+
+err_exit:
+	if (ret)
+		dev_err(&priv->self->dev, "failed to access EC: %d\n", ret);
+
+	return ret;
+}
+
+static void asus_ec_handle_smi(struct asus_ec_data *priv, unsigned int code)
+{
+	dev_dbg(&priv->self->dev, "SMI interrupt: 0x%02x\n", code);
+
+	switch (code) {
+	case ASUSEC_SMI_HANDSHAKE:
+	case ASUSEC_SMI_RESET:
+		asus_ec_detect(priv);
+		break;
+	}
+}
+
+static ssize_t dockram_read(struct file *filp, char __user *buf,
+			    size_t count, loff_t *ppos)
+{
+	struct i2c_client *client = filp->private_data;
+	unsigned int reg, rsize;
+	ssize_t n_read = 0, val;
+	loff_t off = *ppos;
+	char *data;
+	int ret;
+
+	reg = off / DOCKRAM_ENTRY_SIZE;
+	off %= DOCKRAM_ENTRY_SIZE;
+	rsize = DOCKRAM_ENTRIES * DOCKRAM_ENTRY_SIZE;
+
+	if (!count)
+		return 0;
+
+	data = kmalloc(DOCKRAM_ENTRY_BUFSIZE, GFP_KERNEL);
+
+	while (reg < DOCKRAM_ENTRIES) {
+		unsigned int len = DOCKRAM_ENTRY_SIZE - off;
+
+		if (len > rsize)
+			len = rsize;
+
+		ret = asus_dockram_read(client, reg, data);
+		if (ret < 0) {
+			if (!n_read)
+				n_read = ret;
+			break;
+		}
+
+		val = copy_to_user(buf, data + 1 + off, len);
+		if (val == len)
+			return -EFAULT;
+
+		*ppos += len;
+		n_read += len;
+
+		if (len == rsize)
+			break;
+
+		rsize -= len;
+		buf += len;
+		off = 0;
+		++reg;
+	}
+
+	kfree(data);
+
+	return n_read;
+}
+
+static int dockram_write_one(struct i2c_client *client, int reg,
+			     const char __user *buf, size_t count)
+{
+	struct dockram_ec_data *priv = i2c_get_clientdata(client);
+	int ret;
+
+	if (!count || count > DOCKRAM_ENTRY_SIZE)
+		return -EINVAL;
+	if (buf[0] != count - 1)
+		return -EINVAL;
+
+	guard(mutex)(&priv->ctl_lock);
+
+	priv->ctl_data[0] = (u8)count;
+	memcpy(priv->ctl_data + 1, buf, count);
+	ret = asus_dockram_write(client, reg, priv->ctl_data);
+
+	return ret;
+}
+
+static ssize_t dockram_write(struct file *filp, const char __user *buf,
+			     size_t count, loff_t *ppos)
+{
+	struct i2c_client *client = filp->private_data;
+	unsigned int reg;
+	loff_t off = *ppos;
+	int ret;
+
+	if (off % DOCKRAM_ENTRY_SIZE != 0)
+		return -EINVAL;
+
+	reg = off / DOCKRAM_ENTRY_SIZE;
+	if (reg >= DOCKRAM_ENTRIES)
+		return -EINVAL;
+
+	ret = dockram_write_one(client, reg, buf, count);
+
+	return ret < 0 ? ret : count;
+}
+
+static const struct debugfs_short_fops dockram_fops = {
+	.read	= dockram_read,
+	.write	= dockram_write,
+	.llseek	= default_llseek,
+};
+
+static int control_reg_get(void *client, u64 *val)
+{
+	return asus_dockram_access_ctl(client, val, 0, 0);
+}
+
+static int control_reg_set(void *client, u64 val)
+{
+	return asus_dockram_access_ctl(client, NULL, ~0ull, val);
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(control_reg_fops, control_reg_get,
+			 control_reg_set, "%016llx\n");
+
+static int ec_request_set(void *ec, u64 val)
+{
+	if (val)
+		asus_ec_signal_request(ec);
+
+	return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(ec_request_fops, NULL, ec_request_set, "%llu\n");
+
+static int ec_irq_set(void *ec, u64 val)
+{
+	struct asus_ec_data *priv = to_ec_data(ec);
+
+	if (val)
+		irq_wake_thread(priv->self->irq, priv);
+
+	return 0;
+}
+
+DEFINE_DEBUGFS_ATTRIBUTE(ec_irq_fops, NULL, ec_irq_set, "%llu\n");
+
+static void asus_ec_debugfs_remove(void *debugfs_root)
+{
+	debugfs_remove_recursive(debugfs_root);
+}
+
+static void devm_asus_ec_debugfs_init(struct device *dev)
+{
+	struct asusec_info *ec = dev_get_drvdata(dev);
+	struct asus_ec_data *priv = to_ec_data(ec);
+	struct dentry *debugfs_root, *dockram_dir;
+	char *name = devm_kasprintf(dev, GFP_KERNEL, "asus-ec-%s",
+				    priv->data->name);
+
+	debugfs_root = debugfs_create_dir(name, NULL);
+	dockram_dir = debugfs_create_dir("dockram", debugfs_root);
+
+	debugfs_create_file("ec_irq", 0200, debugfs_root, ec,
+			    &ec_irq_fops);
+	debugfs_create_file("ec_request", 0200, debugfs_root, ec,
+			    &ec_request_fops);
+	debugfs_create_file("control_reg", 0644, dockram_dir,
+			    priv->info.dockram, &control_reg_fops);
+	debugfs_create_file("dockram", 0644, dockram_dir,
+			    priv->info.dockram, &dockram_fops);
+
+	devm_add_action_or_reset(dev, asus_ec_debugfs_remove, debugfs_root);
+}
+
+static void asus_ec_release_dockram_dev(void *client)
+{
+	i2c_unregister_device(client);
+}
+
+static struct i2c_client *devm_asus_dockram_get(struct device *dev)
+{
+	struct i2c_client *parent = to_i2c_client(dev);
+	struct i2c_client *dockram;
+	struct dockram_ec_data *priv;
+	int ret;
+
+	dockram = i2c_new_ancillary_device(parent, "dockram",
+					   parent->addr + 2);
+	if (IS_ERR(dockram))
+		return dockram;
+
+	ret = devm_add_action_or_reset(dev, asus_ec_release_dockram_dev,
+				       dockram);
+	if (ret)
+		return ERR_PTR(ret);
+
+	priv = devm_kzalloc(&dockram->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	i2c_set_clientdata(dockram, priv);
+	mutex_init(&priv->ctl_lock);
+
+	return dockram;
+}
+
+static int asus_ec_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct asus_ec_data *priv;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+		return dev_err_probe(dev, -ENXIO,
+			"I2C bus is missing required SMBus block mode support\n");
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->data = device_get_match_data(dev);
+	if (!priv->data)
+		return -ENODEV;
+
+	i2c_set_clientdata(client, priv);
+	priv->self = client;
+
+	priv->info.dockram = devm_asus_dockram_get(dev);
+	if (IS_ERR(priv->info.dockram))
+		return dev_err_probe(dev, PTR_ERR(priv->info.dockram),
+				     "failed to get dockram\n");
+
+	priv->ecreq = devm_gpiod_get(dev, "request", GPIOD_OUT_LOW);
+	if (IS_ERR(priv->ecreq))
+		return dev_err_probe(dev, PTR_ERR(priv->ecreq),
+				     "failed to get request GPIO\n");
+
+	BLOCKING_INIT_NOTIFIER_HEAD(&priv->info.notify_list);
+	mutex_init(&priv->ecreq_lock);
+
+	asus_ec_signal_request(&priv->info);
+
+	ret = asus_ec_detect(priv);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to detect EC version\n");
+
+	ret = devm_request_threaded_irq(dev, client->irq, NULL,
+					&asus_ec_interrupt,
+					IRQF_ONESHOT | IRQF_SHARED,
+					client->name, priv);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register IRQ\n");
+
+	/* Parent I2C controller uses DMA, ASUS EC and child devices do not */
+	client->dev.coherent_dma_mask = 0;
+	client->dev.dma_mask = &client->dev.coherent_dma_mask;
+
+	devm_asus_ec_debugfs_init(dev);
+
+	return devm_mfd_add_devices(dev, 0, priv->data->mfd_devices,
+				    priv->data->num_devices, NULL, 0, NULL);
+}
+
+static const struct mfd_cell asus_ec_pad_mfd_devices[] = {
+	{
+		.name = "asus-transformer-ec-battery",
+		.id = 0,
+	}, {
+		.name = "asus-transformer-ec-led",
+		.id = 0,
+	},
+};
+
+static const struct asus_ec_chip_data asus_ec_pad_data = {
+	.name = "pad",
+	.mfd_devices = asus_ec_pad_mfd_devices,
+	.num_devices = ARRAY_SIZE(asus_ec_pad_mfd_devices),
+	.clr_fmode = true,
+};
+
+static const struct mfd_cell asus_ec_pad_charger_mfd_devices[] = {
+	{
+		.name = "asus-transformer-ec-battery",
+		.id = 0,
+	}, {
+		.name = "asus-transformer-ec-charger",
+		.id = 0,
+	}, {
+		.name = "asus-transformer-ec-led",
+		.id = 0,
+	},
+};
+
+static const struct asus_ec_chip_data asus_ec_pad_charger_data = {
+	.name = "pad",
+	.mfd_devices = asus_ec_pad_charger_mfd_devices,
+	.num_devices = ARRAY_SIZE(asus_ec_pad_charger_mfd_devices),
+	.clr_fmode = true,
+};
+
+static const struct mfd_cell asus_ec_dock_mfd_devices[] = {
+	{
+		.name = "asus-transformer-ec-battery",
+		.id = 1,
+	}, {
+		.name = "asus-transformer-ec-charger",
+		.id = 1,
+	}, {
+		.name = "asus-transformer-ec-led",
+		.id = 1,
+	}, {
+		.name = "asus-transformer-ec-keys",
+	}, {
+		.name = "asus-transformer-ec-kbc",
+	},
+};
+
+static const struct asus_ec_chip_data asus_ec_dock_data = {
+	.name = "dock",
+	.mfd_devices = asus_ec_dock_mfd_devices,
+	.num_devices = ARRAY_SIZE(asus_ec_dock_mfd_devices),
+	.clr_fmode = false,
+};
+
+static const struct mfd_cell asus_ec_dock_sl101_mfd_devices[] = {
+	{
+		.name = "asus-transformer-ec-kbc",
+	},
+};
+
+static const struct asus_ec_chip_data asus_ec_dock_sl101_data = {
+	.name = "dock",
+	.mfd_devices = asus_ec_dock_sl101_mfd_devices,
+	.num_devices = ARRAY_SIZE(asus_ec_dock_sl101_mfd_devices),
+	.clr_fmode = false,
+};
+
+static const struct of_device_id asus_ec_match[] = {
+	{ .compatible = "asus,transformer-ec-pad", .data = &asus_ec_pad_data },
+	{ .compatible = "asus,p1801-t-ec-pad", .data = &asus_ec_pad_charger_data },
+	{ .compatible = "asus,tf600t-ec-pad", .data = &asus_ec_pad_charger_data },
+	{ .compatible = "asus,tf701t-ec-pad", .data = &asus_ec_pad_charger_data },
+	{ .compatible = "asus,transformer-ec-dock", .data = &asus_ec_dock_data },
+	{ .compatible = "asus,sl101-ec-dock", .data = &asus_ec_dock_sl101_data },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, asus_ec_match);
+
+static struct i2c_driver asus_ec_driver = {
+	.driver	= {
+		.name = "asus-transformer-ec",
+		.of_match_table = asus_ec_match,
+	},
+	.probe = asus_ec_probe,
+};
+module_i2c_driver(asus_ec_driver);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_AUTHOR("Svyatoslav Ryhel <clamor95@gmail.com>");
+MODULE_DESCRIPTION("ASUS Transformer's EC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/asus-transformer-ec.h b/include/linux/mfd/asus-transformer-ec.h
new file mode 100644
index 000000000000..0a72de40352e
--- /dev/null
+++ b/include/linux/mfd/asus-transformer-ec.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __MFD_ASUS_TRANSFORMER_EC_H
+#define __MFD_ASUS_TRANSFORMER_EC_H
+
+#include <linux/notifier.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+struct i2c_client;
+
+struct asusec_info {
+	const char *model;
+	const char *name;
+	struct i2c_client *dockram;
+	struct workqueue_struct *wq;
+	struct blocking_notifier_head notify_list;
+};
+
+#define DOCKRAM_ENTRIES			0x100
+#define DOCKRAM_ENTRY_SIZE		32
+#define DOCKRAM_ENTRY_BUFSIZE		(DOCKRAM_ENTRY_SIZE + 1)
+
+/* interrupt sources */
+#define ASUSEC_OBF_MASK			BIT(0)
+#define ASUSEC_KEY_MASK			BIT(2)
+#define ASUSEC_KBC_MASK			BIT(3)
+#define ASUSEC_AUX_MASK			BIT(5)
+#define ASUSEC_SCI_MASK			BIT(6)
+#define ASUSEC_SMI_MASK			BIT(7)
+
+/* SMI notification codes */
+#define ASUSEC_SMI_POWER_NOTIFY		0x31	/* [un]plugging USB cable */
+#define ASUSEC_SMI_HANDSHAKE		0x50	/* response to ec_req edge */
+#define ASUSEC_SMI_WAKE			0x53
+#define ASUSEC_SMI_RESET		0x5f
+#define ASUSEC_SMI_ADAPTER_EVENT	0x60	/* [un]plugging charger to dock */
+#define ASUSEC_SMI_BACKLIGHT_ON		0x63
+#define ASUSEC_SMI_AUDIO_DOCK_IN	0x70
+
+#define ASUSEC_SMI_ACTION(code)		(ASUSEC_SMI_MASK | ASUSEC_OBF_MASK | \
+					(ASUSEC_SMI_##code << 8))
+
+/* control register [0x0a] layout */
+#define ASUSEC_CTL_SIZE			8
+
+/*
+ * EC reports power from 40-pin connector in the LSB of the control
+ * register.  The following values have been observed (xor 0x02):
+ *
+ * PAD-ec no-plug  0x40 / PAD-ec DOCK     0x20 / DOCK-ec no-plug 0x40
+ * PAD-ec AC       0x25 / PAD-ec DOCK+AC  0x24 / DOCK-ec AC      0x25
+ * PAD-ec USB      0x45 / PAD-ec DOCK+USB 0x24 / DOCK-ec USB     0x41
+ */
+
+#define ASUSEC_CTL_DIRECT_POWER_SOURCE	BIT_ULL(0)
+#define ASUSEC_STAT_CHARGING		BIT_ULL(2)
+#define ASUSEC_CTL_FULL_POWER_SOURCE	BIT_ULL(5)
+#define ASUSEC_CTL_SUSB_MODE		BIT_ULL(9)
+#define ASUSEC_CMD_SUSPEND_S3		BIT_ULL(33)
+#define ASUSEC_CTL_TEST_DISCHARGE	BIT_ULL(35)
+#define ASUSEC_CMD_SUSPEND_INHIBIT	BIT_ULL(37)
+#define ASUSEC_CTL_FACTORY_MODE		BIT_ULL(38)
+#define ASUSEC_CTL_KEEP_AWAKE		BIT_ULL(39)
+#define ASUSEC_CTL_USB_CHARGE		BIT_ULL(40)
+#define ASUSEC_CTL_LED_BLINK		BIT_ULL(40)
+#define ASUSEC_CTL_LED_AMBER		BIT_ULL(41)
+#define ASUSEC_CTL_LED_GREEN		BIT_ULL(42)
+#define ASUSEC_CMD_SWITCH_HDMI		BIT_ULL(56)
+#define ASUSEC_CMD_WIN_SHUTDOWN		BIT_ULL(62)
+
+#define ASUSEC_DOCKRAM_INFO_MODEL	0x01
+#define ASUSEC_DOCKRAM_INFO_FW		0x02
+#define ASUSEC_DOCKRAM_INFO_CFGFMT	0x03
+#define ASUSEC_DOCKRAM_INFO_HW		0x04
+#define ASUSEC_DOCKRAM_CONTROL		0x0a
+#define ASUSEC_DOCKRAM_BATT_CTL		0x14
+
+#define ASUSEC_WRITE_BUF		0x64
+#define ASUSEC_READ_BUF			0x6a
+
+/* dockram comm */
+int asus_dockram_read(struct i2c_client *client, int reg, char *buf);
+int asus_dockram_write(struct i2c_client *client, int reg, const char *buf);
+int asus_dockram_access_ctl(struct i2c_client *client,
+			    u64 *out, u64 mask, u64 xor);
+
+/* EC public API */
+
+/**
+ * cell_to_ec - Request the shared ASUS EC structure via a subdevice's pdev.
+ * @pdev: EC subdevice pdev requesting access to the shared ASUS EC structure.
+ *
+ * Returns a pointer to the asusec_info structure.
+ */
+static inline struct asusec_info *cell_to_ec(struct platform_device *pdev)
+{
+	return dev_get_drvdata(pdev->dev.parent);
+}
+
+/**
+ * asus_ec_get_ctl - Read from the DockRAM control register.
+ * @ec:  Pointer to the shared ASUS EC structure.
+ * @out: Pointer to the variable where the register value will be stored.
+ *
+ * Performs a control register read and stores the value in @out.
+ *
+ * Return: 0 on success, or a negative errno code on failure.
+ */
+static inline int asus_ec_get_ctl(const struct asusec_info *ec, u64 *out)
+{
+	return asus_dockram_access_ctl(ec->dockram, out, 0, 0);
+}
+
+/**
+ * asus_ec_update_ctl - Update the DockRAM control register.
+ * @ec:   Pointer to the shared ASUS EC structure.
+ * @mask: Bitmask of bits to be cleared.
+ * @xor:  Bitmask of bits to be toggled or set (via XOR).
+ *
+ * Performs a read-modify-write update on the control register using
+ * the provided @mask and @xor values.
+ *
+ * Return: 0 on success, or a negative errno code on failure.
+ */
+static inline int asus_ec_update_ctl(const struct asusec_info *ec,
+				     u64 mask, u64 xor)
+{
+	return asus_dockram_access_ctl(ec->dockram, NULL, mask, xor);
+}
+
+/**
+ * asus_ec_set_ctl_bits - Sets bits of the DockRAM control register.
+ * @ec:   Pointer to the shared ASUS EC structure.
+ * @mask: Bitmask of bits to be set.
+ *
+ * Sets bits of the control register using the provided @mask value.
+ *
+ * Return: 0 on success, or a negative errno code on failure.
+ */
+static inline int asus_ec_set_ctl_bits(const struct asusec_info *ec, u64 mask)
+{
+	return asus_dockram_access_ctl(ec->dockram, NULL, mask, mask);
+}
+
+/**
+ * asus_ec_clear_ctl_bits - Clears bits of the DockRAM control register.
+ * @ec:   Pointer to the shared ASUS EC structure.
+ * @mask: Bitmask of bits to be cleared.
+ *
+ * Clears bits of the control register using the provided @mask value.
+ *
+ * Return: 0 on success, or a negative errno code on failure.
+ */
+static inline int asus_ec_clear_ctl_bits(const struct asusec_info *ec, u64 mask)
+{
+	return asus_dockram_access_ctl(ec->dockram, NULL, mask, 0);
+}
+
+int asus_ec_i2c_command(const struct asusec_info *ec, u16 data);
+int devm_asus_ec_register_notifier(struct platform_device *dev,
+				   struct notifier_block *nb);
+#endif /* __MFD_ASUS_TRANSFORMER_EC_H */
-- 
2.51.0


^ permalink raw reply related

* [PATCH v3 3/7] input: serio: Add driver for ASUS Transformer dock keyboard and touchpad
From: Svyatoslav Ryhel @ 2026-02-14 18:09 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov,
	Lee Jones, Pavel Machek, Sebastian Reichel, Svyatoslav Ryhel,
	Ion Agorria, Michał Mirosław
  Cc: devicetree, linux-kernel, linux-input, linux-leds, linux-pm
In-Reply-To: <20260214180959.30714-1-clamor95@gmail.com>

From: Michał Mirosław <mirq-linux@rere.qmqm.pl>

Add input driver for ASUS Transformer dock keyboard and touchpad.

Some keys in ASUS Dock report keycodes that don't make sense according to
their position, this patch modifies the incoming data that is sent to
serio to send proper scancodes.

Co-developed-by: Ion Agorria <ion@agorria.com>
Signed-off-by: Ion Agorria <ion@agorria.com>
Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 drivers/input/serio/Kconfig                   |  15 ++
 drivers/input/serio/Makefile                  |   1 +
 drivers/input/serio/asus-transformer-ec-kbc.c | 147 ++++++++++++++++++
 3 files changed, 163 insertions(+)
 create mode 100644 drivers/input/serio/asus-transformer-ec-kbc.c

diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
index c7ef347a4dff..1ca17ba632cc 100644
--- a/drivers/input/serio/Kconfig
+++ b/drivers/input/serio/Kconfig
@@ -97,6 +97,21 @@ config SERIO_RPCKBD
 	  To compile this driver as a module, choose M here: the
 	  module will be called rpckbd.
 
+config SERIO_ASUS_TRANSFORMER_EC
+	tristate "Asus Transformer's Dock keyboard and touchpad controller"
+	depends on MFD_ASUS_TRANSFORMER_EC
+	help
+	  Say Y here if you want to use the keyboard and/or touchpad on
+	  Asus Transformed's Mobile Dock.
+
+	  For keyboard support you also need atkbd driver.
+
+	  For touchpad support you also need psmouse driver with Elantech
+	  touchpad option enabled.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called asus-transformer-ec-kbc.
+
 config SERIO_AMBAKMI
 	tristate "AMBA KMI keyboard controller"
 	depends on ARM_AMBA
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile
index 6d97bad7b844..9ecf0d011863 100644
--- a/drivers/input/serio/Makefile
+++ b/drivers/input/serio/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_SERIO_CT82C710)	+= ct82c710.o
 obj-$(CONFIG_SERIO_RPCKBD)	+= rpckbd.o
 obj-$(CONFIG_SERIO_SA1111)	+= sa1111ps2.o
 obj-$(CONFIG_SERIO_AMBAKMI)	+= ambakmi.o
+obj-$(CONFIG_SERIO_ASUS_TRANSFORMER_EC)	+= asus-transformer-ec-kbc.o
 obj-$(CONFIG_SERIO_Q40KBD)	+= q40kbd.o
 obj-$(CONFIG_SERIO_GSCPS2)	+= gscps2.o
 obj-$(CONFIG_HP_SDC)		+= hp_sdc.o
diff --git a/drivers/input/serio/asus-transformer-ec-kbc.c b/drivers/input/serio/asus-transformer-ec-kbc.c
new file mode 100644
index 000000000000..af755cd0ae38
--- /dev/null
+++ b/drivers/input/serio/asus-transformer-ec-kbc.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/i8042.h>
+#include <linux/mfd/asus-transformer-ec.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/serio.h>
+
+struct asus_ec_kbc_data {
+	struct notifier_block nb;
+	struct asusec_info *ec;
+	struct serio *sdev[2];
+};
+
+static int asus_ec_kbc_notify(struct notifier_block *nb,
+			      unsigned long action, void *data_)
+{
+	struct asus_ec_kbc_data *priv = container_of(nb, struct asus_ec_kbc_data, nb);
+	unsigned int port_idx, n;
+	u8 *data = data_;
+
+	if (action & (ASUSEC_SMI_MASK | ASUSEC_SCI_MASK))
+		return NOTIFY_DONE;
+	else if (action & ASUSEC_AUX_MASK)
+		port_idx = 1;
+	else if (action & (ASUSEC_KBC_MASK | ASUSEC_KEY_MASK))
+		port_idx = 0;
+	else
+		return NOTIFY_DONE;
+
+	n = data[0] - 1;
+	data += 2;
+
+	/*
+	 * We need to replace these incoming data for keys:
+	 * RIGHT_META Press   0xE0 0x27      -> LEFT_ALT   Press   0x11
+	 * RIGHT_META Release 0xE0 0xF0 0x27 -> LEFT_ALT   Release 0xF0 0x11
+	 * COMPOSE    Press   0xE0 0x2F      -> RIGHT_META Press   0xE0 0x27
+	 * COMPOSE    Release 0xE0 0xF0 0x2F -> RIGHT_META Release 0xE0 0xF0 0x27
+	 */
+
+	if (port_idx == 0 && n >= 2 && data[0] == 0xE0) {
+		if (n == 3 && data[1] == 0xF0) {
+			switch (data[2]) {
+			case 0x27:
+				data[0] = 0xF0;
+				data[1] = 0x11;
+				n = 2;
+				break;
+			case 0x2F:
+				data[2] = 0x27;
+				break;
+			}
+		} else if (n == 2) {
+			switch (data[1]) {
+			case 0x27:
+				data[0] = 0x11;
+				n = 1;
+				break;
+			case 0x2F:
+				data[1] = 0x27;
+				break;
+			}
+		}
+	}
+
+	while (n--)
+		serio_interrupt(priv->sdev[port_idx], *data++, 0);
+
+	return NOTIFY_OK;
+}
+
+static int asus_ec_serio_write(struct serio *port, unsigned char data)
+{
+	const struct asusec_info *ec = port->port_data;
+
+	return asus_ec_i2c_command(ec, (data << 8) | port->id.extra);
+}
+
+static void asus_ec_serio_remove(void *data)
+{
+	serio_unregister_port(data);
+}
+
+static int asus_ec_register_serio(struct platform_device *pdev, int idx,
+				  const char *name, int cmd)
+{
+	struct asus_ec_kbc_data *priv = platform_get_drvdata(pdev);
+	struct i2c_client *parent = to_i2c_client(pdev->dev.parent);
+	struct serio *port = kzalloc(sizeof(*port), GFP_KERNEL);
+
+	if (!port)
+		return -ENOMEM;
+
+	priv->sdev[idx] = port;
+	port->dev.parent = &pdev->dev;
+	port->id.type = SERIO_8042;
+	port->id.extra = cmd & 0xFF;
+	port->write = asus_ec_serio_write;
+	port->port_data = (void *)priv->ec;
+	snprintf(port->name, sizeof(port->name), "%s %s",
+		 priv->ec->model, name);
+	snprintf(port->phys, sizeof(port->phys), "i2c-%u-%04x/serio%d",
+		 i2c_adapter_id(parent->adapter), parent->addr, idx);
+
+	serio_register_port(port);
+
+	return devm_add_action_or_reset(&pdev->dev, asus_ec_serio_remove, port);
+}
+
+static int asus_ec_kbc_probe(struct platform_device *pdev)
+{
+	struct asusec_info *ec = cell_to_ec(pdev);
+	struct asus_ec_kbc_data *priv;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+	priv->ec = ec;
+
+	ret = asus_ec_register_serio(pdev, 0, "Keyboard", 0);
+	if (ret < 0)
+		return ret;
+
+	ret = asus_ec_register_serio(pdev, 1, "Touchpad", I8042_CMD_AUX_SEND);
+	if (ret < 0)
+		return ret;
+
+	priv->nb.notifier_call = asus_ec_kbc_notify;
+
+	return devm_asus_ec_register_notifier(pdev, &priv->nb);
+}
+
+static struct platform_driver asus_ec_kbc_driver = {
+	.driver.name = "asus-transformer-ec-kbc",
+	.probe = asus_ec_kbc_probe,
+};
+module_platform_driver(asus_ec_kbc_driver);
+
+MODULE_AUTHOR("Michał Mirosław <mirq-linux@rere.qmqm.pl>");
+MODULE_DESCRIPTION("ASUS Transformer's Dock keyboard and touchpad controller driver");
+MODULE_LICENSE("GPL");
-- 
2.51.0


^ permalink raw reply related

* [PATCH v3 1/7] dt-bindings: embedded-controller: document ASUS Transformer EC
From: Svyatoslav Ryhel @ 2026-02-14 18:09 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov,
	Lee Jones, Pavel Machek, Sebastian Reichel, Svyatoslav Ryhel,
	Ion Agorria, Michał Mirosław
  Cc: devicetree, linux-kernel, linux-input, linux-leds, linux-pm
In-Reply-To: <20260214180959.30714-1-clamor95@gmail.com>

Document embedded controller used in ASUS Transformer device series.

Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
---
 .../asus,transformer-ec.yaml                  | 98 +++++++++++++++++++
 1 file changed, 98 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/embedded-controller/asus,transformer-ec.yaml

diff --git a/Documentation/devicetree/bindings/embedded-controller/asus,transformer-ec.yaml b/Documentation/devicetree/bindings/embedded-controller/asus,transformer-ec.yaml
new file mode 100644
index 000000000000..670c4c2d339d
--- /dev/null
+++ b/Documentation/devicetree/bindings/embedded-controller/asus,transformer-ec.yaml
@@ -0,0 +1,98 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/embedded-controller/asus,transformer-ec.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASUS Transformer's Embedded Controller
+
+description:
+  Several Nuvoton based Embedded Controllers attached to an I2C bus,
+  running a custom ASUS firmware, specific to the ASUS Transformer
+  device series.
+
+maintainers:
+  - Svyatoslav Ryhel <clamor95@gmail.com>
+
+allOf:
+  - $ref: /schemas/power/supply/power-supply.yaml
+
+properties:
+  compatible:
+    oneOf:
+      - enum:
+          - asus,p1801-t-ec-pad
+          - asus,sl101-ec-dock
+          - asus,tf600t-ec-pad
+          - asus,tf701t-ec-pad
+
+      - items:
+          - enum:
+              - asus,tf101-ec-dock
+              - asus,tf101g-ec-dock
+              - asus,tf201-ec-dock
+              - asus,tf300t-ec-dock
+              - asus,tf300tg-ec-dock
+              - asus,tf300tl-ec-dock
+              - asus,tf700t-ec-dock
+          - const: asus,transformer-ec-dock
+
+      - items:
+          - enum:
+              - asus,tf201-ec-pad
+              - asus,tf300t-ec-pad
+              - asus,tf300tg-ec-pad
+              - asus,tf300tl-ec-pad
+              - asus,tf700t-ec-pad
+          - const: asus,transformer-ec-pad
+
+  reg:
+    description:
+      The ASUS Transformer EC has a main I2C address and an associated
+      DockRAM device, which provides power-related functions for the
+      embedded controller. Both addresses are required for operation.
+    minItems: 2
+
+  reg-names:
+    items:
+      - const: ec
+      - const: dockram
+
+  interrupts:
+    maxItems: 1
+
+  request-gpios:
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - interrupts
+  - request-gpios
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      embedded-controller@19 {
+        compatible = "asus,tf201-ec-dock", "asus,transformer-ec-dock";
+        reg = <0x19>, <0x1b>;
+        reg-names = "ec", "dockram";
+
+        interrupt-parent = <&gpio>;
+        interrupts = <151 IRQ_TYPE_LEVEL_LOW>;
+
+        request-gpios = <&gpio 134 GPIO_ACTIVE_LOW>;
+
+        monitored-battery = <&dock_battery>;
+      };
+    };
+...
-- 
2.51.0


^ permalink raw reply related

* [PATCH v3 0/7] mfd: Add support for Asus Transformer embedded controller
From: Svyatoslav Ryhel @ 2026-02-14 18:09 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Dmitry Torokhov,
	Lee Jones, Pavel Machek, Sebastian Reichel, Svyatoslav Ryhel,
	Ion Agorria, Michał Mirosław
  Cc: devicetree, linux-kernel, linux-input, linux-leds, linux-pm

Add support for embedded controller used in Asus Transformers for
managing power and input functions.

---
Changes in v2:
- converted sysfs debug exports into debugfs
- added kernel-doc comments for exposed functions
- fixed minor typos and inconsistencies

Changes in v3:
- dropped DockRAM commits (both schema and driver)
- integrated DockRAM functionality directly into the controller driver
- EC schema moved to embedded controllers folder
- removed all cell descriptions from the schema
- removed all compatibles from the cell drivers
- adjusted naming conventions to better align with the ASUS Transformers
- defined EC variant sets to provide coverage for all known devices
---

Michał Mirosław (6):
  mfd: Add driver for ASUS Transformer embedded controller
  input: serio: Add driver for ASUS Transformer dock keyboard and
    touchpad
  input: keyboard: Add driver for ASUS Transformer dock multimedia keys
  leds: Add driver for ASUS Transformer LEDs
  power: supply: Add driver for ASUS Transformer battery
  power: supply: Add charger driver for Asus Transformers

Svyatoslav Ryhel (1):
  dt-bindings: embedded-controller: document ASUS Transformer EC

 .../asus,transformer-ec.yaml                  |  98 +++
 drivers/input/keyboard/Kconfig                |  10 +
 drivers/input/keyboard/Makefile               |   1 +
 .../input/keyboard/asus-transformer-ec-keys.c | 272 +++++++
 drivers/input/serio/Kconfig                   |  15 +
 drivers/input/serio/Makefile                  |   1 +
 drivers/input/serio/asus-transformer-ec-kbc.c | 147 ++++
 drivers/leds/Kconfig                          |  11 +
 drivers/leds/Makefile                         |   1 +
 drivers/leds/leds-asus-transformer-ec.c       |  79 ++
 drivers/mfd/Kconfig                           |  14 +
 drivers/mfd/Makefile                          |   1 +
 drivers/mfd/asus-transformer-ec.c             | 763 ++++++++++++++++++
 drivers/power/supply/Kconfig                  |  22 +
 drivers/power/supply/Makefile                 |   2 +
 .../supply/asus-transformer-ec-battery.c      | 272 +++++++
 .../supply/asus-transformer-ec-charger.c      | 193 +++++
 include/linux/mfd/asus-transformer-ec.h       | 162 ++++
 18 files changed, 2064 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/embedded-controller/asus,transformer-ec.yaml
 create mode 100644 drivers/input/keyboard/asus-transformer-ec-keys.c
 create mode 100644 drivers/input/serio/asus-transformer-ec-kbc.c
 create mode 100644 drivers/leds/leds-asus-transformer-ec.c
 create mode 100644 drivers/mfd/asus-transformer-ec.c
 create mode 100644 drivers/power/supply/asus-transformer-ec-battery.c
 create mode 100644 drivers/power/supply/asus-transformer-ec-charger.c
 create mode 100644 include/linux/mfd/asus-transformer-ec.h

-- 
2.51.0


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox