From nobody Fri Sep 25 14:32:27 2026 Received: from ironport.ite.com.tw (60-251-196-230.hinet-ip.hinet.net [60.251.196.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 27ABE28505E; Fri, 11 Sep 2026 10:18:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.251.196.230 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789121916; cv=none; b=tEqn0jptYLrU9FxmG7HwrLcqvsZyL3hlK/IoDU+5K97IaFzz172FQwU7CM+4Fkyqh+U5f8RlBEanNIIaFbPWKK8arW5a4Dkv/o6g8CqkDO089yNjSFGAF3r/X1RTtJJQcFvcw0PmSlO2s/QrzNkLjxpzqbZiWCqyi0Aa3+qL8aY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789121916; c=relaxed/simple; bh=EZFgjzmIkXKtBiVBR5kDiTZvhm0BWlSah60eYrztaZc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=N7NRa7Sl54IDRJYPoBlbslmV3IM7XqeAP4ZSxB5L24k/L2OCx32Y3IEmtfoPfLME8004BgUOkZElqNE293zTgk/UZ15XISN0z24a1roLkhARPrihEAokyRzAYKAs0btA3UZT1ZrCtGNsVz6KCG9NAiDGNeFaA2bdNo48P+02ko0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ite.com.tw; spf=pass smtp.mailfrom=ite.com.tw; dkim=pass (2048-bit key) header.d=ite.com.tw header.i=@ite.com.tw header.b=Fqwlyw3A; arc=none smtp.client-ip=60.251.196.230 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ite.com.tw Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ite.com.tw Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ite.com.tw header.i=@ite.com.tw header.b="Fqwlyw3A" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ite.com.tw; s=dkim; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=oHvNl5rUJ3iJJdtBUPTK1GIxlFixSOtnW5yXY3TOxLk=; b=Fqwlyw3AwS1lZ5qS1YO/T2QU4of37Q2jEWB3YBDLxBzlNXOhhOrfxHJ5 nGhTWEmkmJpjaRT7vSF4GrV40y4/gsbpyvRvWZXv7+uKlhrlJzuQRpy4y 2pCouBCRuHsxQHxvtFTF1Swr/gKqktqFU9AVLngbQYTqvMg0vzuJ+wnb9 dt4DXdO/M+TIK9QSGZ3RM1JltZLrBH2umDKeSOPpLdDvnLkDx2QdKhQ6B YY+JibqemAxUubpmXR1262m/tNgot/qXHz8zDMWk0LF66dIXEDetUM9y1 vnqipsvLQ2XNaottEBxPYMpG1CC+j0ruQKtogCfGy4bIw1eIlqHp2vsyr Q==; X-CSE-ConnectionGUID: 7drFANLVSYGdQwkq9b8wyw== X-CSE-MsgGUID: mCOKXzzqQf+SLLc0IAe7GQ== Received: from unknown (HELO mse.ite.com.tw) ([192.168.35.30]) by ironport.ite.com.tw with ESMTP; 11 Sep 2026 18:17:20 +0800 Received: from jiraproxy.ite.com.tw (jira.internal.ite.com.tw [192.168.35.200]) by mse.ite.com.tw with ESMTP id 68BAHGM7037146; Fri, 11 Sep 2026 18:17:16 +0800 (+08) (envelope-from ite_pd@ite.com.tw) Received: from [127.0.1.1] (ITE01791W10.internal.ite.com.tw [192.168.37.107]) by jiraproxy.ite.com.tw (Postfix) with ESMTP id 7C62E2D59; Fri, 11 Sep 2026 18:17:16 +0800 (CST) From: Amber Kao Date: Fri, 11 Sep 2026 18:17:57 +0800 Subject: [PATCH 1/2] dt-bindings: usb: ite,it8851: Add IT5271 series support 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 Message-Id: <20260911-ucsi-itepd-it527x-v1-1-14c8041afb53@ite.com.tw> References: <20260911-ucsi-itepd-it527x-v1-0-14c8041afb53@ite.com.tw> In-Reply-To: <20260911-ucsi-itepd-it527x-v1-0-14c8041afb53@ite.com.tw> To: Amber Kao , Jeson Yang , Yaode Fang , Greg Kroah-Hartman , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heikki Krogerus Cc: linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Amber Kao X-Mailer: b4 0.15.2 X-MAIL: mse.ite.com.tw 68BAHGM7037146 Add compatible strings for the ITE IT5271 series USB Type-C Power Delivery controllers. The IT5272 and IT5274 are the two-port variants, and the IT5273 and IT5274 additionally support EPR (Extended Power Range). All of them share the IT5271 programming interface. Signed-off-by: Amber Kao --- .../devicetree/bindings/usb/ite,it8851.yaml | 72 ++++++++++++++++++= +--- 1 file changed, 65 insertions(+), 7 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/ite,it8851.yaml b/Docume= ntation/devicetree/bindings/usb/ite,it8851.yaml index defd431aba97..e2e50507f573 100644 --- a/Documentation/devicetree/bindings/usb/ite,it8851.yaml +++ b/Documentation/devicetree/bindings/usb/ite,it8851.yaml @@ -4,23 +4,33 @@ $id: http://devicetree.org/schemas/usb/ite,it8851.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# =20 -title: ITE IT8851/IT8853 USB Type-C Power Delivery Controller +title: ITE USB Type-C Power Delivery Controllers =20 maintainers: - Jeson Yang - - Amber Kao + description: - The ITE IT8851 is an I2C-based USB Type-C Power Delivery (PD) controller - supporting one Type-C port. - The IT8853 is the two-port variant sharing the same programming interfac= e. + I2C-based USB Type-C Power Delivery (PD) controllers sharing the same + programming interface. The IT8853, IT5272 and IT5274 drive two Type-C + connectors, the other variants drive one. =20 properties: compatible: oneOf: - - const: ite,it8851 + - enum: + - ite,it5271 + - ite,it8851 + - items: + - enum: + - ite,it5272 + - ite,it5273 + - ite,it5274 + - const: ite,it5271 - items: - const: ite,it8853 - const: ite,it8851 + description: + IT5273 and IT5274 support EPR (Extended Power Range). =20 reg: maxItems: 1 @@ -63,7 +73,10 @@ allOf: properties: compatible: contains: - const: ite,it8853 + enum: + - ite,it5272 + - ite,it5274 + - ite,it8853 then: properties: connector@1: false @@ -154,3 +167,48 @@ examples: }; }; }; + + - | + #include + + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + typec@26 { + compatible =3D "ite,it5271"; + reg =3D <0x26>; + interrupts-extended =3D <&tlmm 130 IRQ_TYPE_EDGE_FALLING>; + wakeup-source; + + #address-cells =3D <1>; + #size-cells =3D <0>; + + connector@0 { + compatible =3D "usb-c-connector"; + reg =3D <0>; + label =3D "USB-C"; + power-role =3D "dual"; + data-role =3D "dual"; + + ports { + #address-cells =3D <1>; + #size-cells =3D <0>; + + port@0 { + reg =3D <0>; + endpoint { + remote-endpoint =3D <&usb_hs_con>; + }; + }; + + port@1 { + reg =3D <1>; + endpoint { + remote-endpoint =3D <&usb_ss_con>; + }; + }; + }; + }; + }; + }; --=20 2.53.0 From nobody Fri Sep 25 14:32:27 2026 Received: from ironport.ite.com.tw (60-251-196-230.hinet-ip.hinet.net [60.251.196.230]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9A6DA426697; Fri, 11 Sep 2026 10:18:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=60.251.196.230 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789121920; cv=none; b=WUaIwEFI12vgHVZHYzr3p54U++8/H/xoIbgZCBLAcRw5A7pkizSH4NJO0VCRDOxiWWFjOUgvRox46j9EEAayFpbH562tNVEUVoArWGOIReP3MHXhneWzqaEkGJJCBTDMtSU2uMOBXEfZrvfp6MZz0eQxlPGyBb7brVUSEo3cq0k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789121920; c=relaxed/simple; bh=zEN+Tlxtey5v+y9a+kQZs5q9CFva50G1DDtURICKYSM=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=lzN71uIGrPzAjv/nz6gIiQLpOTiR8+gBegY4fxxrNgcaI3ey+h6W5hOyNX/zR1eMiLRBeEQ37IKnd4GhaYLDCktIDG5YSkSmqF2kG4dfpYNwIIr0Ww6pIlkw9sMbrGIU+JnllvAP32qw0NZzcJPe9sFtVexk7S19YEH1kYGdBJc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ite.com.tw; spf=pass smtp.mailfrom=ite.com.tw; dkim=pass (2048-bit key) header.d=ite.com.tw header.i=@ite.com.tw header.b=gBc+E6zr; arc=none smtp.client-ip=60.251.196.230 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=ite.com.tw Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=ite.com.tw Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=ite.com.tw header.i=@ite.com.tw header.b="gBc+E6zr" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ite.com.tw; s=dkim; h=from:date:subject:mime-version:content-transfer-encoding: message-id:references:in-reply-to:to:cc; bh=vqwU2csTHS0N+MHmJUX0kCytmGs4GMAAcZapWYnZC9g=; b=gBc+E6zrGcpDnnEmXx0JOEnwRFmH5ZmhhoVDtTKV4FLgm9vIWksO0xWc bo9sJONMFRWaF8ZxvhGx7NnK2wSYGZZg2t1xJkxSd29uQQDoIijbAPaRU 0IZG+FBmv7T/Cv4VcGoPlG3I548sgrcfudJkbn+FqxCrij1uGDOPXHuoM LtUCEOLHoRK3NAsLG+29vX+uMD1gW1fp+l5rBp5vdhKqZk254YId7iHsG MocjybjQN197sW5NGqrm2xZpy7DRLLzyIOqW8YyY5Ce/a2X8ER2MQc9cy +PkZkMbeILUZZAIzefzQe92/78lrptAB+KzGNg/R5ji6ABCxv1tBXZzai w==; X-CSE-ConnectionGUID: XmHgJjstSS6xEvtVYcmDiQ== X-CSE-MsgGUID: iooRoSlPR0GawMfz2hFYwg== Received: from unknown (HELO mse.ite.com.tw) ([192.168.35.30]) by ironport.ite.com.tw with ESMTP; 11 Sep 2026 18:17:21 +0800 Received: from jiraproxy.ite.com.tw (jira.internal.ite.com.tw [192.168.35.200]) by mse.ite.com.tw with ESMTP id 68BAHGws037147; Fri, 11 Sep 2026 18:17:16 +0800 (+08) (envelope-from ite_pd@ite.com.tw) Received: from [127.0.1.1] (ITE01791W10.internal.ite.com.tw [192.168.37.107]) by jiraproxy.ite.com.tw (Postfix) with ESMTP id 7EE6D2D5C; Fri, 11 Sep 2026 18:17:16 +0800 (CST) From: Amber Kao Date: Fri, 11 Sep 2026 18:17:58 +0800 Subject: [PATCH 2/2] usb: typec: ucsi: itepd: Add DisplayPort alternate mode support 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 Message-Id: <20260911-ucsi-itepd-it527x-v1-2-14c8041afb53@ite.com.tw> References: <20260911-ucsi-itepd-it527x-v1-0-14c8041afb53@ite.com.tw> In-Reply-To: <20260911-ucsi-itepd-it527x-v1-0-14c8041afb53@ite.com.tw> To: Amber Kao , Jeson Yang , Yaode Fang , Greg Kroah-Hartman , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Heikki Krogerus Cc: linux-usb@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Amber Kao X-Mailer: b4 0.15.2 X-MAIL: mse.ite.com.tw 68BAHGws037147 Add support for the ITE IT5271 series controllers and for DisplayPort alternate mode. The driver retrieves the current alternate mode from the PPM, configures the Type-C mux and retimer accordingly, and reports HPD state and HPD IRQ events through the DRM auxiliary HPD bridge. Signed-off-by: Amber Kao --- drivers/usb/typec/ucsi/Kconfig | 10 +- drivers/usb/typec/ucsi/ucsi_itepd.c | 405 ++++++++++++++++++++++++++++++++= +++- 2 files changed, 402 insertions(+), 13 deletions(-) diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index 0f10dac9b34b..309ee82a166c 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -105,13 +105,17 @@ config UCSI_HUAWEI_GAOKUN called ucsi_huawei_gaokun. =20 config UCSI_ITEPD - tristate "UCSI Interface Driver for ITE IT8851/IT8853" + tristate "UCSI Interface Driver for ITE IT885x and IT527x series" depends on I2C + depends on DRM || !DRM + select DRM_AUX_HPD_BRIDGE if DRM_BRIDGE && OF help This driver enables UCSI support on platforms that expose an - ITE IT8851/IT8853 Type-C Power Delivery controller over an I2C interfac= e. + ITE IT8851 or IT5271 series Type-C Power Delivery controller + over an I2C interface. It also supports DisplayPort alternate + mode, including HPD notification through the DRM bridge. =20 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/ucsi_itepd.c b/drivers/usb/typec/ucsi/u= csi_itepd.c index 50a9eae656ab..8f7f40677767 100644 --- a/drivers/usb/typec/ucsi/ucsi_itepd.c +++ b/drivers/usb/typec/ucsi/ucsi_itepd.c @@ -3,14 +3,24 @@ * Copyright (C) 2025-2026, ITE. All Rights Reserved * */ +#include +#include #include #include #include #include +#include #include #include +#include +#include #include #include +#include +#include +#include + +#include =20 #include "ucsi.h" =20 @@ -29,6 +39,35 @@ #define ITEPD_EVENT_NONE 0 #define ITEPD_EVENT_UCSI 1 #define ITEPD_EVENT_VDM 2 +#define ITEPD_EVENT_OTHER 3 + +#define ITEPD_CONSTAT_ORIENTATION_OFFSET 86 + +#define ITEPD_CAM_CS_INDEX(_i_) ((u64)(_i_) << 24) +#define ITEPD_CAM_CS_LEN 10 +#define ITEPD_CAM_CS_STATUS_OFFSET 1 +#define ITEPD_CAM_CS_CONF_OFFSET 6 + +#define ITEPD_CAM_NONE 0xff + +#define ITEPD_ATTENTION_VDO_LEN 11 +#define ITEPD_ATTENTION_NUM_VDOS_OFFSET 2 +#define ITEPD_ATTENTION_NUM_VDOS_MASK GENMASK(2, 0) +#define ITEPD_ATTENTION_VDO_OFFSET 7 + +struct itepd; + +struct itepd_port { + struct itepd *itepd; + unsigned int index; + struct typec_mux *typec_mux; + struct typec_retimer *typec_retimer; + struct auxiliary_device *bridge; + struct typec_mux_state state; + struct typec_altmode dp_alt; + unsigned long mode; + bool hpd_state; +}; =20 struct itepd { struct i2c_client *client; @@ -38,6 +77,9 @@ struct itepd { struct mutex received_lock; /* Protects cci and msg_in */ u8 msg_in[ITEPD_MSG_IN_MAX_LEN]; u32 cci; + + struct itepd_port *ports; + unsigned int num_ports; }; =20 static u8 ucsi_itepd_get_len(u32 cci) @@ -115,25 +157,26 @@ static int itepd_process_event(struct itepd *itepd, u= 32 *cci) { u8 msg_in[ITEPD_MSG_IN_MAX_LEN] =3D {}; __le32 le_cci; - u8 event, ack; + u8 raw, event, ack; u8 len =3D 0; int err =3D 0; int ret; =20 guard(mutex)(&itepd->event_lock); =20 - ret =3D itepd_read_reg(itepd, ITEPD_VENDOR_INT, &event, sizeof(event)); + ret =3D itepd_read_reg(itepd, ITEPD_VENDOR_INT, &raw, sizeof(raw)); if (ret) return ret; =20 - event &=3D ITEPD_ALERT_VDM_EVENT | ITEPD_ALERT_UCSI_EVENT; - if (!event) { + if (!raw) { mutex_lock(&itepd->received_lock); *cci =3D itepd->cci; mutex_unlock(&itepd->received_lock); return ITEPD_EVENT_NONE; } =20 + event =3D raw & (ITEPD_ALERT_VDM_EVENT | ITEPD_ALERT_UCSI_EVENT); + if (event & ITEPD_ALERT_UCSI_EVENT) { err =3D itepd_read_reg(itepd, ITEPD_UCSI_CCI_REG, &le_cci, sizeof(le_cci)); @@ -148,7 +191,7 @@ static int itepd_process_event(struct itepd *itepd, u32= *cci) } } =20 - ack =3D event; + ack =3D raw; ret =3D itepd_write_reg(itepd, ITEPD_VENDOR_WC_INT, &ack, sizeof(ack)); if (ret) return ret; @@ -169,7 +212,8 @@ static int itepd_process_event(struct itepd *itepd, u32= *cci) *cci =3D itepd->cci; mutex_unlock(&itepd->received_lock); =20 - return ITEPD_EVENT_VDM; + return event & ITEPD_ALERT_VDM_EVENT ? ITEPD_EVENT_VDM : + ITEPD_EVENT_OTHER; } =20 static int ucsi_itepd_read_version(struct ucsi *ucsi, u16 *version) @@ -220,18 +264,233 @@ static int ucsi_itepd_read_message_in(struct ucsi *u= csi, void *val, size_t val_l static int ucsi_itepd_async_control(struct ucsi *ucsi, u64 command) { struct itepd *itepd =3D ucsi_get_drvdata(ucsi); - __le64 le_cmd =3D cpu_to_le64(command); + __le64 le_cmd; =20 - if (UCSI_COMMAND(command) =3D=3D UCSI_PPM_RESET) { + switch (UCSI_COMMAND(command)) { + case UCSI_PPM_RESET: mutex_lock(&itepd->received_lock); itepd->cci =3D 0; mutex_unlock(&itepd->received_lock); + break; + case UCSI_SET_NOTIFICATION_ENABLE: + command |=3D UCSI_ENABLE_NTFY_ATTENTION; + break; + default: + break; } =20 + le_cmd =3D cpu_to_le64(command); + return itepd_write_reg(itepd, ITEPD_UCSI_CONTROL_REG, &le_cmd, sizeof(le_cmd)); } =20 +static void itepd_port_set_state(struct itepd_port *port, unsigned long mo= de, + struct typec_displayport_data *dp_data) +{ + struct device *dev =3D &port->itepd->client->dev; + struct typec_retimer_state retimer_state =3D {}; + int ret; + + if (!dp_data && mode =3D=3D port->mode) + return; + + port->dp_alt.active =3D !!dp_data; + + port->state.alt =3D dp_data ? &port->dp_alt : NULL; + port->state.mode =3D mode; + port->state.data =3D dp_data; + + retimer_state.alt =3D port->state.alt; + retimer_state.mode =3D mode; + retimer_state.data =3D dp_data; + + ret =3D typec_retimer_set(port->typec_retimer, &retimer_state); + if (ret) + dev_err(dev, "port%u: failed to set retimer to mode %lu: %d\n", + port->index, mode, ret); + + ret =3D typec_mux_set(port->typec_mux, &port->state); + if (ret) + dev_err(dev, "port%u: failed to set mux to mode %lu: %d\n", + port->index, mode, ret); + + port->mode =3D mode; + + port->state.data =3D NULL; +} + +static void itepd_port_notify_hpd(struct itepd_port *port, bool hpd_state, + bool hpd_irq) +{ + if (!port->bridge) + return; + + if (hpd_state =3D=3D port->hpd_state && !hpd_irq) + return; + + port->hpd_state =3D hpd_state; + + drm_aux_hpd_bridge_notify(&port->bridge->dev, + hpd_state ? connector_status_connected : + connector_status_disconnected); +} + +static void itepd_set_orientation(struct ucsi_connector *con) +{ + if (con->ucsi->version >=3D UCSI_VERSION_2_0) + return; + + if (!UCSI_CONSTAT(con, CONNECTED)) { + typec_set_orientation(con->port, TYPEC_ORIENTATION_NONE); + return; + } + + if (bitmap_read(con->status, ITEPD_CONSTAT_ORIENTATION_OFFSET, 1) =3D=3D + UCSI_CONSTAT_ORIENTATION_REVERSE) + typec_set_orientation(con->port, TYPEC_ORIENTATION_REVERSE); + else + typec_set_orientation(con->port, TYPEC_ORIENTATION_NORMAL); +} + +static int itepd_get_dp_cam(struct itepd *itepd, struct ucsi_connector *co= n) +{ + struct ucsi_altmode alt[2] =3D {}; + u64 cmd; + u8 cam; + int ret; + + cmd =3D UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num); + ret =3D ucsi_send_command(itepd->ucsi, cmd, &cam, sizeof(cam)); + if (ret < 0) + return ret; + + if (cam =3D=3D ITEPD_CAM_NONE) + return -ENODEV; + + cmd =3D UCSI_GET_ALTERNATE_MODES; + cmd |=3D UCSI_GET_ALTMODE_RECIPIENT(UCSI_RECIPIENT_CON); + cmd |=3D UCSI_GET_ALTMODE_CONNECTOR_NUMBER(con->num); + cmd |=3D UCSI_GET_ALTMODE_OFFSET(cam); + ret =3D ucsi_send_command(itepd->ucsi, cmd, alt, sizeof(alt)); + if (ret < 0) { + dev_dbg(&itepd->client->dev, "con%d: cannot verify CAM %u: %d\n", + con->num, cam, ret); + return cam; + } + + if (alt[0].svid !=3D USB_TYPEC_DP_SID) + return -ENODEV; + + return cam; +} + +static int itepd_get_attention_status(struct itepd *itepd, + struct ucsi_connector *con, u32 *status) +{ + u8 data[ITEPD_ATTENTION_VDO_LEN] =3D {}; + u64 cmd; + int ret; + + cmd =3D UCSI_GET_ATTENTION_VDO | UCSI_CONNECTOR_NUMBER(con->num); + ret =3D ucsi_send_command(itepd->ucsi, cmd, data, sizeof(data)); + if (ret < 0) + return ret; + + if (!(data[ITEPD_ATTENTION_NUM_VDOS_OFFSET] & + ITEPD_ATTENTION_NUM_VDOS_MASK)) + return -ENODATA; + + *status =3D get_unaligned_le32(data + ITEPD_ATTENTION_VDO_OFFSET); + + return 0; +} + +static void itepd_handle_dp_altmode(struct itepd *itepd, + struct itepd_port *port, + struct ucsi_connector *con) +{ + struct typec_displayport_data dp_data =3D {}; + u8 data[ITEPD_CAM_CS_LEN] =3D {}; + unsigned long mode; + u32 status; + u8 pin_assign; + u64 cmd; + int cam; + int ret; + + cam =3D itepd_get_dp_cam(itepd, con); + if (cam < 0) { + /* Partner is in some other alternate mode, keep USB alive */ + itepd_port_set_state(port, TYPEC_STATE_USB, NULL); + itepd_port_notify_hpd(port, false, false); + return; + } + + cmd =3D UCSI_GET_CAM_CS | UCSI_CONNECTOR_NUMBER(con->num) | + ITEPD_CAM_CS_INDEX(cam); + ret =3D ucsi_send_command(itepd->ucsi, cmd, data, sizeof(data)); + if (ret < 0) { + dev_err(&itepd->client->dev, "con%d: GET_CAM_CS failed: %d\n", + con->num, ret); + return; + } + + dp_data.status =3D get_unaligned_le32(data + ITEPD_CAM_CS_STATUS_OFFSET); + dp_data.conf =3D get_unaligned_le32(data + ITEPD_CAM_CS_CONF_OFFSET); + + pin_assign =3D DP_CONF_GET_PIN_ASSIGN(dp_data.conf); + if (pin_assign) + mode =3D TYPEC_MODAL_STATE(get_count_order(pin_assign)); + else + mode =3D TYPEC_STATE_SAFE; + + itepd_port_set_state(port, mode, &dp_data); + + if (!itepd_get_attention_status(itepd, con, &status)) + dp_data.status =3D status; + + itepd_port_notify_hpd(port, !!(dp_data.status & DP_STATUS_HPD_STATE), + !!(dp_data.status & DP_STATUS_IRQ_HPD)); +} + +static void ucsi_itepd_update_connector(struct ucsi_connector *con) +{ + struct itepd *itepd =3D ucsi_get_drvdata(con->ucsi); + + if (con->num < 1 || con->num > itepd->num_ports) + return; + + con->typec_cap.orientation_aware =3D true; +} + +static void ucsi_itepd_connector_status(struct ucsi_connector *con) +{ + struct itepd *itepd =3D ucsi_get_drvdata(con->ucsi); + struct itepd_port *port; + + if (con->num < 1 || con->num > itepd->num_ports) + return; + + port =3D &itepd->ports[con->num - 1]; + + itepd_set_orientation(con); + + if (!UCSI_CONSTAT(con, CONNECTED)) { + itepd_port_set_state(port, TYPEC_STATE_SAFE, NULL); + itepd_port_notify_hpd(port, false, false); + return; + } + + if (!UCSI_CONSTAT(con, PARTNER_FLAG_ALT_MODE)) { + itepd_port_set_state(port, TYPEC_STATE_USB, NULL); + itepd_port_notify_hpd(port, false, false); + return; + } + + itepd_handle_dp_altmode(itepd, port, con); +} + static const struct ucsi_operations ucsi_itepd_ops =3D { .read_version =3D ucsi_itepd_read_version, .read_cci =3D ucsi_itepd_read_cci, @@ -239,6 +498,8 @@ static const struct ucsi_operations ucsi_itepd_ops =3D { .read_message_in =3D ucsi_itepd_read_message_in, .sync_control =3D ucsi_sync_control_common, .async_control =3D ucsi_itepd_async_control, + .update_connector =3D ucsi_itepd_update_connector, + .connector_status =3D ucsi_itepd_connector_status, }; =20 static irqreturn_t itepd_irq_process(struct itepd *itepd) @@ -256,6 +517,9 @@ static irqreturn_t itepd_irq_process(struct itepd *itep= d) if (ret =3D=3D ITEPD_EVENT_UCSI) ucsi_notify_common(itepd->ucsi, cci); =20 + if (ret =3D=3D ITEPD_EVENT_VDM) + dev_dbg(&itepd->client->dev, "VDM event\n"); + return IRQ_HANDLED; } =20 @@ -266,6 +530,117 @@ static irqreturn_t itepd_irq_thread_fn(int irq, void = *data) return itepd_irq_process(itepd); } =20 +static void itepd_put_mux(void *data) +{ + typec_mux_put(data); +} + +static void itepd_put_retimer(void *data) +{ + typec_retimer_put(data); +} + +static int itepd_ports_init(struct itepd *itepd) +{ + struct device *dev =3D &itepd->client->dev; + struct itepd_port *port; + unsigned int count, idx =3D 0; + u32 reg; + int ret; + + count =3D device_get_child_node_count(dev); + if (!count) + return 0; + + itepd->ports =3D devm_kcalloc(dev, count, sizeof(*itepd->ports), + GFP_KERNEL); + if (!itepd->ports) + return -ENOMEM; + + device_for_each_child_node_scoped(dev, fwnode) { + ret =3D fwnode_property_read_u32(fwnode, "reg", ®); + if (ret < 0) + return dev_err_probe(dev, ret, + "%pfwP: missing reg property\n", + fwnode); + + if (reg !=3D idx) + return dev_err_probe(dev, -EINVAL, + "%pfwP: reg is %u, expected %u\n", + fwnode, reg, idx); + + port =3D &itepd->ports[idx]; + port->itepd =3D itepd; + port->index =3D idx; + port->mode =3D TYPEC_STATE_SAFE; + port->state.mode =3D TYPEC_STATE_SAFE; + port->dp_alt.svid =3D USB_TYPEC_DP_SID; + port->dp_alt.mode =3D USB_TYPEC_DP_MODE; + + port->typec_mux =3D fwnode_typec_mux_get(fwnode); + if (IS_ERR(port->typec_mux)) + return dev_err_probe(dev, PTR_ERR(port->typec_mux), + "port%u: failed to get mode-switch\n", + idx); + + if (port->typec_mux) { + ret =3D devm_add_action_or_reset(dev, itepd_put_mux, + port->typec_mux); + if (ret) + return ret; + } + + port->typec_retimer =3D fwnode_typec_retimer_get(fwnode); + if (IS_ERR(port->typec_retimer)) + return dev_err_probe(dev, PTR_ERR(port->typec_retimer), + "port%u: failed to get retimer-switch\n", + idx); + + if (port->typec_retimer) { + ret =3D devm_add_action_or_reset(dev, itepd_put_retimer, + port->typec_retimer); + if (ret) + return ret; + } + + if (is_of_node(fwnode)) { + struct device_node *np =3D to_of_node(fwnode); + + port->bridge =3D devm_drm_dp_hpd_bridge_alloc(dev, np); + if (IS_ERR(port->bridge)) + return dev_err_probe(dev, PTR_ERR(port->bridge), + "port%u: failed to allocate HPD bridge\n", + idx); + } + + idx++; + } + + itepd->num_ports =3D idx; + + return 0; +} + +static int itepd_bridges_add(struct itepd *itepd) +{ + struct device *dev =3D &itepd->client->dev; + unsigned int i; + int ret; + + for (i =3D 0; i < itepd->num_ports; i++) { + if (!itepd->ports[i].bridge) + continue; + + ret =3D devm_drm_dp_hpd_bridge_add(dev, itepd->ports[i].bridge); + if (ret) + return dev_err_probe(dev, ret, + "port%u: failed to add HPD bridge\n", + i); + } + + return 0; +} + static int itepd_probe(struct i2c_client *client) { struct device *dev =3D &client->dev; @@ -286,6 +661,10 @@ static int itepd_probe(struct i2c_client *client) mutex_init(&itepd->received_lock); i2c_set_clientdata(client, itepd); =20 + ret =3D itepd_ports_init(itepd); + if (ret) + return ret; + itepd->ucsi =3D ucsi_create(dev, &ucsi_itepd_ops); if (IS_ERR(itepd->ucsi)) return dev_err_probe(dev, PTR_ERR(itepd->ucsi), @@ -310,8 +689,14 @@ static int itepd_probe(struct i2c_client *client) goto out_free_irq; } =20 + ret =3D itepd_bridges_add(itepd); + if (ret) + goto out_ucsi_unregister; + return 0; =20 +out_ucsi_unregister: + ucsi_unregister(itepd->ucsi); out_free_irq: free_irq(client->irq, itepd); out_ucsi_destroy: @@ -330,6 +715,7 @@ static void itepd_remove(struct i2c_client *client) =20 static const struct of_device_id itepd_of_match_table[] =3D { { .compatible =3D "ite,it8851" }, + { .compatible =3D "ite,it5271" }, {} }; MODULE_DEVICE_TABLE(of, itepd_of_match_table); @@ -352,6 +738,5 @@ static struct i2c_driver itepd_driver =3D { module_i2c_driver(itepd_driver); =20 MODULE_AUTHOR("Jeson Yang "); -MODULE_AUTHOR("Amber Kao "); -MODULE_DESCRIPTION("UCSI driver for ITE IT8851 and IT8853 Type-C PD contro= llers"); +MODULE_DESCRIPTION("UCSI driver for ITE IT8851 and IT5271 Type-C PD contro= llers"); MODULE_LICENSE("GPL"); --=20 2.53.0