From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wm1-f67.google.com (mail-wm1-f67.google.com [209.85.128.67]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CA88D32274B for ; Sun, 1 Feb 2026 10:44:13 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.67 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942655; cv=none; b=FQYm2IVEQoYm++sfdmQ+pFhzptMXH2Zasu3iT6UV0ikw2dIogSTLjrqlqytzmYlSSXd5/z2UXmUklqGzI7gA+EJAJHAkI1TWtDyQUZNDFQ0qyF+n5hMvb+QQh4oHV0Zl7+MLIpoziKx0L2iNtpXOKGTgJBxEaeG/UMrMuA7kMrU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942655; c=relaxed/simple; bh=UgzLW6WQiFkkdi/rFQy7Nb0uewdDPhiTeLJ7Dkher+0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FW8wvBZgCUXqW/Aw/NDZSlEg45j/U3Fv8JhsfJk9P2NlkaGxskChCDflxm4yCyT1Pmk1Fal1T5tqghzfz0Nih7HMKZA2/ddYaebq2cNNqQx8sGSiWwlf5BI5ek3zD1jiypp6ZqK4TkEoRZX+jC8z2cNMQBjmjLuaePGiMPEP3+Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=mi32/ymp; arc=none smtp.client-ip=209.85.128.67 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="mi32/ymp" Received: by mail-wm1-f67.google.com with SMTP id 5b1f17b1804b1-48068ed1eccso31720575e9.2 for ; Sun, 01 Feb 2026 02:44:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942652; x=1770547452; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=P47K2h9cgKb+6dAIX40zG1TXVWmp99RTsx0ZYM/JpXo=; b=mi32/ympP84VZQtzjCebifnr0b+nnojEj/gnlhdcwM+DLmeUkYD1DGZNucDFJ2LNay Ewf0aOXHkYYYmMnAPGolNFJy2rwYe9XHlqrA2+z5YUo5/NXNH/DyhLZkSk3cL2sVJagL scQo1KPbjC77jq0Pr855DyS7WXszv72VjrJJa5xu+ziHOTZ3HwcZ9qpBJZr683M+yXjx ORrz0Ii3GQ1y1hNAbrPrdKs4NC3gwYgfuP1wUDlKGuj9a1gDFvt3C+LYokHnka1haR7y +0823bdpL06D8DGAC0z1FU5lM8GxmxiFws/cGf5QmPpB1Fvao/tr++000Lsq2OrlX6lt nz3Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942652; x=1770547452; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=P47K2h9cgKb+6dAIX40zG1TXVWmp99RTsx0ZYM/JpXo=; b=ORX3j1bTKFTeoMh0KX74f0b1VkeDA02WWOxJRw1QdVbziFfP79FLfmDHiepk44ZWdY BE5ADfh0lc84mQFhjwXDL16bm3kt1dDYmgDV+RnCrTq+AIhpqcHyDZzlIDVua/DVCxam AwJWMmnFW03no70PMfJSEn11qTAg5Pqn2CtaB+OVUY3EEmmRxO8j3iznX1Kkfd0Td8cE Iz4jkVFXcsVT6D3jLLUzEwCWh++OCk1y5S3yXfVKd8jvjhNSuNEVwFATyhRZ3+9SdOrb bkBPuE4XKZUb4cUVN7aTGvB/KbwiANHVm/0ccb5Rjn+fk6ccH8DOc3HhW/IAV9sTVHUp a0rQ== X-Forwarded-Encrypted: i=1; AJvYcCXRTQXNvjTGo13L8vCrbC8KBChNfV4H1aj8vmwc72MPrNqDp9ZExuJGRFKzhV7/CZK0TbGjHKu4L1Wa3L0=@vger.kernel.org X-Gm-Message-State: AOJu0YwSF0hQENGjsrPSaNQawFle7lH6MHFKuJ5Pz26IJNJEtJHC5C6B ycDLwjHUWOzOoFYHurvnZSuhUu4HHrCAgYBGqULP2izqpg4NythErV7W X-Gm-Gg: AZuq6aLIAoP2Lyi2mAcgmkABiY2X35nuOSM51POYSn/8weQk+WI2d+F7VcG7cewQ83O a+J9WN6RrcXfCrleiAFP1uIa4JxlSGTiLqkqcgmG4MAA8ctpmZ/lfnP8H+4PwRE0czSwSqQP99x lvg/aQcFKUslljhTTTLG3xAbVfaAn2QpanlUv8rq06ugpHlne2sX+mpH0DKTDgc5UMZJlMux5tP 9zmjomYEIUJZaxxdKNYzUvJgOfv5F7fGs8ZQGP7OTQxDkaZv58ludfNbXDZ5yQnSQERA/Ay7QUZ o1ZLV6/tkeG0IMoBoWxJgAJkfhTKry2TGwTaGAYhn+sn9xImAFptYnfNFRH04DB33HqBdbuAmHv rLh2HvED4KZT2oR7qXzssT+z/cKF9d0pa8cFirypJdVOWmmVq/nPdvY+P3Ed7OPxWcYYavLnhxY 6m X-Received: by 2002:a05:600c:138a:b0:47e:e20e:bbbe with SMTP id 5b1f17b1804b1-482db48e4a7mr113010365e9.25.1769942651960; Sun, 01 Feb 2026 02:44:11 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:11 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 1/9] dt-bindings: misc: document ASUS Transformers EC Dockram Date: Sun, 1 Feb 2026 12:43:35 +0200 Message-ID: <20260201104343.79231-2-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Documenting an I2C device used in conjunction with the EC on ASUS Transformers. The main function of Dockram (the name used by downstream ASUS sources) is to provide power-related functions, such as battery and charger communication. The device is exposed as an individual entity because multiple embedded controllers can utilize the same Dockram instance. Signed-off-by: Svyatoslav Ryhel --- .../bindings/misc/asus,dockram.yaml | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/asus,dockram.yaml diff --git a/Documentation/devicetree/bindings/misc/asus,dockram.yaml b/Doc= umentation/devicetree/bindings/misc/asus,dockram.yaml new file mode 100644 index 000000000000..3c690b83dffe --- /dev/null +++ b/Documentation/devicetree/bindings/misc/asus,dockram.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/misc/asus,dockram.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Asus Transformer EC Dockram + +maintainers: + - Svyatoslav Ryhel + +description: + Dedicated i2c device used to provide power related functions of the + embedded controller used in ASUS Transformer device family. + +properties: + compatible: + const: asus,dockram + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + dockram@17 { + compatible =3D "asus,dockram"; + reg =3D <0x17>; + }; + }; +... --=20 2.51.0 From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wr1-f54.google.com (mail-wr1-f54.google.com [209.85.221.54]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EF76B3246F3 for ; Sun, 1 Feb 2026 10:44:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942657; cv=none; b=FfgpcaQqzhY9NWBnvC5IHnZsTYAxU1MQk34q69CNfYvtNQDKS/rPpMGEzR5OENPDc/3J7SwiFX6G2T4C/hwWDSZ7VNchEZCb6chC9H7ubDiZCqhoNs86w2R3C1WjtmrVQQ7s5qPSSQQwO//afxY3xApJJ7SOk2fgB06BRadB3Dw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942657; c=relaxed/simple; bh=MmvcjAHmY9aFurBioYKxYGT+fXCRvKADDio6QvZg5Do=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JX02af+4rkKg382Y3rR8vqqdy8J4J0/8UpAqVgWrjHSqPBlBva8qkc3F0SsrNl9DJqGfkU+SbvzrTKyUFxf6zRbRFYhkp83XdiBEDkMVTfpgpZJ/3mGkTSf+ekdnM3lToRjznMWjKfAgwmtW85hqcnCINKyjQ6L5dBi5O6Fk7A8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=IK8fV1qZ; arc=none smtp.client-ip=209.85.221.54 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="IK8fV1qZ" Received: by mail-wr1-f54.google.com with SMTP id ffacd0b85a97d-432da746749so2005573f8f.0 for ; Sun, 01 Feb 2026 02:44:14 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942653; x=1770547453; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=FW0JM8bdWfKfSamq7DjcZL13sVfyz7u3bs6KeiNnLcY=; b=IK8fV1qZjVEFFQ1iUfVaNwe6Vf2H/FmtinypcsKPqdJrqOWRAlySQ3NZyoUEdDC15p bt6VHwibGoUZZrq8dxj+GYXTPc6cGEOx4jGLHWD/yih9EPXr/lyDNr1H9WR7sN6j1idw GbxYYAC1dVqlmygLc7A7ZbtkKZy6bgDTF+bRKFSppdcboLweRhiVtdDwgyoszY9eX7I9 Mq61VocPi84A7iKq3Y0hjS5v7GwLX7hGQxzwhb247D4+yRcOrZ+0WV+V+w+nBv90pi9l S+Ac7tq6G0c7CQAbyvIfONi9PvK9sp3YPZ+ri+I5fSH4xAmcAbZFxd+pK8EE4yfCjzzz 7mFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942653; x=1770547453; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=FW0JM8bdWfKfSamq7DjcZL13sVfyz7u3bs6KeiNnLcY=; b=pvUMss9zsTsm2MRw8lVnovyZ+Bq011Ptd0FOqU083CMi7Ip2Dbfi4WcEAm+5CUZ9bl mT+loxan3rHjfAOqS3+j0wifOrAwMG8iRz1HbAXii/l2pkZF0Cswbq/j0UTvWMQldLlm n9qp+n53Z46Vk/QtIlMiYN82V+zkrEySiQPb/XMEUizuxXOrcm6TuY28yDULq4kczr98 Niyf/Gidos/MbYaWifL3XuPae6IzjeH/anYfVmvjiNL2dq/RmrhrvdL5a/xgKLYcCi/n F7BHCFbI8+SVlFQzaPUjvLWxIyxvqHmFcDgD72fxpQyKW7kDoAqmFvtnSebOOk5IuOz1 AF9g== X-Forwarded-Encrypted: i=1; AJvYcCVwBz0SHDqfW+mlfW497LRok1TX3S1D0yjflyF5Oh21z11YjnNfEfF//JIAX+9l23AC5SL7flJ5BU1lni4=@vger.kernel.org X-Gm-Message-State: AOJu0YxxxDvvrxnmIkoNk4zMa70YZQhgB/2kwn1tgF4rxeDQohSJ73FZ f97vZzf5iFmbSKiYBVcBo/pvIbfkLG8xv8Dj69mq8Ds3RqBhb6Hjn2Ke X-Gm-Gg: AZuq6aJLBOxJaRS8bL+3HcTqyYHy5JZcAYkH/9/ZMZdymJAHKsXQGo4DPAxEngBpdyX 5y/wboAJmrH7IzthHC7gjhDIIr8OUOHUYR1hTPDJ0qmimr2lsUqtnPTtSvVRzLPRSX6KJ5E2F/h RieOomu1wDmLPaNTyrIIGGmFS4KesuzWPxzpAsR1fpuAdWSmf2WNzC2HSSmSjzCffsmRINk0RE0 UmHv39GsLU3QOAf6T4lpDbWqPlXaD0ZQ0zXicSUmMckyJ4VACSD168z417e4ndW1yzj7eF9Nh1n T2asdSG9o/3LLQxeipBENBhCxJgq4KPbwU07wJqX/HaT5Clh/DkR5Aj/FYk1gxaD0nW8BpF7SoQ G9tdRHuphp18UENQLly0DMEGB1AvV4P52TIek6Eoc7uOmFopPXv1yloJUSl0T0n+6aNfywSH8oK 4T X-Received: by 2002:a05:600c:1385:b0:480:6852:8d94 with SMTP id 5b1f17b1804b1-482db499781mr103571145e9.27.1769942653146; Sun, 01 Feb 2026 02:44:13 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:12 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 2/9] misc: Support Asus Transformer's EC access device Date: Sun, 1 Feb 2026 12:43:36 +0200 Message-ID: <20260201104343.79231-3-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Micha=C5=82 Miros=C5=82aw Add support for accessing Embedded Controller of Asus Transformer devices. This will be used by the EC MFD drivers. Signed-off-by: Micha=C5=82 Miros=C5=82aw Signed-off-by: Svyatoslav Ryhel --- drivers/misc/Kconfig | 9 + drivers/misc/Makefile | 1 + drivers/misc/asus-dockram.c | 327 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/asus-ec.h | 18 ++ 4 files changed, 355 insertions(+) create mode 100644 drivers/misc/asus-dockram.c create mode 100644 include/linux/mfd/asus-ec.h diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index dd1ab7e445ac..e7faa7ab4199 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -50,6 +50,15 @@ config AD525X_DPOT_SPI To compile this driver as a module, choose M here: the module will be called ad525x_dpot-spi. =20 +config ASUS_DOCKRAM + tristate "Asus Transformer's EC DockRAM" + depends on I2C + help + Select this if you are building for Asus Transformer's. + + To compile this driver as a module, choose M here: the + module will be called asus-dockram. + config DUMMY_IRQ tristate "Dummy IRQ handler" help diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index bfad6982591c..d2287e912d59 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_IBMVMC) +=3D ibmvmc.o obj-$(CONFIG_AD525X_DPOT) +=3D ad525x_dpot.o obj-$(CONFIG_AD525X_DPOT_I2C) +=3D ad525x_dpot-i2c.o obj-$(CONFIG_AD525X_DPOT_SPI) +=3D ad525x_dpot-spi.o +obj-$(CONFIG_ASUS_DOCKRAM) +=3D asus-dockram.o obj-$(CONFIG_ATMEL_SSC) +=3D atmel-ssc.o obj-$(CONFIG_DUMMY_IRQ) +=3D dummy-irq.o obj-$(CONFIG_ICS932S401) +=3D ics932s401.o diff --git a/drivers/misc/asus-dockram.c b/drivers/misc/asus-dockram.c new file mode 100644 index 000000000000..d98dcf5ef2d4 --- /dev/null +++ b/drivers/misc/asus-dockram.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ASUS EC: DockRAM + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct dockram_ec_data { + struct mutex ctl_lock; /* prevent simultaneous access */ + char ctl_data[DOCKRAM_ENTRY_BUFSIZE]; +}; + +int asus_dockram_read(struct i2c_client *client, int reg, char *buf) +{ + int rc; + + memset(buf, 0, DOCKRAM_ENTRY_BUFSIZE); + rc =3D i2c_smbus_read_i2c_block_data(client, reg, DOCKRAM_ENTRY_BUFSIZE, = buf); + if (rc < 0) + return rc; + + if (buf[0] > DOCKRAM_ENTRY_SIZE) { + dev_err(&client->dev, "bad data len; buffer: %*ph; rc: %d\n", + DOCKRAM_ENTRY_BUFSIZE, buf, rc); + return -EPROTO; + } + + dev_dbg(&client->dev, "got data; buffer: %*ph; rc: %d\n", + DOCKRAM_ENTRY_BUFSIZE, buf, rc); + + return 0; +} +EXPORT_SYMBOL_GPL(asus_dockram_read); + +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); + +int asus_dockram_access_ctl(struct i2c_client *client, + u64 *out, u64 mask, u64 xor) +{ + struct dockram_ec_data *priv =3D i2c_get_clientdata(client); + char *buf =3D priv->ctl_data; + u64 val; + int ret =3D 0; + + guard(mutex)(&priv->ctl_lock); + + ret =3D asus_dockram_read(client, ASUSEC_DOCKRAM_CONTROL, buf); + if (ret < 0) + goto exit; + + if (buf[0] !=3D ASUSEC_CTL_SIZE) { + ret =3D -EPROTO; + goto exit; + } + + val =3D get_unaligned_le64(buf + 1); + + if (out) + *out =3D val; + + if (mask || xor) { + put_unaligned_le64((val & ~mask) ^ xor, buf + 1); + ret =3D 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 ssize_t dockram_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client =3D kobj_to_i2c_client(kobj); + unsigned int reg; + ssize_t n_read =3D 0; + char *data; + int ret; + + reg =3D off / DOCKRAM_ENTRY_SIZE; + off %=3D DOCKRAM_ENTRY_SIZE; + + if (!count) + return 0; + + data =3D kmalloc(DOCKRAM_ENTRY_BUFSIZE, GFP_KERNEL); + + while (reg < DOCKRAM_ENTRIES) { + unsigned int len =3D DOCKRAM_ENTRY_SIZE - off; + + if (len > count) + len =3D count; + + ret =3D asus_dockram_read(client, reg, data); + if (ret < 0) { + if (!n_read) + n_read =3D ret; + break; + } + + memcpy(buf, data + 1 + off, len); + n_read +=3D len; + + if (len =3D=3D count) + break; + + count -=3D len; + buf +=3D len; + off =3D 0; + ++reg; + } + + kfree(data); + + return n_read; +} + +static int dockram_write_one(struct i2c_client *client, int reg, + const char *buf, size_t count) +{ + struct dockram_ec_data *priv =3D i2c_get_clientdata(client); + int ret; + + if (!count || count > DOCKRAM_ENTRY_SIZE) + return -EINVAL; + if (buf[0] !=3D count - 1) + return -EINVAL; + + guard(mutex)(&priv->ctl_lock); + + priv->ctl_data[0] =3D (u8)count; + memcpy(priv->ctl_data + 1, buf, count); + ret =3D asus_dockram_write(client, reg, priv->ctl_data); + + return ret; +} + +static ssize_t dockram_write(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct i2c_client *client =3D kobj_to_i2c_client(kobj); + unsigned int reg; + int ret; + + if (off % DOCKRAM_ENTRY_SIZE !=3D 0) + return -EINVAL; + + reg =3D off / DOCKRAM_ENTRY_SIZE; + if (reg >=3D DOCKRAM_ENTRIES) + return -EINVAL; + + ret =3D dockram_write_one(client, reg, buf, count); + + return ret < 0 ? ret : count; +} + +static ssize_t control_reg_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client =3D to_i2c_client(dev); + u64 val; + int ret; + + ret =3D asus_dockram_access_ctl(client, &val, 0, 0); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%016llx\n", val); +} + +static ssize_t control_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client =3D to_i2c_client(dev); + u64 val; + int ret; + + ret =3D kstrtoull(buf, 16, &val); + if (ret < 0) + return ret; + + ret =3D asus_dockram_access_ctl(client, NULL, ~0ull, val); + if (ret < 0) + return ret; + + return count; +} + +static BIN_ATTR_RW(dockram, DOCKRAM_ENTRIES * DOCKRAM_ENTRY_SIZE); +static DEVICE_ATTR_RW(control_reg); + +static struct attribute *dockram_attrs[] =3D { + &dev_attr_control_reg.attr, + NULL +}; + +static const struct bin_attribute *dockram_bin_attrs[] =3D { + &bin_attr_dockram, + NULL +}; + +static const struct attribute_group dockram_group =3D { + .attrs =3D dockram_attrs, + .bin_attrs =3D dockram_bin_attrs, +}; + +static int asus_dockram_probe(struct i2c_client *client) +{ + struct dockram_ec_data *priv; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return dev_err_probe(&client->dev, -ENXIO, + "I2C bus is missing required SMBus block mode support\n"); + + priv =3D devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(client, priv); + mutex_init(&priv->ctl_lock); + + return devm_device_add_group(&client->dev, &dockram_group); +} + +static const struct of_device_id asus_dockram_ids[] =3D { + { .compatible =3D "asus,dockram" }, + { } +}; +MODULE_DEVICE_TABLE(of, asus_dockram_ids); + +static struct i2c_driver asus_dockram_driver =3D { + .driver =3D { + .name =3D "asus-dockram", + .of_match_table =3D of_match_ptr(asus_dockram_ids), + }, + .probe =3D asus_dockram_probe, +}; +module_i2c_driver(asus_dockram_driver); + +static void devm_i2c_device_release(struct device *dev, void *res) +{ + struct i2c_client **pdev =3D res; + struct i2c_client *child =3D *pdev; + + if (child) + put_device(&child->dev); +} + +static struct i2c_client *devm_i2c_device_get_by_phandle(struct device *de= v, + const char *name, + int index) +{ + struct device_node *np; + struct i2c_client **pdev; + + pdev =3D devres_alloc(devm_i2c_device_release, sizeof(*pdev), + GFP_KERNEL); + if (!pdev) + return ERR_PTR(-ENOMEM); + + np =3D of_parse_phandle(dev_of_node(dev), name, index); + if (!np) { + devres_free(pdev); + dev_err(dev, "can't resolve phandle %s: %d\n", name, index); + return ERR_PTR(-ENODEV); + } + + *pdev =3D of_find_i2c_device_by_node(np); + of_node_put(np); + + if (!*pdev) { + devres_free(pdev); + return ERR_PTR(-EPROBE_DEFER); + } + + devres_add(dev, pdev); + + return *pdev; +} + +struct i2c_client *devm_asus_dockram_get(struct device *parent) +{ + struct i2c_client *dockram =3D + devm_i2c_device_get_by_phandle(parent, "asus,dockram", 0); + + if (IS_ERR(dockram)) + return dockram; + if (!dockram->dev.driver) + return ERR_PTR(-EPROBE_DEFER); + if (dockram->dev.driver !=3D &asus_dockram_driver.driver) + return ERR_PTR(-EBUSY); + + return dockram; +} +EXPORT_SYMBOL_GPL(devm_asus_dockram_get); + +MODULE_AUTHOR("Micha=C5=82 Miros=C5=82aw "); +MODULE_DESCRIPTION("ASUS Transformer's dockram driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/asus-ec.h b/include/linux/mfd/asus-ec.h new file mode 100644 index 000000000000..bc4efa37f5ba --- /dev/null +++ b/include/linux/mfd/asus-ec.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __MISC_ASUS_EC_H +#define __MISC_ASUS_EC_H + +struct i2c_client; + +/* 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); +struct i2c_client *devm_asus_dockram_get(struct device *parent); + +#define DOCKRAM_ENTRIES 0x100 +#define DOCKRAM_ENTRY_SIZE 32 +#define DOCKRAM_ENTRY_BUFSIZE (DOCKRAM_ENTRY_SIZE + 1) + +#endif /* __MISC_ASUS_EC_H */ --=20 2.51.0 From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wm1-f68.google.com (mail-wm1-f68.google.com [209.85.128.68]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF8F732548B for ; Sun, 1 Feb 2026 10:44:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.68 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942658; cv=none; b=ZABH85Jf4YNk8X33QSLWFb0XF6xMUBnjUA7YKVNmJ/ipHCZWTo7NOK4YcYCVRyYTtSOEBh1ov6Mz9bgCybSmHnj8+Zk+7au14rp7cnJETqfSW8zXwBzXU3ydzUUBkl+OqQq/wnSN/HuGNs+tOmKRKTw4Vg4qTtnisyQWDn8AIy0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942658; c=relaxed/simple; bh=0N+pk2TI+JtovuI3l2xhTK/qQ/ayuu1+Gg+HuId26qk=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DxrtdGuL1nmzYo16uICobpQke/khqFjzCg3kUgermfn+CBPwdJlTuMUhotfz9CeUUH5/E5btJW8oDRiuqXu4FE+uhrOHVEytR59KJkr0OnoRMANEfDkIarxjFHa0wAeOR2egZUtRYUNEjFzZvYlLVmn91MXRhk/oVFZdJnurPW8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=HcJhkolq; arc=none smtp.client-ip=209.85.128.68 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HcJhkolq" Received: by mail-wm1-f68.google.com with SMTP id 5b1f17b1804b1-4806d23e9f1so39160785e9.2 for ; Sun, 01 Feb 2026 02:44:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942654; x=1770547454; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=tIkuCcLFcESeg4evD7K33beEJL3vgA15kSHNr6N6YkQ=; b=HcJhkolqx7Nr4774jKrYtvbesalaQ38nWA1bXtBl3+Kwtzvdk0i+0715eFIoH2NrMf q6KsdfSU2o/vvgae2N2ysr58FEMvi8nVSWew+S8mckqnkYzh0Ri8KHOqQbdiu4Mxp5XJ TTf3vwZJLCQYZxp10xADD6amI731nKONjpm5fnaXDSznJjDf2MVwheHo0Q8hljYuLwG4 H985BCeSVru6DArpR9Icfi5c2UnToqJfkWku/j5jDcPUZYr6W8nh+ICxCTXVU82Ujh6b KO6AwhhmWcLAe1Aw5sW4cWRFmIZDIKyVtkkNdOl1v/FfmHNBh6gvkJXLXBmhEvEbYtab wMCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942654; x=1770547454; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=tIkuCcLFcESeg4evD7K33beEJL3vgA15kSHNr6N6YkQ=; b=jwgydc4Dg4TZf7cj/Sn9DzZ7ZYKAJ49x1iM8lcbVOuN7zCPxhEWPJDHB//J6vtXXtH S8LbOusZC6IBNPRM/iXiEC+VIeDVt4EzkKZbLjCwOhg8Yb2ih81QW5taNG6f+06B2uXz qnVU+8Cnjtkw9y5PHs3Xil/UdEp0T5C64i0M5nMCIIWn5rAC+oqHe9i5ssuHImM2ub5Z PbLD87DNIQEOknu9d6/wF0IHRICqK2e6bwCF3hKOGDwmrH1LuDxyrqVVUKOEi1fat8/a FK0qQK3V4ImZgTMDNh0YhqZ1Nx4RkmxgXtFkzNLK1PqP3Yow1dLcW2pis46BLmkeG06o spkw== X-Forwarded-Encrypted: i=1; AJvYcCU9lWWq7wnF9ZnlzU0da/YYGXB2u0Z3JLmJxw2T7s43Ljl4T3FLO4QR7ByJPJYMPqa7W7rQahk90k/nj+M=@vger.kernel.org X-Gm-Message-State: AOJu0Yx2KQ6n1PklhfFTfv6bfiYoZ65SbrRN7X8kaVO+TMDLq72aIgeg 7Z0ylDE+eELBKg0q3WP3F14exNe39N11H8UkggsfKlTeYxnVJG7/dt0F X-Gm-Gg: AZuq6aKfas+ftAG2C2kaYArfJY5wwtRiZJD7IXjR47dBnHtg7R5KODMJZbNbw2WxcHC /Rlhm2lmOksqo9DIBuqNIz9XW9/Cd3XIGXn/3zTtk1lTvFv3Ar92BsZMw4AGSOzGivwAmGhgaQn ydvJKZrJn5qZvWzY1Fmz6wgfD6bzAkrqvggeB2f4ihrpzb7NeOeNdjcPgUh6M3pc30yGw5K9UEw gPyUZPvgmTsME/RFTtlaIB4vmBWBT7+8wBWKTX77Oe/ZtmKNhyp2JVMmXRzDOvngSWA+Sx7joaQ 7joncEJVIgK0c6y1Vj5MA2YRKE8uDfu2c7W8WpoXgUALptHUxVdfZ30Gj5ZgmZfcpRgGSHh237y t4tJdXLjQEav+lA05/SecKSgEAw1OkPcmLplCWjQTPHRAq2ucveoLrGZfNaCM8EKw10AXgBXIs3 b8 X-Received: by 2002:a05:600c:3b1d:b0:477:9cdb:e32e with SMTP id 5b1f17b1804b1-482db46014dmr106028315e9.9.1769942654259; Sun, 01 Feb 2026 02:44:14 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:13 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 3/9] dt-bindings: mfd: document ASUS Transformer EC Date: Sun, 1 Feb 2026 12:43:37 +0200 Message-ID: <20260201104343.79231-4-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Document embedded controller used in ASUS Transformer device series. Signed-off-by: Svyatoslav Ryhel --- .../devicetree/bindings/mfd/asus,ec.yaml | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/asus,ec.yaml diff --git a/Documentation/devicetree/bindings/mfd/asus,ec.yaml b/Documenta= tion/devicetree/bindings/mfd/asus,ec.yaml new file mode 100644 index 000000000000..5c28deebce7b --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/asus,ec.yaml @@ -0,0 +1,153 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/asus,ec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ASUS Transformer's Embedded Controller + +description: + Several Nuvoton based Embedded Controller attached to an I2C bus, + running a custom ASUS firmware, specific to the Asus Transformer + device series. + +maintainers: + - Svyatoslav Ryhel + +properties: + compatible: + oneOf: + - enum: + - asus,ec-pad # Pad part of Asus Transformer + - asus,ec-dock # Dock part of Asus Transformer + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + request-gpio: + maxItems: 1 + + asus,dockram: + $ref: /schemas/types.yaml#/definitions/phandle + description: I2C device used to access power related functions. + + battery: + type: object + $ref: /schemas/power/supply/power-supply.yaml + unevaluatedProperties: false + + properties: + compatible: + const: asus,ec-battery + + required: + - compatible + + charger: + type: object + $ref: /schemas/power/supply/power-supply.yaml + additionalProperties: false + + properties: + compatible: + const: asus,ec-charger + + monitored-battery: true + + required: + - compatible + + keyboard-ext: + type: object + description: top row of multimedia keys + additionalProperties: false + + properties: + compatible: + const: asus,ec-keys + + required: + - compatible + + led: + type: object + additionalProperties: false + + properties: + compatible: + const: asus,ec-led + + required: + - compatible + + serio: + type: object + description: keyboard and touchpad + additionalProperties: false + + properties: + compatible: + const: asus,ec-kbc + + required: + - compatible + + asus,clear-factory-mode: + type: boolean + description: clear Factory Mode bit in EC control register + additionalProperties: false + +required: + - compatible + - reg + - interrupts + - request-gpio + - asus,dockram + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + embedded-controller@19 { + compatible =3D "asus,ec-dock"; + reg =3D <0x19>; + + interrupt-parent =3D <&gpio>; + interrupts =3D <151 IRQ_TYPE_LEVEL_LOW>; + + request-gpio =3D <&gpio 134 GPIO_ACTIVE_LOW>; + asus,dockram =3D <&dockram_ec>; + + battery { + compatible =3D "asus,ec-battery"; + monitored-battery =3D <&dock_battery>; + }; + + charger { + compatible =3D "asus,ec-charger"; + monitored-battery =3D <&dock_battery>; + }; + + keyboard-ext { + compatible =3D "asus,ec-keys"; + }; + + led { + compatible =3D "asus,ec-led"; + }; + + serio { + compatible =3D "asus,ec-kbc"; + }; + }; + }; +... --=20 2.51.0 From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7EEDB328254 for ; Sun, 1 Feb 2026 10:44:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942660; cv=none; b=LcRFi/sykpPBejCyrM9efHs2maB0KDBdtPkZQo6eeGvvysNXTc8kgB7FrRfCsY5jEipOEdLMsTGJo4ZQYLy5rmUlnQLC6NJqIdd3es2jFZnqW4G+d4RVQ0Z1SeAe17Ml9LXEua2fMaubCsg66TZJ6lomg+EyE0ipWTL4CmgEihE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942660; c=relaxed/simple; bh=2AfNJD4L0rq1KzdDBDlAr7eYuOto2g4n0W7mmtI2Pa0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=FdKbKPoQiJV/gPzBkRER0p0t4VyNzIrBrw/YKMshcaNPhIV751oOFeknVMlQfdBKQW1ZdSrOzbBxXyFYGkJVGFPWi3+b1wLtuWht6cRySGNcg/J8vOnEwDIiZQ/urg0qz+sEztIcADymGfs4kOLcqFfhaa7X2GyLB7DXQ3AztHA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GGNeTbdA; arc=none smtp.client-ip=209.85.128.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GGNeTbdA" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-4806f3fc50bso35774385e9.0 for ; Sun, 01 Feb 2026 02:44:17 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942656; x=1770547456; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=IUWqWBNmt95ycDvovtO/3m39YHg1WQW5qxhoJyj0xwg=; b=GGNeTbdAP20L5r+BWml6wP5b7eEphVUtdBxb/vbTOKRPyCbo5QXShvynGowiVYBDP6 eeUN2+0Ol/RiWkQ48P0lO24wiciV1CBIxOuZ3GE4TThjrOnueRdXm9jPmppfNEa3LpGA aMkgPq+QsqU6PBQ2nsD0cFdKb8LzG2fxHB1qGwpCKnbiybI54cH/j0hasczycTxNN8vy +4RedOSfiG5DW4FvzX9eu30R4CutHGOWrvKEaXcld5CH4yU0dK8cH3qQsp6uDxDeoEN+ yIH8Sg/PokSwKWM56P12G6wYGBiK9GrkSjhgy72RLgq5gjUeSU3+kJ3V7dQO0yPyz0NN Kr7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942656; x=1770547456; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=IUWqWBNmt95ycDvovtO/3m39YHg1WQW5qxhoJyj0xwg=; b=fc8wkyipmHNw8byqWe7dq+3c8T5oKeMPC5wRcNSGTt6zjvVClgcfK38WaPhf/JmhTV zDIrjhqzhfFqkrnK8Wchj9578rjc81RexGg74459NOg5/54gXcIZXInifzp9sBGQsw4h mxOoJroUBl8IQ/Dw3NuM3xZr4fyuJwfETlGHjJ3YsuTiY0vpQhUpTGCnItyOhf0mT3XJ me9KmA9NwNnL4Y6EQjAQCPQu4q5JIR8H7expwNWaOAPxdFmccQXCka/m0FKN0+A8KqVq +0qi6X042GBN2BqKlb+o7Ev+ErkVv5GZKBcQ5B2V/VNI8Gxe1Fl6LoObZSjkcWjc3Exg p8rA== X-Forwarded-Encrypted: i=1; AJvYcCWUV1YNBflCpzse1Pu2Kh8ajpXuYRoTIJKUeTy1rsImfYmrHKp5QSBobzT794EJ4MdR5GmWDFL9xEDsBEY=@vger.kernel.org X-Gm-Message-State: AOJu0YwYTFtPZGpRVjO0dDSXuAGhQihaAn6dc/u+Chsnq5D+YPNoVr3N DqwJMCtqlWxVQrd4lvtY0BlGxr2moHDNSLV9gwKZS86SMKZ1Ey/v2c5c X-Gm-Gg: AZuq6aLNspIGfKS18HwHIemAxwsX/YaQc6kingXHVgBdi+gpQO3NC3d1lwZPscEpliW H3JPJrpaxvEkgmvX437bHfZgskj++PCon1AK0Xw6Ckk3qL/zO7aybF9H8z6v9sMEMn0AfGlcM/Z DaFP1UIsJu35nKJA/WlJ3SavpJ0VMUQccQZZzubu3cD2g1oUjC2K4OWM6sxsGUipVUAfn6lRhge NIq7Oj0FWbeK1n2rSsr18CQvpNImgQqe6gKQ0FZIoqEs1sRg3Mrh1jrSqDhMOZwLXt9lOjXKsiF uh/1UB79BWB5j6xX2MuBh/aZsnnup1DF8pPMV1knknOiZ9gyDvrWY4rGJwnrzylpVxtBbZo0dZn 0DZhDBmL3MHSoq4HTqFTgeDKmxX4WvU8tifSALlzclreRKSHBMGp+RA8IgZ1Wn7GPk5xMLHe0GQ YT X-Received: by 2002:a05:600c:64c6:b0:477:5ad9:6df1 with SMTP id 5b1f17b1804b1-482db4593a1mr108613865e9.3.1769942655480; Sun, 01 Feb 2026 02:44:15 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:15 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 4/9] mfd: Add driver for Asus Transformer embedded controller Date: Sun, 1 Feb 2026 12:43:38 +0200 Message-ID: <20260201104343.79231-5-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Micha=C5=82 Miros=C5=82aw 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 Signed-off-by: Svyatoslav Ryhel Signed-off-by: Micha=C5=82 Miros=C5=82aw --- drivers/mfd/Kconfig | 14 ++ drivers/mfd/Makefile | 1 + drivers/mfd/asus-ec.c | 460 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/asus-ec.h | 101 +++++++- 4 files changed, 573 insertions(+), 3 deletions(-) create mode 100644 drivers/mfd/asus-ec.c diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7192c9d1d268..c7b32a4e65fa 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. =20 +config MFD_ASUSEC + tristate "ASUS embedded controller" + depends on I2C && OF + select SYSFS + select ASUS_DOCKRAM + help + Support ECs as found in ASUS Transformer's Pad and Mobile Dock. + + This provides shared glue for functional part drivers: + leds-asusec, serio-asusec, asusec-ext-keys, asusec-battery. + + This driver can also be built as a module. If so, the module + will be called asus-ec. + config MFD_AT91_USART tristate "AT91 USART Driver" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e75e8045c28a..b676922601ba 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_MFD_88PM805) +=3D 88pm805.o 88pm80x.o obj-$(CONFIG_MFD_88PM886_PMIC) +=3D 88pm886.o obj-$(CONFIG_MFD_ACT8945A) +=3D act8945a.o obj-$(CONFIG_MFD_SM501) +=3D sm501.o +obj-$(CONFIG_MFD_ASUSEC) +=3D asus-ec.o obj-$(CONFIG_ARCH_BCM2835) +=3D bcm2835-pm.o obj-$(CONFIG_MFD_BCM590XX) +=3D bcm590xx.o obj-$(CONFIG_MFD_BD9571MWV) +=3D bd9571mwv.o diff --git a/drivers/mfd/asus-ec.c b/drivers/mfd/asus-ec.c new file mode 100644 index 000000000000..09a914a963fe --- /dev/null +++ b/drivers/mfd/asus-ec.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ASUS EC driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASUSEC_RSP_BUFFER_SIZE 8 + +struct asus_ec_chip_data { + const char *name; + const struct mfd_cell *mfd_devices; + unsigned int num_devices; +}; + +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; + u8 ec_data[DOCKRAM_ENTRY_BUFSIZE]; + bool logging_disabled; +}; + +#define to_ec_data(ec) \ + container_of(ec, struct asus_ec_data, info) + +static void asus_ec_remove_notifier(struct device *dev, void *res) +{ + struct asusec_info *ec =3D dev_get_drvdata(dev->parent); + struct notifier_block **nb =3D res; + + blocking_notifier_chain_unregister(&ec->notify_list, *nb); +} + +int devm_asus_ec_register_notifier(struct platform_device *pdev, + struct notifier_block *nb) +{ + struct asusec_info *ec =3D dev_get_drvdata(pdev->dev.parent); + struct notifier_block **res; + int ret; + + res =3D devres_alloc(asus_ec_remove_notifier, sizeof(*res), GFP_KERNEL); + if (!res) + return -ENOMEM; + + *res =3D nb; + ret =3D 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 =3D 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 =3D i2c_smbus_write_word_data(priv->self, 0x64, data); + + dev_dbg(&priv->self->dev, "EC write: %04x, ret =3D %d\n", data, ret); + return ret; +} + +static int asus_ec_read(struct asus_ec_data *priv, bool in_irq) +{ + int ret =3D i2c_smbus_read_i2c_block_data(priv->self, 0x6A, + sizeof(priv->ec_data), + priv->ec_data); + + dev_dbg(&priv->self->dev, "EC read: %*ph, ret =3D %d%s\n", + sizeof(priv->ec_data), priv->ec_data, + ret, in_irq ? "; in irq" : ""); + + return ret; +} + +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 =3D 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 =3D 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 =3D kstrndup(buf + 1, buf[0], GFP_KERNEL); + + return 0; +} + +static int asus_ec_reset(struct asus_ec_data *priv) +{ + int retry, ret; + + for (retry =3D 0; retry < 3; retry++) { + ret =3D 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 =3D asus_ec_get_ctl(&priv->info, &flag); + if (ret < 0) + return ret; + + flag &=3D 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" : "norma= l"); + 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 cod= e); + +static irqreturn_t asus_ec_interrupt(int irq, void *dev_id) +{ + struct asus_ec_data *priv =3D dev_id; + unsigned long notify_action; + int ret; + + ret =3D asus_ec_read(priv, true); + if (ret <=3D 0 || !(priv->ec_data[1] & ASUSEC_OBF_MASK)) + return IRQ_NONE; + + notify_action =3D priv->ec_data[1]; + if (notify_action & ASUSEC_SMI_MASK) { + unsigned int code =3D priv->ec_data[2]; + + asus_ec_handle_smi(priv, code); + + notify_action |=3D 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 =3D NULL; + int ret; + + ret =3D asus_ec_reset(priv); + if (ret) + goto err_exit; + + asus_ec_clear_buffer(priv); + + ret =3D asus_ec_log_info(priv, ASUSEC_DOCKRAM_INFO_MODEL, "model", &model= ); + if (ret) + goto err_exit; + + ret =3D asus_ec_log_info(priv, ASUSEC_DOCKRAM_INFO_FW, "FW version", NULL= ); + if (ret) + goto err_exit; + + ret =3D asus_ec_log_info(priv, ASUSEC_DOCKRAM_INFO_CFGFMT, "Config format= ", NULL); + if (ret) + goto err_exit; + + ret =3D asus_ec_log_info(priv, ASUSEC_DOCKRAM_INFO_HW, "HW version", NULL= ); + if (ret) + goto err_exit; + + priv->logging_disabled =3D true; + + ret =3D asus_ec_magic_debug(priv); + if (ret) + goto err_exit; + + priv->info.model =3D model; + priv->info.name =3D priv->data->name; + + if (device_property_read_bool(&priv->self->dev, "asus,clear-factory-mode"= )) + 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 cod= e) +{ + 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 ec_request_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct asusec_info *ec =3D dev_get_drvdata(dev); + + asus_ec_signal_request(ec); + + return count; +} + +static ssize_t ec_irq_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct asusec_info *ec =3D dev_get_drvdata(dev); + struct asus_ec_data *priv =3D to_ec_data(ec); + + irq_wake_thread(priv->self->irq, priv); + + return count; +} + +static DEVICE_ATTR_WO(ec_request); +static DEVICE_ATTR_WO(ec_irq); + +static struct attribute *asus_ec_attributes[] =3D { + &dev_attr_ec_request.attr, + &dev_attr_ec_irq.attr, + NULL +}; + +static const struct attribute_group asus_ec_attr_group =3D { + .attrs =3D asus_ec_attributes, +}; + +static void asus_ec_sysfs_release(void *data) +{ + struct i2c_client *client =3D data; + + sysfs_remove_link(&client->dev.kobj, "dockram"); +} + +static int asus_ec_probe(struct i2c_client *client) +{ + struct asus_ec_data *priv; + int ret; + + priv =3D devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->data =3D device_get_match_data(&client->dev); + if (!priv->data) + return -ENODEV; + + i2c_set_clientdata(client, priv); + priv->self =3D client; + + priv->info.dockram =3D devm_asus_dockram_get(&client->dev); + if (IS_ERR(priv->info.dockram)) + return dev_err_probe(&client->dev, PTR_ERR(priv->info.dockram), + "failed to get dockram\n"); + + priv->ecreq =3D devm_gpiod_get(&client->dev, "request", GPIOD_OUT_LOW); + if (IS_ERR(priv->ecreq)) + return dev_err_probe(&client->dev, PTR_ERR(priv->ecreq), + "failed to get request GPIO\n"); + + BLOCKING_INIT_NOTIFIER_HEAD(&priv->info.notify_list); + mutex_init(&priv->ecreq_lock); + + ret =3D devm_device_add_group(&client->dev, &asus_ec_attr_group); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to create sysfs attributes\n"); + + ret =3D sysfs_create_link(&client->dev.kobj, + &priv->info.dockram->dev.kobj, + "dockram"); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to create sysfs link 'dockram'\n"); + + ret =3D devm_add_action_or_reset(&client->dev, asus_ec_sysfs_release, + client); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to register sysfs release\n"); + + asus_ec_signal_request(&priv->info); + + ret =3D asus_ec_detect(priv); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to detect EC version\n"); + + ret =3D devm_request_threaded_irq(&client->dev, client->irq, + NULL, &asus_ec_interrupt, + IRQF_ONESHOT | IRQF_SHARED, + client->name, priv); + if (ret) + return dev_err_probe(&client->dev, ret, + "failed to register IRQ\n"); + + /* Parent I2C controller uses DMA, ASUS EC and child devices do not */ + client->dev.coherent_dma_mask =3D 0; + client->dev.dma_mask =3D &client->dev.coherent_dma_mask; + + return devm_mfd_add_devices(&client->dev, 0, priv->data->mfd_devices, + priv->data->num_devices, NULL, 0, NULL); +} + +static const struct mfd_cell asus_ec_pad_mfd_devices[] =3D { + { + .name =3D "asus-ec-battery", + .id =3D 0, + .of_compatible =3D "asus,ec-battery", + }, { + .name =3D "asus-ec-charger", + .id =3D 0, + .of_compatible =3D "asus,ec-charger", + }, { + .name =3D "asus-ec-led", + .id =3D 0, + .of_compatible =3D "asus,ec-led", + }, +}; + +static const struct mfd_cell asus_ec_dock_mfd_devices[] =3D { + { + .name =3D "asus-ec-battery", + .id =3D 1, + .of_compatible =3D "asus,ec-battery", + }, { + .name =3D "asus-ec-charger", + .id =3D 1, + .of_compatible =3D "asus,ec-charger", + }, { + .name =3D "asus-ec-led", + .id =3D 1, + .of_compatible =3D "asus,ec-led", + }, { + .name =3D "asus-ec-keys", + .of_compatible =3D "asus,ec-keys", + }, { + .name =3D "asus-ec-kbc", + .of_compatible =3D "asus,ec-kbc", + }, +}; + +static const struct asus_ec_chip_data asus_ec_pad_data =3D { + .name =3D "pad", + .mfd_devices =3D asus_ec_pad_mfd_devices, + .num_devices =3D ARRAY_SIZE(asus_ec_pad_mfd_devices), +}; + +static const struct asus_ec_chip_data asus_ec_dock_data =3D { + .name =3D "dock", + .mfd_devices =3D asus_ec_dock_mfd_devices, + .num_devices =3D ARRAY_SIZE(asus_ec_dock_mfd_devices), +}; + +static const struct of_device_id asus_ec_match[] =3D { + { .compatible =3D "asus,ec-pad", .data =3D &asus_ec_pad_data }, + { .compatible =3D "asus,ec-dock", .data =3D &asus_ec_dock_data }, + { } +}; +MODULE_DEVICE_TABLE(of, asus_ec_match); + +static struct i2c_driver asus_ec_driver =3D { + .driver =3D { + .name =3D "asus-ec", + .of_match_table =3D asus_ec_match, + }, + .probe =3D asus_ec_probe, +}; +module_i2c_driver(asus_ec_driver); + +MODULE_AUTHOR("Micha=C5=82 Miros=C5=82aw "); +MODULE_AUTHOR("Svyatoslav Ryhel "); +MODULE_DESCRIPTION("ASUS Transformer's EC driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/asus-ec.h b/include/linux/mfd/asus-ec.h index bc4efa37f5ba..11b16295afcd 100644 --- a/include/linux/mfd/asus-ec.h +++ b/include/linux/mfd/asus-ec.h @@ -2,8 +2,76 @@ #ifndef __MISC_ASUS_EC_H #define __MISC_ASUS_EC_H =20 +#include +#include +#include + struct i2c_client; =20 +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(11) +#define ASUSEC_CMD_SUSPEND_S3 BIT_ULL(41) +#define ASUSEC_CTL_TEST_DISCHARGE BIT_ULL(43) +#define ASUSEC_CMD_SUSPEND_INHIBIT BIT_ULL(45) +#define ASUSEC_CTL_FACTORY_MODE BIT_ULL(46) +#define ASUSEC_CTL_KEEP_AWAKE BIT_ULL(47) +#define ASUSEC_CTL_USB_CHARGE BIT_ULL(50) +#define ASUSEC_CMD_SWITCH_HDMI BIT_ULL(70) +#define ASUSEC_CMD_WIN_SHUTDOWN BIT_ULL(76) + +#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 + /* 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= ); @@ -11,8 +79,35 @@ int asus_dockram_access_ctl(struct i2c_client *client, u64 *out, u64 mask, u64 xor); struct i2c_client *devm_asus_dockram_get(struct device *parent); =20 -#define DOCKRAM_ENTRIES 0x100 -#define DOCKRAM_ENTRY_SIZE 32 -#define DOCKRAM_ENTRY_BUFSIZE (DOCKRAM_ENTRY_SIZE + 1) +/* EC public API */ +static inline struct asusec_info *cell_to_ec(struct platform_device *pdev) +{ + return dev_get_drvdata(pdev->dev.parent); +} + +static inline int asus_ec_get_ctl(const struct asusec_info *ec, u64 *out) +{ + return asus_dockram_access_ctl(ec->dockram, out, 0, 0); +} + +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); +} + +static inline int asus_ec_set_ctl_bits(const struct asusec_info *ec, u64 m= ask) +{ + return asus_dockram_access_ctl(ec->dockram, NULL, mask, mask); +} + +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); =20 #endif /* __MISC_ASUS_EC_H */ --=20 2.51.0 From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wm1-f46.google.com (mail-wm1-f46.google.com [209.85.128.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 52A74328B53 for ; Sun, 1 Feb 2026 10:44:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942660; cv=none; b=S1TNKvTDQUbuY4hENaozp0a3SbfB9O00ltAq1v95ej6Dh24CmJEXN9cj0qVglrcoJgNfspWdmE75Z69mJB6L8A1l9Qqfl/QdY0lVEPfBv9Q6LOxXbY38guGf8VN5jZyCazJ7bfYWtfMU+lgY/SVC8ubfpQnU5yxOpjI1ot05fvc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942660; c=relaxed/simple; bh=UXzRbv2AJpOFvxhJbL04ahIUHG+2B8PBchoNwQoMDGo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=F+HI+Zn3uxZUynYxe+AnNL0Lg7iZGmIeIEIGXL9jZIkpJZoOQkitd1zRKgM7C+s2sqJx45x1LJQejbqLTZkz4uy8y5pgdG10ejwIuDXeODybEq+eiPNKos6Z9tGY3yMzBJRlSS+6NOnDcDrANmR3SplUDP4B1NNnzqxV8jH+Gf4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=LhHosggr; arc=none smtp.client-ip=209.85.128.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="LhHosggr" Received: by mail-wm1-f46.google.com with SMTP id 5b1f17b1804b1-48069a48629so36488945e9.0 for ; Sun, 01 Feb 2026 02:44:18 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942657; x=1770547457; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=wvGgSKvbVH8wh4V8HM8xwYvX9eYP7WNQfdBJNYHNlf0=; b=LhHosggrXTQaZfviUXFr8xJMLSJb4D3YS39IBK0WfTwlht1lBGICl0mE/GFhIrpIPK npxCf7gM1JEkA+HXj7VaBcXugAhBaTi0FAv71Jaz9agYbwWzlrZOwxPdqO6DzmtP7hnt 7wQBxsAPfB9EMh4mHskbkGsm3N/wRtP/d0uIglRzSzzgjSqWY18Xm3ZNr7Vu6eoAEpBZ 2+VKi66121khJ6Y9rjdcxPsP/tHyH5NSuXdi6XFGqWgo1FqnOMm4ZsJC40J/WT0h86p7 CDZfGS1MGCTaS1w5pQJqbnHPgwJuHdIIvweuTRun3P3N7Sss0XhfN4Nb3s/WFl4t9Gou 4K2w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942657; x=1770547457; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=wvGgSKvbVH8wh4V8HM8xwYvX9eYP7WNQfdBJNYHNlf0=; b=SRcBOhyqGQmc3KrDuBVhXgPqsh+DmZ1G4RK+cNvOPkjSHHxHhJNvn2dny4KpHKATR+ SDSzBeuLt/1f/fyUF7nTG/fTK+thjNqlkhOXgHaGWJ3XyF3VxDqzw0ZuTs84yFTJirn+ ZrP3J4hiOe7k2Five0TQiSEbOMaFDpd5XnkG2cSk9cPOqmcq9jH+4jZaT1Qe63C7KaJq HpfK2fylcFsKMmKO5wsf766XLvET1XG/boGvBbvfUznCWi4N8Q5mHlzv31ww5c1fTZ6E /P086OhoKCvhXA0evZLwyBM1WTpf5zpH2HwKhBfetdaSnVkr6yQR5NNde7uh30nmucOD 6BJQ== X-Forwarded-Encrypted: i=1; AJvYcCW/5UzwlX1c6b9AH3KrXqG4VzoTSbX2v5FPiGLHXJskQVT9sQa8Tkn1ukrvcBc7yKWawqmpOLWxXj1sEjo=@vger.kernel.org X-Gm-Message-State: AOJu0YxerU3xghozfG8KfQY4167d5ziK+6wbR1KWD+fsTfKPQ1K7sedu 8pJYLff0fYYjAbJ85gI1//oknZQiQ5y6WprQ6yuJZ11ZWSkj1DLspmg9 X-Gm-Gg: AZuq6aLrZx2LC3/9j+bGbBI2aTnDvB4dojNPtcEHNqKBEnSqoK7j3my+SqhKs4Oc/LS 48Bj3FI1tbAHICT34LpWwuyS2iJOSTvgmsjUXmdmoTDaoItkgsyFDPfMf84uQ1edNJpj15n3a7r oCSuuc36ecU3dWH/BJh1CF4ZEvBOdUK63QeOk3WVZd46BHist4EMu0kuwyKEwlzgAS/jh4Z8YU7 AqK0lJ6t6Tc6i84t+o8jcJNl044nCyi1dW/ryi06EZvhrBhOnsvkYZ7I9D1OBq+aYL2iZHB3CX+ vbOoejktB1qyQ76V9zPZgGLoli2+xom17gugrcgpPXP+i7Wf6OMtZqB+KwrVoBl1qCithMLFUZE ITDd/W8r1/M8vz7mv+DSu7Wyc0wRuJKVrU4ewg8HttFG7AUkz5Y7u4ZgfQzPkva7dc5PrqWC01+ j3 X-Received: by 2002:a05:600c:4f8f:b0:480:4c45:aff5 with SMTP id 5b1f17b1804b1-482db4995b1mr94284625e9.34.1769942656573; Sun, 01 Feb 2026 02:44:16 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:16 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 5/9] input: serio: Add driver for Asus Transformer dock keyboard and touchpad Date: Sun, 1 Feb 2026 12:43:39 +0200 Message-ID: <20260201104343.79231-6-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Micha=C5=82 Miros=C5=82aw 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 Signed-off-by: Ion Agorria Signed-off-by: Micha=C5=82 Miros=C5=82aw Signed-off-by: Svyatoslav Ryhel --- drivers/input/serio/Kconfig | 15 +++ drivers/input/serio/Makefile | 1 + drivers/input/serio/asus-ec-kbc.c | 162 ++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 drivers/input/serio/asus-ec-kbc.c diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index c7ef347a4dff..c4e17dcfc98a 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. =20 +config SERIO_ASUSEC + tristate "Asus Transformer's Dock keyboard and touchpad controller" + depends on MFD_ASUSEC + 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 asusec-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..444e3ea70e37 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_SERIO_CT82C710) +=3D ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) +=3D rpckbd.o obj-$(CONFIG_SERIO_SA1111) +=3D sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) +=3D ambakmi.o +obj-$(CONFIG_SERIO_ASUSEC) +=3D asus-ec-kbc.o obj-$(CONFIG_SERIO_Q40KBD) +=3D q40kbd.o obj-$(CONFIG_SERIO_GSCPS2) +=3D gscps2.o obj-$(CONFIG_HP_SDC) +=3D hp_sdc.o diff --git a/drivers/input/serio/asus-ec-kbc.c b/drivers/input/serio/asus-e= c-kbc.c new file mode 100644 index 000000000000..796ce0de38c8 --- /dev/null +++ b/drivers/input/serio/asus-ec-kbc.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ASUS EC - keyboard and touchpad + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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 =3D container_of(nb, struct asus_ec_kbc_dat= a, nb); + unsigned int port_idx, n; + u8 *data =3D data_; + + if (action & (ASUSEC_SMI_MASK | ASUSEC_SCI_MASK)) + return NOTIFY_DONE; + else if (action & ASUSEC_AUX_MASK) + port_idx =3D 1; + else if (action & (ASUSEC_KBC_MASK | ASUSEC_KEY_MASK)) + port_idx =3D 0; + else + return NOTIFY_DONE; + + n =3D data[0] - 1; + data +=3D 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 =3D=3D 0 && n >=3D 2 && data[0] =3D=3D 0xE0) { + if (n =3D=3D 3 && data[1] =3D=3D 0xF0) { + switch (data[2]) { + case 0x27: + data[0] =3D 0xF0; + data[1] =3D 0x11; + n =3D 2; + break; + case 0x2F: + data[2] =3D 0x27; + break; + } + } else if (n =3D=3D 2) { + switch (data[1]) { + case 0x27: + data[0] =3D 0x11; + n =3D 1; + break; + case 0x2F: + data[1] =3D 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 =3D 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 =3D platform_get_drvdata(pdev); + struct i2c_client *parent =3D to_i2c_client(pdev->dev.parent); + struct serio *port =3D kzalloc(sizeof(*port), GFP_KERNEL); + + if (!port) + dev_err_probe(&pdev->dev, -ENOMEM, + "No memory for serio%d\n", idx); + + priv->sdev[idx] =3D port; + port->dev.parent =3D &pdev->dev; + port->id.type =3D SERIO_8042; + port->id.extra =3D cmd & 0xFF; + port->write =3D asus_ec_serio_write; + port->port_data =3D (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 =3D cell_to_ec(pdev); + struct asus_ec_kbc_data *priv; + int ret; + + priv =3D devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + priv->ec =3D ec; + + ret =3D asus_ec_register_serio(pdev, 0, "Keyboard", 0); + if (ret < 0) + return ret; + + ret =3D asus_ec_register_serio(pdev, 1, "Touchpad", I8042_CMD_AUX_SEND); + if (ret < 0) + return ret; + + priv->nb.notifier_call =3D asus_ec_kbc_notify; + + return devm_asus_ec_register_notifier(pdev, &priv->nb); +} + +static const struct of_device_id asus_ec_kbc_match[] =3D { + { .compatible =3D "asus,ec-kbc" }, + { } +}; +MODULE_DEVICE_TABLE(of, asus_ec_kbc_match); + +static struct platform_driver asus_ec_kbc_driver =3D { + .driver =3D { + .name =3D "asus-ec-kbc", + .of_match_table =3D asus_ec_kbc_match, + }, + .probe =3D asus_ec_kbc_probe, +}; +module_platform_driver(asus_ec_kbc_driver); + +MODULE_AUTHOR("Micha=C5=82 Miros=C5=82aw "); +MODULE_DESCRIPTION("ASUS Transformer's Dock keyboard and touchpad controll= er driver"); +MODULE_LICENSE("GPL"); --=20 2.51.0 From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wm1-f49.google.com (mail-wm1-f49.google.com [209.85.128.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7B2D5329E5A for ; Sun, 1 Feb 2026 10:44:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942663; cv=none; b=e6fMdnGkPKl6PKZMa1I9v8QCYyZlTNqKgmnDGgFxrq8SOVjWungs9VOFQ1lw4rWTLkRDcXFgUABg3e5l0vU9p1tnMfbjas9WojE2Zn/ZfOPyTw8PwxS+q1RictN9Bs+kKdMWXmd+GMQiLhKp4cpXFOFqvA2pniEXLxuUcxT6VaU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942663; c=relaxed/simple; bh=3GFKhpWfEd4wx9op/cPLQRg9NIiBv/Np5fjkxtlpOAY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=mD0CppC8aufpzoZZZ7YDqAOqRhN577W/byDbZMjVthhJrqwXEKDePyrUX68fnNElqrUeT3Eyqcdstw8oJvECqGiih58oLUTPgssrbmo9X7D5gZ2VWCXIEaTNkcRZC1SA0tjMcqHzyngFH4pMbkWJxENd2gLg+zWYetm98oHyans= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Hc7KkNUu; arc=none smtp.client-ip=209.85.128.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Hc7KkNUu" Received: by mail-wm1-f49.google.com with SMTP id 5b1f17b1804b1-47ee3a63300so38153995e9.2 for ; Sun, 01 Feb 2026 02:44:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942658; x=1770547458; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=tSm3jMIbP4801iaRvLpNxizE8IF0rCGULRQu9fembFY=; b=Hc7KkNUu8AYpubs78AjdVzEU6ssuUBW/B58xhwNUUXYvxG0IAgnSaviGteRtCC4pGg tEJnBdIbeMqV2EcsYM8EDTF7ebzgZw7oiJDVAgtiCKEN1QNnqdWo/iD5YWPEoIUJI+qt ezaMty+iFXPa3nqywnmKB+EmtRPL4bN8jJntRm01BYhtuNb0X2IVr7T9gYw8R4zTpKYx RknDAMSUsYCM+Oz8h8WQuRMe1IkshHQT55fP9HPetaXabfFLJmf/vwGAB2jc64XhRjtz XwWBQwZEdyoQCewt8y1745OpVH1qmz+rFx0aZowRRWBJ43jNGKb//G0rQamdecZR3BjZ lnhQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942658; x=1770547458; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=tSm3jMIbP4801iaRvLpNxizE8IF0rCGULRQu9fembFY=; b=kAXmslIlNLwJH8EmMmfUh+g73TYv9Gf8zXoqvGWgwPH0Gnm3QyJeeBacNzgPwn61J6 F8AcP5bsZSdU9l7GAU0XbGFuNFwi98jBLT58/Lrdn+xxDE+BNwGYJWn0U1/6NwM7os6f MYw/SMiZIFf8oROHwWB+qiNCZumTomqCxg3koCaxubU6K3AgA/RrJRm/qGvt1hWkGsXa QzAnVD6z12+48ZC4rdvrolce8HBvdWVkj1S8yUttwSl+g2gxRTD00sW4aM5aSf3gB6jD 82bpgxCdf/oV6TMza6zsZTwf1fkSvo7awYobaOACfxBSMHYMZjsvXD3Xi2CiAS71tk0A sHwg== X-Forwarded-Encrypted: i=1; AJvYcCUu9/xLEfeit6zNqLFap2k8fLGXL3OYi89qEmF/6HoFVdCZXdP0FeA1vYiBbd25lPL743YdbmB9NyL9qYI=@vger.kernel.org X-Gm-Message-State: AOJu0YzDWwg5K4erW1Jjth9onbvf4+CR1VCww3bDQTiHPfJ61iJIkq6y h6teAyaT1FmWdsq3mKoUB0+cZoSlfeGXmWJHtI9sMnuQ5U1c5rgI4nzQ X-Gm-Gg: AZuq6aLFpIX3uy83pokSapE/dzZQndFpz2HnRqkkxLt+0KtjHzQDDwAhIQcpb4Zj2js C70vxVpYqLU86QETur9OVltciceQN3+SmcUoLlJdOej8PZUKUjfAgzX7awgQLR+lVsSvZo+XlEe sbM2TDqr9NfXBaNGcv6HIWdf2BDT200Tq3rP6FIfZRpGSV832Je4+U0cRt+5HwfQnBtpaaMPTc4 /QAd8B7fJ9Umv5yBpVlq2tyi9I5+xtnRbfi+LdMglyNzRfqHi92KWnb2QKUgz8KkemXYMoRbbdo ka1twVDRRlpzY3EhGgxAB8AUhneogeb8bLJz7RweldadrmsBS9E/EVlb7Gh/VflQ3peD/nWpjpZ ol+EX/TyDij9tKPIYHDak7yjZ6dabM7jOBu8USCRn1VyD1JYlUxu9UxClNRf2/K4e6ZQ7M47sIc 83 X-Received: by 2002:a05:600c:1f16:b0:47e:e712:aa88 with SMTP id 5b1f17b1804b1-482db4a0bb2mr124686615e9.31.1769942657686; Sun, 01 Feb 2026 02:44:17 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:17 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 6/9] input: keyboard: Add driver for Asus Transformer dock multimedia keys Date: Sun, 1 Feb 2026 12:43:40 +0200 Message-ID: <20260201104343.79231-7-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Micha=C5=82 Miros=C5=82aw 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 Signed-off-by: Ion Agorria Signed-off-by: Micha=C5=82 Miros=C5=82aw Signed-off-by: Svyatoslav Ryhel --- drivers/input/keyboard/Kconfig | 10 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/asus-ec-keys.c | 285 ++++++++++++++++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 drivers/input/keyboard/asus-ec-keys.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 2ff4fef322c2..9eef1ff279c3 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. =20 +config KEYBOARD_ASUSEC + tristate "Asus Transformer Mobile Dock multimedia keys" + depends on MFD_ASUSEC + 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 asusec-ext-keys. + config KEYBOARD_ATARI tristate "Atari keyboard" depends on ATARI diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makef= ile index 2d906e14f3e2..7226aafddf7a 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_ADP5585) +=3D adp5585-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) +=3D adp5588-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) +=3D amikbd.o obj-$(CONFIG_KEYBOARD_APPLESPI) +=3D applespi.o +obj-$(CONFIG_KEYBOARD_ASUSEC) +=3D asus-ec-keys.o obj-$(CONFIG_KEYBOARD_ATARI) +=3D atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) +=3D atkbd.o obj-$(CONFIG_KEYBOARD_BCM) +=3D bcm-keypad.o diff --git a/drivers/input/keyboard/asus-ec-keys.c b/drivers/input/keyboard= /asus-ec-keys.c new file mode 100644 index 000000000000..42365db63bdf --- /dev/null +++ b/drivers/input/keyboard/asus-ec-keys.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ASUS Transformer Pad - multimedia keys + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 =3D handle->handler->private; + + /* Store special key state */ + if (event_type =3D=3D EV_KEY && event_code =3D=3D KEY_RIGHTALT) + priv->special_key_pressed =3D !!value; +} + +static int asus_ec_input_connect(struct input_handler *handler, struct inp= ut_dev *dev, + const struct input_device_id *id) +{ + struct input_handle *handle; + int error; + + handle =3D kzalloc(sizeof(*handle), GFP_KERNEL); + if (!handle) + return -ENOMEM; + + handle->dev =3D dev; + handle->handler =3D handler; + handle->name =3D "asusec-media-handler"; + + error =3D input_register_handle(handle); + if (error) + goto err_free_handle; + + error =3D 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[] =3D { + { + .flags =3D INPUT_DEVICE_ID_MATCH_EVBIT, + .evbit =3D { BIT_MASK(EV_KEY) }, + }, + { } +}; + +static struct input_handler asus_ec_input_handler =3D { + .name =3D "asusec-media-handler", + .event =3D asus_ec_input_event, + .connect =3D asus_ec_input_connect, + .disconnect =3D asus_ec_input_disconnect, + .id_table =3D asus_ec_input_ids, +}; + +static const unsigned short asus_ec_dock_ext_keys[] =3D { + /* Function keys [0x00 - 0x19] */ + [0x01] =3D KEY_DELETE, + [0x02] =3D KEY_F1, + [0x03] =3D KEY_F2, + [0x04] =3D KEY_F3, + [0x05] =3D KEY_F4, + [0x06] =3D KEY_F5, + [0x07] =3D KEY_F6, + [0x08] =3D KEY_F7, + [0x10] =3D KEY_F8, + [0x11] =3D KEY_F9, + [0x12] =3D KEY_F10, + [0x13] =3D KEY_F11, + [0x14] =3D KEY_F12, + [0x15] =3D KEY_MUTE, + [0x16] =3D KEY_VOLUMEDOWN, + [0x17] =3D KEY_VOLUMEUP, + /* Multimedia keys [0x20 - 0x39] */ + [0x21] =3D KEY_SCREENLOCK, + [0x22] =3D KEY_WLAN, + [0x23] =3D KEY_BLUETOOTH, + [0x24] =3D KEY_TOUCHPAD_TOGGLE, + [0x25] =3D KEY_BRIGHTNESSDOWN, + [0x26] =3D KEY_BRIGHTNESSUP, + [0x27] =3D KEY_BRIGHTNESS_AUTO, + [0x28] =3D KEY_PRINT, + [0x30] =3D KEY_WWW, + [0x31] =3D KEY_CONFIG, + [0x32] =3D KEY_PREVIOUSSONG, + [0x33] =3D KEY_PLAYPAUSE, + [0x34] =3D KEY_NEXTSONG, + [0x35] =3D KEY_MUTE, + [0x36] =3D KEY_VOLUMEDOWN, + [0x37] =3D KEY_VOLUMEUP, +}; + +static void asus_ec_keys_report_key(struct input_dev *dev, unsigned int co= de, + 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 =3D dev_get_drvdata(dev->dev.parent); + unsigned int key =3D 0; + + if (code =3D=3D 0) + return NOTIFY_DONE; + + /* Flip special key mode state when pressing key 1 with special key press= ed */ + if (priv->special_key_pressed && code =3D=3D 1) { + priv->special_key_mode =3D !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 +=3D ASUSEC_EXT_KEY_CODES; + + if (code < dev->keycodemax) { + unsigned short *map =3D dev->keycode; + + key =3D map[code]; + } + + if (!key) + key =3D 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 =3D container_of(nb, struct asus_ec_keys_d= ata, nb); + u8 *data =3D 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 =3D priv->xidev; + unsigned int i; + + BUILD_BUG_ON(ARRAY_SIZE(priv->keymap) < ARRAY_SIZE(asus_ec_dock_ext_keys)= ); + + dev->keycode =3D priv->keymap; + dev->keycodesize =3D sizeof(*priv->keymap); + dev->keycodemax =3D ARRAY_SIZE(priv->keymap); + + input_set_capability(dev, EV_MSC, MSC_SCAN); + input_set_capability(dev, EV_KEY, KEY_UNKNOWN); + + for (i =3D 0; i < ARRAY_SIZE(asus_ec_dock_ext_keys); i++) { + unsigned int code =3D asus_ec_dock_ext_keys[i]; + + if (!code) + continue; + + __set_bit(code, dev->keybit); + priv->keymap[i] =3D 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 =3D cell_to_ec(pdev); + struct i2c_client *parent =3D to_i2c_client(pdev->dev.parent); + struct asus_ec_keys_data *priv; + int ret; + + priv =3D devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + priv->ec =3D ec; + + priv->xidev =3D devm_input_allocate_device(&pdev->dev); + if (!priv->xidev) + return -ENOMEM; + + priv->xidev->name =3D devm_kasprintf(&pdev->dev, GFP_KERNEL, + "%s Keyboard Ext", ec->model); + priv->xidev->phys =3D devm_kasprintf(&pdev->dev, GFP_KERNEL, + "i2c-%u-%04x", + i2c_adapter_id(parent->adapter), + parent->addr); + asus_ec_keys_setup_keymap(priv); + + ret =3D 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 =3D priv; + + ret =3D input_register_handler(&asus_ec_input_handler); + if (ret) + return ret; + + ret =3D devm_add_action_or_reset(&pdev->dev, asus_ec_input_handler_deregi= ster, + priv); + if (ret) + return ret; + + priv->nb.notifier_call =3D asus_ec_keys_notify; + + return devm_asus_ec_register_notifier(pdev, &priv->nb); +} + +static const struct of_device_id asus_ec_keys_match[] =3D { + { .compatible =3D "asus,ec-keys" }, + { } +}; +MODULE_DEVICE_TABLE(of, asus_ec_keys_match); + +static struct platform_driver asus_ec_keys_driver =3D { + .driver =3D { + .name =3D "asus-ec-keys", + .of_match_table =3D asus_ec_keys_match, + }, + .probe =3D asus_ec_keys_probe, +}; +module_platform_driver(asus_ec_keys_driver); + +MODULE_AUTHOR("Micha=C5=82 Miros=C5=82aw "); +MODULE_DESCRIPTION("ASUS Transformer's multimedia keys driver"); +MODULE_LICENSE("GPL"); --=20 2.51.0 From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wm1-f44.google.com (mail-wm1-f44.google.com [209.85.128.44]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7ECBD32AAB8 for ; Sun, 1 Feb 2026 10:44:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942662; cv=none; b=n9HqDvpnUEH09NZgqY+lYfte1d7eqNcxhVHYBz09npNX0MlvLO9yxzxHRXQPYaeH+ELh3XgyyoLExuJqnKVvtoCI2d5hFKy+8jaVrxi/CDfAG00c3hHl30XloVJz03jjnyCP7jgI381lzd4GB346/ZroFTXR8/QhbRXN7k350Pg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942662; c=relaxed/simple; bh=/R8TKTdaXJ5E7nFbRFXKGFI2CoDw82xuVjg3o42YEKM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=b9ptgeVSo7FsYF5vg7GYcYlPfXo/7xThncFHgmPy0PzgQrNLgHaEqIQfblXvlEiDs28S8oknw/E+KbJHaVLqq7FVuczGwaqd7i0QmOFU321C5WsR24HCOoq46igqZ99YEOjkDrCCdi5nId/jytY+NWsCVgn78Y7jNVv8P8S1QD8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=m5jbc3td; arc=none smtp.client-ip=209.85.128.44 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="m5jbc3td" Received: by mail-wm1-f44.google.com with SMTP id 5b1f17b1804b1-48039fdc8aeso20651455e9.3 for ; Sun, 01 Feb 2026 02:44:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942659; x=1770547459; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2qAcF+L0Np568uJdLN21hB/SuPE8/a7buuQ9U5NhjBM=; b=m5jbc3tddC+UMxdR//EwDty10PcdaIs6xWscrj9W0LvVBxE3GZI/3a4PE3gQKEaGVI SJ9nWCXJKx7zTayzOWEswBclArRHibchXeyBglGig7Zf7bm0OJ8O5vOAFo1Td2ERMTqj +TnSfSnRGGkMomAkJBGIPxbgQK6TU2JT74q3xDJYkr68b2tm+lDJMQk9vXepJJPEOvFZ p3BHWXTWdiVbEWvi00gUgz1J4/Xnb1z8YnIwU+Q7LZt42ZwIWz+QE5M4QIV45zN0es8E DYN5/74woiQCWFauiSq1WjkBMuuSfy059WR7RwGdv109IRL1wTBcnI75EGwlEToIUrrh hRvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942659; x=1770547459; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=2qAcF+L0Np568uJdLN21hB/SuPE8/a7buuQ9U5NhjBM=; b=mu9K2Z6aUv6OI/O6R8IkhrZKcV6g65Sd6OuihLg9xhx1Y01VttZllaEACJIT83ROND jAzU1tcq8OmEPLSUYGFKFRbbusYd84Luukm0Q3tFkF2n7J6ZXzemcJ3rvNGfvgSPyLmp gfX+Xz9KqCuvzZ9Jbeo5vXkwcN24iE9yy2A3u6MnHFaOHhf6P5Fuc7poZBbQPqhxceR/ BG+cT9gVK7jMqEXjqQjcjPkUSdWgtp5PxemRDOGlHIerumCmWQ09Pt+QoZGeLrJDs5zd /KWgBLL4vOQanSpO9217E82qACpMmYzoI7nKTytmz5IA0ApI+H6Hl41Ku4RS4mL9Ik/n 8LOw== X-Forwarded-Encrypted: i=1; AJvYcCU+5Uu1gewTlDM4GK51db/OOH0Blj7Bpm1NQJ68pIIQlLFA/Xto18d1QEF+8HewBsOjeiJ5ayrq0wkfKj8=@vger.kernel.org X-Gm-Message-State: AOJu0YyXZEQIDa85EQF4+EEDzfDYPG2OP9nkVL4aaf5LfxX0d5ErDvPS hcW/On9SASp6ljID51lQ6PAOL7KwSLIXCzWVAtnW3eYC/wPfSql/Tzc+ X-Gm-Gg: AZuq6aJSh+h8KMJvMSzRvW2UGVmuc+ZjBTYIc0b74StI8ybQRp3V2Haj/19HgBsPrIV Ue4/1o65yk1V3GPZmdZ6JpSLFh3lC8lzaQ9nD9dI47ZIHDIlTlv7S4F5K4FjzctW+e3ZHQvy3Vx XuUC00MrE+Gf760cu+wWjTQzA81sBDhyePfS9/V3QKzgnnO0FWnKgVaL/enmuClCoS1ovXreMcE GZWHG1TG2QKiHHbWUPRN/jdez67A1MDKmKLIsOCUVnF2WFA+subNM2hTg1/jzUUvUCgL8/fNawg iBOyGdPUB0Sl3Ruzi1XhIylYxLL1QPPhoB4npuIckOE/7mi01PUSDwLnxpsrqYUgMwvtZsW58qb iTV4S/F7bDxJlwTYjt7XL8xEVWkItgch5T1mFM1+PlxQqXzRv+IurWWd4v1IZN6ufgyNpfvdNDn yg X-Received: by 2002:a05:600c:3e83:b0:46e:4e6d:79f4 with SMTP id 5b1f17b1804b1-482db45dc48mr108393835e9.15.1769942658765; Sun, 01 Feb 2026 02:44:18 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:18 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 7/9] leds: Add driver for Asus Transformer LEDs Date: Sun, 1 Feb 2026 12:43:41 +0200 Message-ID: <20260201104343.79231-8-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Micha=C5=82 Miros=C5=82aw 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 Signed-off-by: Svyatoslav Ryhel Signed-off-by: Micha=C5=82 Miros=C5=82aw --- drivers/leds/Kconfig | 11 ++++ drivers/leds/Makefile | 1 + drivers/leds/leds-asus-ec.c | 106 ++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+) create mode 100644 drivers/leds/leds-asus-ec.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 597d7a79c988..96dab210f6ca 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. =20 +config LEDS_ASUSEC + tristate "LED Support for Asus Transformer charging LED" + depends on LEDS_CLASS + depends on MFD_ASUSEC + 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-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..1117304dfdf4 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -16,6 +16,7 @@ obj-$(CONFIG_LEDS_AN30259A) +=3D leds-an30259a.o obj-$(CONFIG_LEDS_APU) +=3D leds-apu.o obj-$(CONFIG_LEDS_ARIEL) +=3D leds-ariel.o obj-$(CONFIG_LEDS_AS3668) +=3D leds-as3668.o +obj-$(CONFIG_LEDS_ASUSEC) +=3D leds-asus-ec.o obj-$(CONFIG_LEDS_AW200XX) +=3D leds-aw200xx.o obj-$(CONFIG_LEDS_AW2013) +=3D leds-aw2013.o obj-$(CONFIG_LEDS_BCM6328) +=3D leds-bcm6328.o diff --git a/drivers/leds/leds-asus-ec.c b/drivers/leds/leds-asus-ec.c new file mode 100644 index 000000000000..2fae62ddb936 --- /dev/null +++ b/drivers/leds/leds-asus-ec.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ASUS EC driver - battery LED + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * F[5] & 0x07 + * auto: brightness =3D=3D 0 + * bit 0: blink / charger on + * bit 1: amber on + * bit 2: green on + */ + +#define ASUSEC_CTL_LED_BLINK BIT_ULL(40) +#define ASUSEC_CTL_LED_AMBER BIT_ULL(41) +#define ASUSEC_CTL_LED_GREEN BIT_ULL(42) + +static void asus_ec_led_set_brightness_amber(struct led_classdev *led, + enum led_brightness brightness) +{ + const struct asusec_info *ec =3D 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 =3D + 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 =3D cell_to_ec(pdev); + struct led_classdev *amber_led, *green_led; + int ret; + + platform_set_drvdata(pdev, ec); + + amber_led =3D devm_kzalloc(&pdev->dev, sizeof(*amber_led), GFP_KERNEL); + if (!amber_led) + return -ENOMEM; + + amber_led->name =3D devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s::amber", e= c->name); + amber_led->max_brightness =3D 1; + amber_led->flags =3D LED_CORE_SUSPENDRESUME | LED_RETAIN_AT_SHUTDOWN; + amber_led->brightness_set =3D asus_ec_led_set_brightness_amber; + + ret =3D devm_led_classdev_register(&pdev->dev, amber_led); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to register amber LED\n"); + + green_led =3D devm_kzalloc(&pdev->dev, sizeof(*green_led), GFP_KERNEL); + if (!green_led) + return -ENOMEM; + + green_led->name =3D devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s::green", e= c->name); + green_led->max_brightness =3D 1; + green_led->flags =3D LED_CORE_SUSPENDRESUME | LED_RETAIN_AT_SHUTDOWN; + green_led->brightness_set =3D asus_ec_led_set_brightness_green; + + ret =3D devm_led_classdev_register(&pdev->dev, green_led); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "failed to register green LED\n"); + + return 0; +} + +static const struct of_device_id asus_ec_led_match[] =3D { + { .compatible =3D "asus,ec-led" }, + { } +}; +MODULE_DEVICE_TABLE(of, asus_ec_led_match); + +static struct platform_driver asus_ec_led_driver =3D { + .driver =3D { + .name =3D "asus-ec-led", + .of_match_table =3D asus_ec_led_match, + }, + .probe =3D asus_ec_led_probe, +}; +module_platform_driver(asus_ec_led_driver); + +MODULE_AUTHOR("Micha=C5=82 Miros=C5=82aw "); +MODULE_AUTHOR("Svyatoslav Ryhel "); +MODULE_DESCRIPTION("ASUS Transformer's charging LED driver"); +MODULE_LICENSE("GPL"); --=20 2.51.0 From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wm1-f42.google.com (mail-wm1-f42.google.com [209.85.128.42]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 2A7F432C945 for ; Sun, 1 Feb 2026 10:44:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942665; cv=none; b=chfdMz/1StB3uOruwTxNdYw4WMhmM3MrqHrix1pobRhQRMHviC4vvZJmhoBTsk9Y9BIRdvpokmigDEggvyy1L3EfhXZ2hlV+mc6+kbdnhNWRGNDGUXYmlKrpnbP9bW3ksWfkgM2NahgcV8z86v9rOqNqLi8U8nccqCKWqy4YxW0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942665; c=relaxed/simple; bh=8NuHMq0VALACf4W7ccX2I+UZL6WHvoJZXHtgSaAu0K0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=TVgFlNM859pqMtS3vsZYv/XN/h6GusNgjlHj/QwKMMIO46khLVKFIlfIqLhjyG3v0wXx9AyCinVIIyKOReNAGb9GRrIUDqm/W8XKm3kAIep1zkuW+11/PA3A1DxxiJytvGJdBo9JobAQl6rqIx5GHLyQBq4BKf1g33bWG3SCqYI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=G+fZ9AN0; arc=none smtp.client-ip=209.85.128.42 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="G+fZ9AN0" Received: by mail-wm1-f42.google.com with SMTP id 5b1f17b1804b1-48068127f00so29610685e9.3 for ; Sun, 01 Feb 2026 02:44:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942660; x=1770547460; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=OnDBGTm1Ih+jnzybSzeIGvkZ/MyRumNvTLokxTWDPVg=; b=G+fZ9AN0JQy5nffughlfaMVLSFEvdeft5xmFZo4QZJVPjYmuxaehSuhxy+cOdxyzwI hu8nJTww5I1lnffyH55m3Sutbf2Vggbo0IWW64m+JpdhuzEx2bo7Rj0Hhdwze+LbQl6B pt9ROObbJuc31aKc6VXM1sL/zhcp0981PU8kK9gs+vgG+Mg6WDassD8c+NsYTsmNgH3Z mQt2LCGLJRDiiZEx1wRogXvvduBP5fxQX1HXOJn8e9az4bSXJmrN2gGKgxmGSrWCgsvQ qhVE64azD/Qb4/fWn6Q2Y3V8kDlLMwnGgp6JWttA4Fava5mWWfTqamtbE0SYUMnLVSlb kREA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942660; x=1770547460; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=OnDBGTm1Ih+jnzybSzeIGvkZ/MyRumNvTLokxTWDPVg=; b=eRaoPN/0EoDHRbaGLKRHfzQB8tLOgOjSd6PY6/oStDL1ztHeyhaVilwOa0qF1zGo1F khEvTIYxvE6/F2KAYaieRfimtqpr0i83+/nHqmadVwNR3gxbz80uUFgK06w8xlViQEtm JLlsm6+W24LkmVKTx2O2/OTN0dHQDbcfzrSgmJ9tX3SfS8YXLJiy95fK+q10nTXr1H8x H8HGibOoSibzPZ+hfa6on7Yq6eGMMwFIeGuFy+tpcxaBIBgCnmoqltkaKo6wBg7HdgxS UfqX5zM9OdW7qxEoSyPG7dOOHEW+qIaWnpEFP7sgtcSG7w3I7hMe57jVKNWOTyMKn+b1 4r0w== X-Forwarded-Encrypted: i=1; AJvYcCVyOEiMa0J4T1mYZmTBSgW2o0WY+YcI1baHFdPn1r1X7ZiXJobpkCQq2k8BX8o2OW4WgP1uOi5q2RHcoog=@vger.kernel.org X-Gm-Message-State: AOJu0YzxLai0h5Sj8btmYGoGVc0ZtAgp9qG9N10JljwMGVBSheBGoIVi slmcQBwyzgEP5XHwj1L7FZubIQuiVL4bpoKmP5CcuqaFKXpOYcsbSYBR X-Gm-Gg: AZuq6aIId0/nAHa1baGP0HBhvLyeQyUv2cggWUKMv6yrOiP/bFh0eG0niOtear8Wa1z UZYYUkf90ZzoJH7YeJCyQI0cr3m7iuNZbrWW2bM4e8PW1OHNk3jJLhsMEvVWkks4sDTVa1MEd8a hAPDkKawqV3yv6OiJ2KHk4JD/Dy0pIlDB/8fb7LLEVVHoRXA6ZV7y6Ib3JCIwEU2eK/B09tOker 3XXJ1jiIe/lg6TMJQFXAyHAgvARJz5eYlhCzrBHX5rL+J+ZP/U5dM9b43ATwEW78j/CWWKmUrIY GXyD/1hoV5NDCWe0UlKluaAA7G9YK/HavBbuoppMlrNt2HpR9MRzjSHky7768HLAnXBtZt4QCly 1NuFbOLDzhgH3v9DnuClIziqnUhyoiH5ZPZKKqFHrb+Vu9xbez51gviBFFlYaHrzWNMJTezbfoF Upk7EOJuaZ1/8= X-Received: by 2002:a05:600c:4e89:b0:477:9fcf:3fe3 with SMTP id 5b1f17b1804b1-482db213a37mr104561035e9.0.1769942660006; Sun, 01 Feb 2026 02:44:20 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:19 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 8/9] power: supply: Add driver for Asus Transformer battery Date: Sun, 1 Feb 2026 12:43:42 +0200 Message-ID: <20260201104343.79231-9-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Micha=C5=82 Miros=C5=82aw 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 Signed-off-by: Svyatoslav Ryhel Signed-off-by: Micha=C5=82 Miros=C5=82aw --- drivers/power/supply/Kconfig | 11 + drivers/power/supply/Makefile | 1 + drivers/power/supply/asus-ec-battery.c | 282 +++++++++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 drivers/power/supply/asus-ec-battery.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 81fadb0695a9..bcf6a23858be 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. =20 +config BATTERY_ASUSEC + tristate "Asus Transformer's battery driver" + depends on MFD_ASUSEC + 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..0a2cbfa96ed9 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_TEST_POWER) +=3D test_power.o obj-$(CONFIG_BATTERY_88PM860X) +=3D 88pm860x_battery.o obj-$(CONFIG_CHARGER_ADP5061) +=3D adp5061.o obj-$(CONFIG_BATTERY_ACT8945A) +=3D act8945a_charger.o +obj-$(CONFIG_BATTERY_ASUSEC) +=3D asus-ec-battery.o obj-$(CONFIG_BATTERY_AXP20X) +=3D axp20x_battery.o obj-$(CONFIG_CHARGER_AXP20X) +=3D axp20x_ac_power.o obj-$(CONFIG_BATTERY_CHAGALL) +=3D chagall-battery.o diff --git a/drivers/power/supply/asus-ec-battery.c b/drivers/power/supply/= asus-ec-battery.c new file mode 100644 index 000000000000..7e6cb6017339 --- /dev/null +++ b/drivers/power/supply/asus-ec-battery.c @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ASUS EC driver - battery monitoring + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 =3D 0; + + guard(mutex)(&priv->battery_lock); + + if (time_before(jiffies, priv->batt_data_ts)) + return ret; + + ret =3D asus_dockram_read(priv->ec->dockram, ASUSEC_DOCKRAM_BATT_CTL, + priv->batt_data); + if (ret < 0) + return ret; + + priv->batt_data_ts =3D jiffies + + msecs_to_jiffies(ASUSEC_BATTERY_DATA_FRESH_MSEC); + + return ret; +} + +static enum power_supply_property asus_ec_battery_properties[] =3D { + 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[] =3D { + [POWER_SUPPLY_PROP_STATUS] =3D 1, + [POWER_SUPPLY_PROP_VOLTAGE_MAX] =3D 3, + [POWER_SUPPLY_PROP_CURRENT_MAX] =3D 5, + [POWER_SUPPLY_PROP_TEMP] =3D 7, + [POWER_SUPPLY_PROP_VOLTAGE_NOW] =3D 9, + [POWER_SUPPLY_PROP_CURRENT_NOW] =3D 11, + [POWER_SUPPLY_PROP_CAPACITY] =3D 13, + [POWER_SUPPLY_PROP_CHARGE_NOW] =3D 15, + [POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW] =3D 17, + [POWER_SUPPLY_PROP_TIME_TO_FULL_NOW] =3D 19, +}; + +static int asus_ec_battery_get_value(struct asus_ec_battery_data *priv, + enum power_supply_property psp) +{ + int ret, offs; + + if (psp >=3D ARRAY_SIZE(asus_ec_battery_prop_offs)) + return -EINVAL; + + offs =3D asus_ec_battery_prop_offs[psp]; + if (!offs) + return -EINVAL; + + ret =3D asus_ec_battery_refresh(priv); + if (ret < 0) + return ret; + + if (offs >=3D 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 =3D power_supply_get_drvdata(psy); + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval =3D 1; + break; + + default: + ret =3D asus_ec_battery_get_value(priv, psp); + if (ret < 0) + return ret; + + val->intval =3D (s16)ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (ret & ASUSEC_BATTERY_FULL_CHARGED) + val->intval =3D POWER_SUPPLY_STATUS_FULL; + else if (ret & ASUSEC_BATTERY_NOT_CHARGING) + val->intval =3D POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (ret & ASUSEC_BATTERY_DISCHARGING) + val->intval =3D POWER_SUPPLY_STATUS_DISCHARGING; + else + val->intval =3D POWER_SUPPLY_STATUS_CHARGING; + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval -=3D 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 *=3D 1000; + break; + + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: + val->intval *=3D 60; + break; + + default: + break; + } + + break; + } + + return 0; +} + +static void asus_ec_battery_poll_work(struct work_struct *work) +{ + struct asus_ec_battery_data *priv =3D + container_of(work, struct asus_ec_battery_data, poll_work.work); + int state; + + state =3D asus_ec_battery_get_value(priv, POWER_SUPPLY_PROP_STATUS); + if (state < 0) + return; + + if (state & ASUSEC_BATTERY_FULL_CHARGED) + state =3D POWER_SUPPLY_STATUS_FULL; + else if (state & ASUSEC_BATTERY_DISCHARGING) + state =3D POWER_SUPPLY_STATUS_DISCHARGING; + else + state =3D POWER_SUPPLY_STATUS_CHARGING; + + if (priv->last_state !=3D state) { + priv->last_state =3D 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 =3D { + .name =3D "asus-ec-battery", + .type =3D POWER_SUPPLY_TYPE_BATTERY, + .properties =3D asus_ec_battery_properties, + .num_properties =3D ARRAY_SIZE(asus_ec_battery_properties), + .get_property =3D asus_ec_battery_get_property, + .external_power_changed =3D power_supply_changed, +}; + +static int asus_ec_battery_probe(struct platform_device *pdev) +{ + struct asus_ec_battery_data *priv; + struct power_supply_config cfg =3D { }; + int ret; + + priv =3D devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + mutex_init(&priv->battery_lock); + + priv->ec =3D cell_to_ec(pdev); + priv->batt_data_ts =3D jiffies - 1; + priv->last_state =3D POWER_SUPPLY_STATUS_UNKNOWN; + + cfg.fwnode =3D dev_fwnode(&pdev->dev); + cfg.drv_data =3D priv; + + memcpy(&priv->psy_desc, &asus_ec_battery_desc, sizeof(priv->psy_desc)); + priv->psy_desc.name =3D devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-batter= y", + priv->ec->name); + + priv->battery =3D devm_power_supply_register(&pdev->dev, &priv->psy_desc,= &cfg); + if (IS_ERR(priv->battery)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->battery), + "Failed to register power supply\n"); + + ret =3D devm_delayed_work_autocancel(&pdev->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 =3D 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 =3D 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 const struct of_device_id asus_ec_battery_match[] =3D { + { .compatible =3D "asus,ec-battery" }, + { } +}; +MODULE_DEVICE_TABLE(of, asus_ec_battery_match); + +static struct platform_driver asus_ec_battery_driver =3D { + .driver =3D { + .name =3D "asus-ec-battery", + .of_match_table =3D asus_ec_battery_match, + .pm =3D &asus_ec_battery_pm_ops, + }, + .probe =3D asus_ec_battery_probe, +}; +module_platform_driver(asus_ec_battery_driver); + +MODULE_AUTHOR("Micha=C5=82 Miros=C5=82aw "); +MODULE_AUTHOR("Svyatoslav Ryhel "); +MODULE_DESCRIPTION("ASUS Transformer's battery driver"); +MODULE_LICENSE("GPL"); --=20 2.51.0 From nobody Sun Feb 8 01:51:50 2026 Received: from mail-wm1-f49.google.com (mail-wm1-f49.google.com [209.85.128.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EB82532E138 for ; Sun, 1 Feb 2026 10:44:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942665; cv=none; b=LLKvveEL/YVG4X5v/KnZm/og3ttZjTBBsaFdlMrQsf9uSkqCWVBdqlBzbDwXmfmirzhOfR4UbDeraARaWatFlhOlTVYIlNOrqdWeR0XsxoMht8zr0ndem76B3mtjy9ZxLZu2VFTl2wRSJUJIxWCwE1tup6G539Or4v73eNxnKl0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1769942665; c=relaxed/simple; bh=PlyybFsZsqNmpXRB8/13kR5LEafXRlgj3o35Wb5kkYc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Sc1Y4kEyiVnwWrlqnozeFCwTAsBKAqc0mZlveZspR/7R25ToYU0bczAgLWAQVkqW8eLZA9AZDvtcYgBd4b1CyWl0mcRbQIMRgatYBKIsOXEn/x7jKDZsfk5TydAbxGRBKBwcN3dTFXl1KK7ufTSUXf4NXcZWo75KH8+D5f4D5fQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=jWihEKQ2; arc=none smtp.client-ip=209.85.128.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="jWihEKQ2" Received: by mail-wm1-f49.google.com with SMTP id 5b1f17b1804b1-480706554beso38338325e9.1 for ; Sun, 01 Feb 2026 02:44:22 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1769942661; x=1770547461; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=RPFQoidqkErVeAGy+/qpK+osZp6nImPKthWl2DazuOo=; b=jWihEKQ2bsASXKl0aiLsW57Pptyzf4nhzmR4/zqv1XuoodLOaIWYviYFxRzkV/pbvs XuAWUr9jqFw8IS3zU049ug6U39O2OmzMVKoJulVvJT/+IBkgCSKRwPkcrViVr0R2/4Rn CLRPl/VhvcQaSLe3rd8r1EYZL0OVmcciGJYpQbgwhha8T8oDKMepnjC5zWRxnFR0ITcP WyvK1bOtDDl+xictmmN5m+d7V5XhIfkfSWNFFs9pYJcFHd/EHlO57H0Fg1rKEYwND1kT /RI+SWv1MEhjFO+r9MDp4uASac7+Cj7QO8WS6lfVr7tj2Rqi9PO8zq8ERkWONSj6QJ5s WIcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1769942661; x=1770547461; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=RPFQoidqkErVeAGy+/qpK+osZp6nImPKthWl2DazuOo=; b=gTx0nUMApaIEUfD6hLBraR7pv/Mur8FHZq5AysFQ2mkzUQG6tZOHerits4p6zwoCgW o/zkuqjlU8gehAsuYZNBQGrpk9YAJEzDv9/PC5IOO28b+levj9cnNdmV937U6Tk/euQj mhCCp9DVvkRbiP3Og0Aloo6P4eyxmTjfW0xm36BBGvFt7I8/AKu7wBLCfGmhYxaDA0OY xyWMDFnlIokLkTKJLlK4YNFnNYR1WYjErCb4RQXxsyLCRX+n6xJdqBtiLASAAhAn51kJ Ft1KKophQvtPqUqA/5fmW8siy+9QTNPuzmlqXkFIGZh64gctQHLDJZvgbHJBD29jjmhQ yICg== X-Forwarded-Encrypted: i=1; AJvYcCX+grQLWszupCuSjZd3Ofg9hdx5cPouILmLK4v+uF3pCvj2ZSPdmAkyq59mpED4XVegwquEVhOxvaJdD5Y=@vger.kernel.org X-Gm-Message-State: AOJu0YwfvB/hauAmfSB7gzDgWJqGwLTSSste7VYq7UkZCzh7hfRw8feE ZS6p2SIzkBkTKvQEJjQ+KvOblS0rEg02x9cHSq3j6e4RkZyPI4l21Zka X-Gm-Gg: AZuq6aIWdtSCF9ftztAiin0HBv3ZVYjkVfSqJCDocQX8r9g7z7iHM2Bs/Y7X0fwNd1L hbiFBgiP4iV3T+FSUmkA4/9ge05EiZOzvPmAxZxrkr1YwhjikUmoxc5YlscZdZwuWpo22636/Gz mNhphXSG+38a6Ok071rs8qqVjragmkYjnCUBQatrEzxWspMQO9DSrW1LprRTUHHfcgr+FYrQQNR SuL0U6lA7kNRAXP4AXqdNbJpaZdE8ILRNzPV9yTtb8AiaEf0cO1vGkYOIWP2EAJFTZQu/mJ8Lqj 5TASbsk7llvpGrvDnRDkk5RsaUPoXMg/vHS9i0XIVs8qGj/yoiE3jSzF+o6YfX/2neRQt4BEP76 lsRC4UQGGWvAB/CdNoP3aA7UDxavLXUAnMp4TcTAG0+ZCWoKXM/f7NELXzB01uTW9jo7JVUX6my By X-Received: by 2002:a05:600c:3552:b0:47e:e970:cf28 with SMTP id 5b1f17b1804b1-482db4a3834mr93508735e9.30.1769942661102; Sun, 01 Feb 2026 02:44:21 -0800 (PST) Received: from xeon ([188.163.112.49]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-4806ce56490sm308947455e9.12.2026.02.01.02.44.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 01 Feb 2026 02:44:20 -0800 (PST) From: Svyatoslav Ryhel To: Lee Jones , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Dmitry Torokhov , Pavel Machek , Arnd Bergmann , Greg Kroah-Hartman , Sebastian Reichel , Svyatoslav Ryhel , =?UTF-8?q?Micha=C5=82=20Miros=C5=82aw?= , Ion Agorria Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-input@vger.kernel.org, linux-leds@vger.kernel.org, linux-pm@vger.kernel.org Subject: [PATCH v1 9/9] power: supply: Add charger driver for Asus Transformers Date: Sun, 1 Feb 2026 12:43:43 +0200 Message-ID: <20260201104343.79231-10-clamor95@gmail.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20260201104343.79231-1-clamor95@gmail.com> References: <20260201104343.79231-1-clamor95@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable From: Micha=C5=82 Miros=C5=82aw Add support for charger detection capabilities found in the embedded controller of ASUS Transformer devices. Suggested-by: Maxim Schwalm Suggested-by: Svyatoslav Ryhel Signed-off-by: Micha=C5=82 Miros=C5=82aw Signed-off-by: Svyatoslav Ryhel --- drivers/power/supply/Kconfig | 11 ++ drivers/power/supply/Makefile | 1 + drivers/power/supply/asus-ec-charger.c | 205 +++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 drivers/power/supply/asus-ec-charger.c diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index bcf6a23858be..6d7e115f5d2f 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -133,6 +133,17 @@ config BATTERY_ASUSEC tablets and mobile docks and controlled by special embedded controller. =20 +config CHARGER_ASUSEC + tristate "Asus Transformer's charger driver" + depends on MFD_ASUSEC + 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 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 0a2cbfa96ed9..b93848227f50 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_BATTERY_88PM860X) +=3D 88pm860x_battery.o obj-$(CONFIG_CHARGER_ADP5061) +=3D adp5061.o obj-$(CONFIG_BATTERY_ACT8945A) +=3D act8945a_charger.o obj-$(CONFIG_BATTERY_ASUSEC) +=3D asus-ec-battery.o +obj-$(CONFIG_CHARGER_ASUSEC) +=3D asus-ec-charger.o obj-$(CONFIG_BATTERY_AXP20X) +=3D axp20x_battery.o obj-$(CONFIG_CHARGER_AXP20X) +=3D axp20x_ac_power.o obj-$(CONFIG_BATTERY_CHAGALL) +=3D chagall-battery.o diff --git a/drivers/power/supply/asus-ec-charger.c b/drivers/power/supply/= asus-ec-charger.c new file mode 100644 index 000000000000..9d98e5014915 --- /dev/null +++ b/drivers/power/supply/asus-ec-charger.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ASUS EC driver - charger monitoring + */ + +#include +#include +#include +#include +#include +#include +#include + +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[] =3D { + 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 =3D power_supply_get_drvdata(psy); + enum power_supply_usb_type psu; + int ret; + u64 ctl; + + ret =3D asus_ec_get_ctl(priv->ec, &ctl); + if (ret) + return ret; + + switch (ctl & (ASUSEC_CTL_FULL_POWER_SOURCE | ASUSEC_CTL_DIRECT_POWER_SOU= RCE)) { + case ASUSEC_CTL_FULL_POWER_SOURCE: + psu =3D POWER_SUPPLY_USB_TYPE_CDP; // DOCK + break; + case ASUSEC_CTL_DIRECT_POWER_SOURCE: + psu =3D POWER_SUPPLY_USB_TYPE_SDP; // USB + break; + case 0: + psu =3D POWER_SUPPLY_USB_TYPE_UNKNOWN; // no power source connected + break; + default: + psu =3D POWER_SUPPLY_USB_TYPE_ACA; // power adapter + break; + } + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval =3D psu !=3D POWER_SUPPLY_USB_TYPE_UNKNOWN; + return 0; + + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval =3D psu; + return 0; + + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + if (ctl & ASUSEC_CTL_TEST_DISCHARGE) + val->intval =3D POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE; + else if (ctl & ASUSEC_CTL_USB_CHARGE) + val->intval =3D POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO; + else + val->intval =3D POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE; + return 0; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval =3D 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 =3D 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 =3D { + .name =3D "asus-ec-charger", + .type =3D POWER_SUPPLY_TYPE_USB, + .charge_behaviours =3D BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE) | + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE), + .usb_types =3D 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 =3D asus_ec_charger_properties, + .num_properties =3D ARRAY_SIZE(asus_ec_charger_properties), + .get_property =3D asus_ec_charger_get_property, + .set_property =3D asus_ec_charger_set_property, + .property_is_writeable =3D asus_ec_charger_property_is_writeable, + .no_thermal =3D true, +}; + +static int asus_ec_charger_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct asus_ec_charger_data *priv =3D + 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 power_supply_config cfg =3D { }; + + priv =3D devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + priv->ec =3D cell_to_ec(pdev); + + cfg.fwnode =3D dev_fwnode(&pdev->dev); + cfg.drv_data =3D priv; + + memcpy(&priv->psy_desc, &asus_ec_charger_desc, sizeof(priv->psy_desc)); + priv->psy_desc.name =3D devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-charge= r", + priv->ec->name); + + priv->psy =3D devm_power_supply_register(&pdev->dev, &priv->psy_desc, &cf= g); + if (IS_ERR(priv->psy)) + return dev_err_probe(&pdev->dev, PTR_ERR(priv->psy), + "Failed to register power supply\n"); + + priv->nb.notifier_call =3D asus_ec_charger_notify; + + return devm_asus_ec_register_notifier(pdev, &priv->nb); +} + +static const struct of_device_id asus_ec_charger_match[] =3D { + { .compatible =3D "asus,ec-charger" }, + { } +}; +MODULE_DEVICE_TABLE(of, asus_ec_charger_match); + +static struct platform_driver asus_ec_charger_driver =3D { + .driver =3D { + .name =3D "asus-ec-charger", + .of_match_table =3D asus_ec_charger_match, + }, + .probe =3D asus_ec_charger_probe, +}; +module_platform_driver(asus_ec_charger_driver); + +MODULE_AUTHOR("Micha=C5=82 Miros=C5=82aw "); +MODULE_DESCRIPTION("ASUS Transformer Pad battery charger driver"); +MODULE_LICENSE("GPL"); --=20 2.51.0