* Re: [PATCH v3 1/2] dt-bindings: usb: Add ITE IT885x support
2026-07-09 19:25 ` [PATCH v3 1/2] dt-bindings: usb: Add ITE IT885x support Amber Kao
@ 2026-07-09 12:14 ` Krzysztof Kozlowski
0 siblings, 0 replies; 4+ messages in thread
From: Krzysztof Kozlowski @ 2026-07-09 12:14 UTC (permalink / raw)
To: Amber Kao, Jeson Yang, Yaode Fang, Bling Chiang, Doreen Lin,
Eric Su, Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heikki Krogerus
Cc: linux-usb, devicetree, linux-kernel
On 09/07/2026 21:25, Amber Kao wrote:
> Add device tree binding documentation for the ITE IT885x. The ITE
> IT885x is an I2C-based USB Type-C Power Delivery (PD) controller.
>
> v2 -> v3: No changes, resent alongside the driver patch which needed
> another revision.
>
> v1 -> v2:
> - Per Conor/Sashiko: dropped the redundant `gpios` property, the
> interrupt line is already described via `interrupts`
> - Per Conor: renamed example node from `itepd@40` to `typec@40`
None of these go to commit msgm but changelog (---).
Please fix your timezone, so you do not send email from the future (7
hours in the future). It messes with mailboxes.
You also keep sending way too often this patchset. Please read
submitting patches.
>
> Cc: Yaode Fang <Yaode.Fang@ite.com.tw>
> Cc: Jeson Yang <jeson.yang@ite.com.tw>
> Cc: Bling Chiang <Bling.Chiang@ite.com.tw>
> Cc: Eric Su <Eric.Su@ite.com.tw>
> Cc: Doreen Lin <doreen.lin@ite.com.tw>
> Signed-off-by: Amber Kao <amber.kao@ite.com.tw>
...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 461a3eed6129..1b03fa3aa060 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -13720,6 +13720,17 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
> F: Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
> F: drivers/gpu/drm/bridge/ite-it66121.c
>
> +ITE IT885x TYPE-C PD CONTROLLER DRIVER
> +M: Jeson Yang <jeson.yang@ite.com.tw>
> +M: Amber Kao <amber.kao@ite.com.tw>
> +R: Yaode Fang <Yaode.Fang@ite.com.tw>
> +R: Bling Chiang <Bling.Chiang@ite.com.tw>
> +R: Doreen Lin <doreen.lin@ite.com.tw>
> +R: Eric Su <Eric.Su@ite.com.tw>
Are all these people going to provide reviews?
> +L: linux-usb@vger.kernel.org
> +S: Maintained
> +F: Documentation/devicetree/bindings/usb/ite,itepd-it885x.yaml
> +
> IVTV VIDEO4LINUX DRIVER
> M: Andy Walls <awalls@md.metrocast.net>
> L: linux-media@vger.kernel.org
>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 0/2] usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver
@ 2026-07-09 19:25 Amber Kao
2026-07-09 19:25 ` [PATCH v3 1/2] dt-bindings: usb: Add ITE IT885x support Amber Kao
2026-07-09 19:25 ` [PATCH v3 2/2] usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver Amber Kao
0 siblings, 2 replies; 4+ messages in thread
From: Amber Kao @ 2026-07-09 19:25 UTC (permalink / raw)
To: Jeson Yang, Yaode Fang, Bling Chiang, Doreen Lin, Eric Su,
Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heikki Krogerus
Cc: linux-usb, devicetree, linux-kernel, Amber Kao
This series adds Device Tree bindings and minimal UCSI driver
support for the ITE IT885x USB Type-C Power Delivery controller
over I2C.
Per Heikki's review of v1, this series has been split into smaller,
self-contained patches. This version drops the Alternate Mode
support, the UCSI command-translation hook, and the auxiliary-bus
core/client split entirely. The driver now does nothing more than
register the UCSI ports and partners. Alternate Mode support and
the auxiliary-bus split will follow in later series, once each
feature can be reviewed on its own.
v2 also had two high-severity issues found by Sashiko review,
both fixed in this version: a use-after-free race in remove() where
free_irq() ran after ucsi_unregister() had already freed the
ucsi->connector array a pending IRQ could still access, and a missing
check on the interrupt-clear write that could unconditionally return
IRQ_HANDLED even when the hardware interrupt was never actually cleared,
risking an IRQ storm.
Note: This driver has not been tested on physical hardware. Runtime
verification was performed by building the module out-of-tree
against the currently running kernel's headers and loading it on an
x86_64 virtual machine, using i2c-stub with manual device
instantiation to trigger probe().
Testing performed:
- checkpatch.pl --strict: no errors or warnings
- dt_binding_check / dtbs_check: no errors or warnings
- Sparse (C=2 CF="-D__CHECK_ENDIAN__"): no errors or warnings
- checkstack.pl: no functions exceed 512-byte stack limit on
x86_64, arm64, arm32, and ppc64le
- Kconfig tristate: tested =m, =y, =n, allmodconfig, allnoconfig
- Cross-compilation: x86_64, arm64, arm32, ppc64le
- CONFIG_SMP and CONFIG_PREEMPT: both on and off
- Strict warning mode (W=1 EXTRA_CFLAGS=-W): no new warnings
- i2c-stub emulation with manual device instantiation: re-verified
after the v3 fixes,probe() still correctly rejects devices with
no IRQ resource (-ENODEV), no crash, module load/unload cycle clean
- Fault injection (CONFIG_FAILSLAB, fail-nth): each reachable kzalloc()/
devm_kzalloc() in probe() was individually failed and returned -ENOMEM
cleanly with correct teardown; the runtime IRQ/UCSI paths were not
reachable via i2c-stub.
Note: big-endian (ppc64) cross-compilation was not tested, as
TYPEC_UCSI currently depends on !CPU_BIG_ENDIAN upstream. Byte-order
correctness was instead verified via sparse.
Signed-off-by: Amber Kao <amber.kao@ite.com.tw>
---
Amber Kao (2):
dt-bindings: usb: Add ITE IT885x support
usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver
.../devicetree/bindings/usb/ite,itepd-it885x.yaml | 105 ++++++
MAINTAINERS | 12 +
drivers/usb/typec/ucsi/Kconfig | 10 +
drivers/usb/typec/ucsi/Makefile | 1 +
drivers/usb/typec/ucsi/ucsi_itepd.c | 354 +++++++++++++++++++++
5 files changed, 482 insertions(+)
---
base-commit: 8fde5d1d47f69db6082dfa34500c27f8485389a5
change-id: 20260605-ucsi-itepd-feature-95e6dcee4fc5
Best regards,
--
Amber Kao <amber.kao@ite.com.tw>
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 1/2] dt-bindings: usb: Add ITE IT885x support
2026-07-09 19:25 [PATCH v3 0/2] usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver Amber Kao
@ 2026-07-09 19:25 ` Amber Kao
2026-07-09 12:14 ` Krzysztof Kozlowski
2026-07-09 19:25 ` [PATCH v3 2/2] usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver Amber Kao
1 sibling, 1 reply; 4+ messages in thread
From: Amber Kao @ 2026-07-09 19:25 UTC (permalink / raw)
To: Jeson Yang, Yaode Fang, Bling Chiang, Doreen Lin, Eric Su,
Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heikki Krogerus
Cc: linux-usb, devicetree, linux-kernel, Amber Kao
Add device tree binding documentation for the ITE IT885x. The ITE
IT885x is an I2C-based USB Type-C Power Delivery (PD) controller.
v2 -> v3: No changes, resent alongside the driver patch which needed
another revision.
v1 -> v2:
- Per Conor/Sashiko: dropped the redundant `gpios` property, the
interrupt line is already described via `interrupts`
- Per Conor: renamed example node from `itepd@40` to `typec@40`
Cc: Yaode Fang <Yaode.Fang@ite.com.tw>
Cc: Jeson Yang <jeson.yang@ite.com.tw>
Cc: Bling Chiang <Bling.Chiang@ite.com.tw>
Cc: Eric Su <Eric.Su@ite.com.tw>
Cc: Doreen Lin <doreen.lin@ite.com.tw>
Signed-off-by: Amber Kao <amber.kao@ite.com.tw>
---
v1: https://lore.kernel.org/all/20260615-ucsi-itepd-feature-v1-0-a826cfd0df6a@ite.com.tw/
---
.../devicetree/bindings/usb/ite,itepd-it885x.yaml | 105 +++++++++++++++++++++
MAINTAINERS | 11 +++
2 files changed, 116 insertions(+)
diff --git a/Documentation/devicetree/bindings/usb/ite,itepd-it885x.yaml b/Documentation/devicetree/bindings/usb/ite,itepd-it885x.yaml
new file mode 100644
index 000000000000..0a292a8e72cd
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/ite,itepd-it885x.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/ite,itepd-it885x.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ITE IT885x USB Type-C Power Delivery Controller
+
+maintainers:
+ - Jeson Yang <jeson.yang@ite.com.tw>
+
+description:
+ The ITE IT885x is an I2C-based USB Type-C Power Delivery (PD) controller.
+
+properties:
+ compatible:
+ const: ite,itepd-it885x
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ wakeup-source: true
+
+ pinctrl-names:
+ minItems: 1
+
+ pinctrl-0: true
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+patternProperties:
+ "^connector(@[0-9a-f]+)?$":
+ $ref: /schemas/connector/usb-connector.yaml#
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ typec@40 {
+ compatible = "ite,itepd-it885x";
+ reg = <0x40>;
+ interrupts-extended = <&tlmm 129 IRQ_TYPE_EDGE_FALLING>;
+ wakeup-source;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&itepd_int_default>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ connector@0 {
+ compatible = "usb-c-connector";
+ reg = <0>;
+ label = "USB-C";
+ power-role = "dual";
+ data-role = "dual";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ endpoint {
+ remote-endpoint = <&eud_con>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ endpoint {
+ remote-endpoint = <&redriver_ss_out>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ endpoint {
+ remote-endpoint = <&fsa4480_sbu_mux>;
+ };
+ };
+ };
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 461a3eed6129..1b03fa3aa060 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13720,6 +13720,17 @@ T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: Documentation/devicetree/bindings/display/bridge/ite,it66121.yaml
F: drivers/gpu/drm/bridge/ite-it66121.c
+ITE IT885x TYPE-C PD CONTROLLER DRIVER
+M: Jeson Yang <jeson.yang@ite.com.tw>
+M: Amber Kao <amber.kao@ite.com.tw>
+R: Yaode Fang <Yaode.Fang@ite.com.tw>
+R: Bling Chiang <Bling.Chiang@ite.com.tw>
+R: Doreen Lin <doreen.lin@ite.com.tw>
+R: Eric Su <Eric.Su@ite.com.tw>
+L: linux-usb@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/usb/ite,itepd-it885x.yaml
+
IVTV VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
L: linux-media@vger.kernel.org
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v3 2/2] usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver
2026-07-09 19:25 [PATCH v3 0/2] usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver Amber Kao
2026-07-09 19:25 ` [PATCH v3 1/2] dt-bindings: usb: Add ITE IT885x support Amber Kao
@ 2026-07-09 19:25 ` Amber Kao
1 sibling, 0 replies; 4+ messages in thread
From: Amber Kao @ 2026-07-09 19:25 UTC (permalink / raw)
To: Jeson Yang, Yaode Fang, Bling Chiang, Doreen Lin, Eric Su,
Greg Kroah-Hartman, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heikki Krogerus
Cc: linux-usb, devicetree, linux-kernel, Amber Kao
Add core UCSI support for the ITE IT885x USB Type-C Power Delivery
controller over I2C.
Per Heikki's review of v1, this series has been split into smaller
patches. This patch provides the bare minimum: register the UCSI
ports and partners only, with no command translation and no
Alternate Mode support. The command-translation hook and Alternate
Mode child device from v1 have been dropped entirely for this patch,
and will be reintroduced one feature at a time in follow-up series.
v2 -> v3:
- Per Sashiko: fixed teardown ordering in remove() -- free_irq()
now runs before ucsi_unregister(), since ucsi_unregister() frees
the ucsi->connector array that a still-pending threaded IRQ could
access via ucsi_notify_common(), causing a use-after-free
- Per Sashiko: itepd_irq_process() no longer unconditionally returns
IRQ_HANDLED when clearing the interrupt fails; it now returns
IRQ_NONE so the kernel's spurious-interrupt detection can engage,
instead of risking an IRQ storm under memory/I2C pressure
v1 -> v2:
- Per Heikki: split into a minimal, single-file patch
(ucsi_itepd.c only); the itepd.c/itepd.h core, the auxiliary-bus
child-device split, and itepd_altmode.c are dropped from this
patch and deferred to a follow-up series
- Per Heikki: removed the UCSI command-translation hook
(ucsi_itepd_command_hook()) entirely for this minimal patch
- Use heap-allocated (kzalloc) I2C buffers instead of stack memory,
since i2c_transfer() buffers must be DMA-safe
- Add explicit little-endian conversions for all multi-byte
register fields
- Require a valid IRQ at probe time
- Fix probe()/remove() ordering so the IRQ thread can never observe
a freed or not-yet-created ucsi instance
- Add an i2c_device_id table alongside of_device_id
- Dropped the AUXILIARY_BUS/DRM Kconfig dependencies, no longer
needed without the altmode client
Cc: Yaode Fang <Yaode.Fang@ite.com.tw>
Cc: Jeson Yang <jeson.yang@ite.com.tw>
Cc: Bling Chiang <Bling.Chiang@ite.com.tw>
Cc: Eric Su <Eric.Su@ite.com.tw>
Cc: Doreen Lin <doreen.lin@ite.com.tw>
Signed-off-by: Amber Kao <amber.kao@ite.com.tw>
---
v2: https://lore.kernel.org/all/20260710-ucsi-itepd-feature-v2-0-41943fd5df38@ite.com.tw/
v1: https://lore.kernel.org/all/20260615-ucsi-itepd-feature-v1-0-a826cfd0df6a@ite.com.tw/
---
MAINTAINERS | 1 +
drivers/usb/typec/ucsi/Kconfig | 10 +
drivers/usb/typec/ucsi/Makefile | 1 +
drivers/usb/typec/ucsi/ucsi_itepd.c | 354 ++++++++++++++++++++++++++++++++++++
4 files changed, 366 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 1b03fa3aa060..22c0f386b25a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13730,6 +13730,7 @@ R: Eric Su <Eric.Su@ite.com.tw>
L: linux-usb@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/usb/ite,itepd-it885x.yaml
+F: drivers/usb/typec/ucsi/ucsi_itepd.c
IVTV VIDEO4LINUX DRIVER
M: Andy Walls <awalls@md.metrocast.net>
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index 87dd992a4b9e..28442d2aebf8 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -104,4 +104,14 @@ config UCSI_HUAWEI_GAOKUN
To compile the driver as a module, choose M here: the module will be
called ucsi_huawei_gaokun.
+config UCSI_ITEPD
+ tristate "UCSI Interface Driver for ITE IT885x"
+ depends on I2C
+ help
+ This driver enables UCSI support on platforms that expose an
+ ITE IT885x Type-C Power Delivery controller over I2C interface
+
+ To compile the driver as a module, choose M here: the module
+ will be called ucsi_itepd.
+
endif
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index c7e38bf01350..0903a1fd486b 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -28,3 +28,4 @@ obj-$(CONFIG_UCSI_PMIC_GLINK) += ucsi_glink.o
obj-$(CONFIG_CROS_EC_UCSI) += cros_ec_ucsi.o
obj-$(CONFIG_UCSI_LENOVO_YOGA_C630) += ucsi_yoga_c630.o
obj-$(CONFIG_UCSI_HUAWEI_GAOKUN) += ucsi_huawei_gaokun.o
+obj-$(CONFIG_UCSI_ITEPD) += ucsi_itepd.o
diff --git a/drivers/usb/typec/ucsi/ucsi_itepd.c b/drivers/usb/typec/ucsi/ucsi_itepd.c
new file mode 100644
index 000000000000..539818869ab7
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_itepd.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025-2026, ITE. All Rights Reserved
+ */
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/unaligned.h>
+
+#include "ucsi.h"
+
+#define ITEPD_UCSI_VERSION_REG 0x80
+#define ITEPD_UCSI_CCI_REG 0x84
+#define ITEPD_UCSI_MSG_IN_REG 0x88
+#define ITEPD_UCSI_CONTROL_REG 0x98
+
+#define ITEPD_VENDOR_WC_INT 0xbc
+#define ITEPD_VENDOR_INT 0xbd
+#define ITEPD_ALERT_VDM_EVENT BIT(0)
+#define ITEPD_ALERT_UCSI_EVENT BIT(1)
+
+#define ITEPD_MSG_IN_MAX_LEN 0x28
+
+struct itepd {
+ struct i2c_client *client;
+ struct ucsi *ucsi;
+ struct mutex i2c_lock; /* Serializes I2C accesses */
+ struct mutex received_lock; /* Protects cci and msg_in */
+ u8 msg_in[ITEPD_MSG_IN_MAX_LEN];
+ u32 cci;
+};
+
+static u8 ucsi_itepd_get_len(u32 cci)
+{
+ if (cci & UCSI_CCI_COMMAND_COMPLETE)
+ return UCSI_CCI_LENGTH(cci);
+ return 0;
+}
+
+static int itepd_read_reg(struct itepd *itepd, u8 reg, void *data, u32 len)
+{
+ struct i2c_client *client = itepd->client;
+ struct i2c_msg msg[2];
+ u8 *buf;
+ int ret;
+
+ /* I2C buffers must be DMA-safe, so no stack memory here. */
+ buf = kzalloc(len + 1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = reg;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = buf;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = len;
+ msg[1].buf = buf + 1;
+
+ mutex_lock(&itepd->i2c_lock);
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ mutex_unlock(&itepd->i2c_lock);
+ if (ret < 0) {
+ dev_err(&client->dev, "reg 0x%02x read failed: %d\n", reg, ret);
+ goto out_free;
+ }
+ if (ret != ARRAY_SIZE(msg)) {
+ ret = -EIO;
+ goto out_free;
+ }
+
+ memcpy(data, buf + 1, len);
+ ret = 0;
+
+out_free:
+ kfree(buf);
+ return ret;
+}
+
+static int itepd_write_reg(struct itepd *itepd, u8 reg, const void *data, u32 len)
+{
+ struct i2c_client *client = itepd->client;
+ struct i2c_msg msg[1];
+ u8 *buf;
+ int ret;
+
+ buf = kzalloc(len + 1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = reg;
+ memcpy(buf + 1, data, len);
+
+ msg[0].addr = client->addr;
+ msg[0].flags = 0;
+ msg[0].len = len + 1;
+ msg[0].buf = buf;
+
+ mutex_lock(&itepd->i2c_lock);
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ mutex_unlock(&itepd->i2c_lock);
+
+ if (ret < 0) {
+ dev_err(&client->dev, "reg 0x%02x write failed: %d\n", reg, ret);
+ goto out_free;
+ }
+
+ if (ret != ARRAY_SIZE(msg)) {
+ ret = -EIO;
+ goto out_free;
+ }
+
+ ret = 0;
+
+out_free:
+ kfree(buf);
+ return ret;
+}
+
+static int ucsi_itepd_read_version(struct ucsi *ucsi, u16 *version)
+{
+ struct itepd *itepd = ucsi_get_drvdata(ucsi);
+ __le16 le_version;
+ int ret;
+
+ ret = itepd_read_reg(itepd, ITEPD_UCSI_VERSION_REG, &le_version,
+ sizeof(le_version));
+
+ if (ret)
+ return ret;
+
+ *version = le16_to_cpu(le_version);
+
+ return 0;
+}
+
+static int ucsi_itepd_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ struct itepd *itepd = ucsi_get_drvdata(ucsi);
+
+ mutex_lock(&itepd->received_lock);
+ *cci = itepd->cci;
+ mutex_unlock(&itepd->received_lock);
+
+ return 0;
+}
+
+static int ucsi_itepd_poll_cci(struct ucsi *ucsi, u32 *cci)
+{
+ struct itepd *itepd = ucsi_get_drvdata(ucsi);
+ __le32 le_cci;
+ int ret;
+
+ ret = itepd_read_reg(itepd, ITEPD_UCSI_CCI_REG, &le_cci,
+ sizeof(le_cci));
+
+ if (ret)
+ return ret;
+
+ *cci = le32_to_cpu(le_cci);
+
+ return 0;
+}
+
+static int ucsi_itepd_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+{
+ struct itepd *itepd = ucsi_get_drvdata(ucsi);
+
+ mutex_lock(&itepd->received_lock);
+ memcpy(val, itepd->msg_in, min(val_len, sizeof(itepd->msg_in)));
+ mutex_unlock(&itepd->received_lock);
+
+ return 0;
+}
+
+static int ucsi_itepd_async_control(struct ucsi *ucsi, u64 command)
+{
+ struct itepd *itepd = ucsi_get_drvdata(ucsi);
+ __le64 le_cmd = cpu_to_le64(command);
+
+ return itepd_write_reg(itepd, ITEPD_UCSI_CONTROL_REG, &le_cmd,
+ sizeof(le_cmd));
+}
+
+static const struct ucsi_operations ucsi_itepd_ops = {
+ .read_version = ucsi_itepd_read_version,
+ .read_cci = ucsi_itepd_read_cci,
+ .poll_cci = ucsi_itepd_poll_cci,
+ .read_message_in = ucsi_itepd_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_itepd_async_control,
+};
+
+static irqreturn_t itepd_irq_process(struct itepd *itepd)
+{
+ u8 msg_in[ITEPD_MSG_IN_MAX_LEN] = {};
+ __le32 le_cci;
+ u32 cci = 0;
+ u8 event;
+ u8 len;
+ int ret;
+ int clear_ret;
+
+ ret = itepd_read_reg(itepd, ITEPD_VENDOR_INT, &event, sizeof(event));
+
+ if (ret)
+ return IRQ_NONE;
+
+ event &= ITEPD_ALERT_VDM_EVENT | ITEPD_ALERT_UCSI_EVENT;
+
+ if (!event)
+ return IRQ_NONE;
+
+ if (event & ITEPD_ALERT_UCSI_EVENT) {
+ ret = itepd_read_reg(itepd, ITEPD_UCSI_CCI_REG, &le_cci, sizeof(le_cci));
+ if (ret)
+ goto out_clear;
+
+ cci = le32_to_cpu(le_cci);
+ len = min_t(u8, ucsi_itepd_get_len(cci), sizeof(msg_in));
+
+ if (len) {
+ ret = itepd_read_reg(itepd, ITEPD_UCSI_MSG_IN_REG,
+ msg_in, len);
+ if (ret)
+ goto out_clear;
+ }
+
+ mutex_lock(&itepd->received_lock);
+ itepd->cci = cci;
+ memcpy(itepd->msg_in, msg_in, sizeof(msg_in));
+ mutex_unlock(&itepd->received_lock);
+ }
+
+out_clear:
+ clear_ret = itepd_write_reg(itepd, ITEPD_VENDOR_WC_INT, &event, sizeof(event));
+ if (clear_ret) {
+ dev_err_ratelimited(&itepd->client->dev,
+ "failed to clear interrupt: %d\n", clear_ret);
+ return IRQ_NONE;
+ }
+
+ if (!ret && (event & ITEPD_ALERT_UCSI_EVENT))
+ ucsi_notify_common(itepd->ucsi, cci);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t itepd_irq_thread_fn(int irq, void *data)
+{
+ struct itepd *itepd = data;
+
+ return itepd_irq_process(itepd);
+}
+
+static int itepd_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct itepd *itepd;
+ u8 event;
+ int ret;
+
+ if (client->irq <= 0)
+ return dev_err_probe(dev, -ENODEV, "no IRQ provided\n");
+
+ itepd = devm_kzalloc(dev, sizeof(*itepd), GFP_KERNEL);
+
+ if (!itepd)
+ return -ENOMEM;
+
+ itepd->client = client;
+ mutex_init(&itepd->i2c_lock);
+ mutex_init(&itepd->received_lock);
+ i2c_set_clientdata(client, itepd);
+
+ itepd->ucsi = ucsi_create(dev, &ucsi_itepd_ops);
+
+ if (IS_ERR(itepd->ucsi))
+ return PTR_ERR(itepd->ucsi);
+
+ ucsi_set_drvdata(itepd->ucsi, itepd);
+
+ event = ITEPD_ALERT_VDM_EVENT | ITEPD_ALERT_UCSI_EVENT;
+ ret = itepd_write_reg(itepd, ITEPD_VENDOR_WC_INT, &event, sizeof(event));
+
+ if (ret)
+ goto out_ucsi_destroy;
+
+ ret = request_threaded_irq(client->irq, NULL, itepd_irq_thread_fn, IRQF_ONESHOT,
+ dev_name(dev), itepd);
+
+ if (ret) {
+ dev_err(dev, "request_threaded_irq failed - %d\n", ret);
+ goto out_ucsi_destroy;
+ }
+
+ ret = ucsi_register(itepd->ucsi);
+ if (ret) {
+ dev_err(dev, "failed to register UCSI: %d\n", ret);
+ goto out_free_irq;
+ }
+
+ return 0;
+
+out_free_irq:
+ free_irq(client->irq, itepd);
+out_ucsi_destroy:
+ ucsi_destroy(itepd->ucsi);
+ return ret;
+}
+
+static void itepd_remove(struct i2c_client *client)
+{
+ struct itepd *itepd = i2c_get_clientdata(client);
+
+ free_irq(client->irq, itepd);
+ ucsi_unregister(itepd->ucsi);
+ ucsi_destroy(itepd->ucsi);
+}
+
+static const struct of_device_id itepd_of_match_table[] = {
+ { .compatible = "ite,itepd-it885x" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, itepd_of_match_table);
+
+static const struct i2c_device_id itepd_id_table[] = {
+ { "ucsi_itepd", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, itepd_id_table);
+
+static struct i2c_driver itepd_driver = {
+ .driver = {
+ .name = "ucsi_itepd",
+ .of_match_table = itepd_of_match_table,
+ },
+ .probe = itepd_probe,
+ .remove = itepd_remove,
+ .id_table = itepd_id_table,
+};
+module_i2c_driver(itepd_driver);
+
+MODULE_AUTHOR("Jeson Yang <jeson.yang@ite.com.tw>");
+MODULE_DESCRIPTION("UCSI driver for ITE IT885x Type-C PD controllers");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-07-09 12:14 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-09 19:25 [PATCH v3 0/2] usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver Amber Kao
2026-07-09 19:25 ` [PATCH v3 1/2] dt-bindings: usb: Add ITE IT885x support Amber Kao
2026-07-09 12:14 ` Krzysztof Kozlowski
2026-07-09 19:25 ` [PATCH v3 2/2] usb: typec: ucsi: Add ITE IT885x Type-C PD controller driver Amber Kao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox