From nobody Sat Sep 26 13:09:28 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (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 ABAE13932D5; Tue, 1 Sep 2026 11:40:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262853; cv=none; b=gxyRBGbAxqn/AGoH/92q8FNrm7kOKKK6qdOiesIGTq0WzJKs4VI9HZPHCWhwq5QLLNb+DS7X7mhgu4pweQGsDHLmdjL6Z6swrMYqLsicFmdZS6V5gDdZ6kSU5TRPOB96FIs+ET2JZSX1crhZS57FW7w2oyYPmtSpCdjpdEFS+Zg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262853; c=relaxed/simple; bh=CmjtH7jTG5+YPiPuOpmBo34pkSZretrF0i3bIFIUIZs=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=oDB1GM8bpgpgD15RqUS8iyYUBxpiY0yQyseKMh9RC9T8ar6Whq/WEXRJVvquXSsQNYGdI027bBytIEGOz+IBT19OCysCLRJK1iRCKaMwMeEERsmKo+9aEim/lIsY7Tmf52KB7WLCXCPvW12CA2kPBifO0I01QXDS91QeQiX7JEk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Tue, 1 Sep 2026 19:35:38 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Tue, 1 Sep 2026 19:35:38 +0800 From: Billy Tsai Date: Tue, 1 Sep 2026 19:35:28 +0800 Subject: [PATCH 1/8] dt-bindings: i3c: Document the AST2700 I3C controller 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: <20260901-b4-i3c-hci-ast2700-v1-1-19909e7cbd7e@aspeedtech.com> References: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> In-Reply-To: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> To: Alexandre Belloni , Frank Li , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Philipp Zabel CC: , , , , , Billy Tsai X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788262538; l=6052; i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id; bh=CmjtH7jTG5+YPiPuOpmBo34pkSZretrF0i3bIFIUIZs=; b=obfw+O0Z2sGTX5UNne+B4lR3b9FwoJjZ9Zlrl0aI1cWzON1B/wCWo+0EN2GTuX/RH0ZTh6vd2 KgVDu5JN4lWC9bfdHmkYiflE6oNctNgHYy50RHU+eJ79u8mtHZmOv+W X-Developer-Key: i=billy_tsai@aspeedtech.com; a=ed25519; pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ= The AST2700 I3C controller implements the MIPI I3C HCI programming model, extended with an ASPEED in-house control block and a PHY programming window advertised through an HCI vendor extended capability. Follow the existing aspeed,ast2600-i3c precedent of describing SoC integrations of a common I3C IP in their own schema. The controller takes a core clock for PHY timing and two reset-names lines: its own reset and a DMA engine reset shared by all I3C controllers on the SoC. The PHY timings derived from the core clock and bus rate assume nominal bus loading. Boards with long traces, weak pull-ups, or capacitive loading need different ratios and setup periods, so document properties to override the push-pull and open-drain SCL periods, the SDA TX hold time, the tCAS/tCBP periods, and the SCL low period used for a repeated START or STOP. Boards without external I3C pull-up resistors can use the PHY's built-in pull-ups instead. The achievable resistance depends on the voltage domain of the pins (high-voltage vs low-voltage), which can be determined from the pin group assigned to the bus. Reuse bias-pull-up (pincfg-node.yaml) for the strength in Ohms; it is declared on this node rather than a pin-config node since the pull-up switch is part of the I3C PHY, not the broader SoC pin control IPs. Signed-off-by: Billy Tsai Assisted-by: Claude:claude-fable-5 --- We considered formally expressing bias-pull-up's dependency on a pinctrl state in the schema, but couldn't find a clean way to say "depends on whichever pinctrl-N is active" without hard-coding pinctrl-0 specifically, which would be wrong in general (a node's active pin state doesn't have to sit at index 0). We've added pinctrl-0 to the example to illustrate the typical usage instead, without a formal schema-level dependency. --- .../bindings/i3c/aspeed,ast2700-i3c-hci.yaml | 133 +++++++++++++++++= ++++ 1 file changed, 133 insertions(+) diff --git a/Documentation/devicetree/bindings/i3c/aspeed,ast2700-i3c-hci.y= aml b/Documentation/devicetree/bindings/i3c/aspeed,ast2700-i3c-hci.yaml new file mode 100644 index 000000000000..19ecb5b15038 --- /dev/null +++ b/Documentation/devicetree/bindings/i3c/aspeed,ast2700-i3c-hci.yaml @@ -0,0 +1,133 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i3c/aspeed,ast2700-i3c-hci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ASPEED AST2700 I3C controller + +maintainers: + - Billy Tsai + +description: + The AST2700 I3C controller implements the MIPI I3C HCI programming model, + extended with an ASPEED in-house control block and a PHY programming win= dow + that are advertised through an HCI vendor extended capability. + +properties: + compatible: + const: aspeed,ast2700-i3c-hci + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + items: + - description: Core controller reset. + - description: DMA engine reset shared by all I3C controllers. + + reset-names: + items: + - const: core + - const: dma + + bias-pull-up: + $ref: /schemas/pinctrl/pincfg-node.yaml#/properties/bias-pull-up + enum: [177, 231, 261, 317, 400, 470, 550, 600, 750] + description: + Enable the PHY-internal pull-ups on SCL and SDA and select their + strength, in Ohms. High-voltage pins support 400, 470 or 600 Ohms, + and low-voltage pins support 177, 231, 261, 317, 400, 550 or 750 + Ohms. When absent the internal pull-ups stay disabled and the bus + relies on external pull-ups. + + aspeed,pp-scl-high-ns: + description: + Push-pull SCL high period. Overrides the value derived from the + i3c-scl-hz bus rate. + + aspeed,pp-scl-low-ns: + description: + Push-pull SCL low period. Overrides the value derived from the + i3c-scl-hz bus rate. + + aspeed,od-scl-high-ns: + default: 380 + description: + Open-drain SCL high period. + + aspeed,od-scl-low-ns: + default: 620 + description: + Open-drain SCL low period. + + aspeed,sda-tx-hold-ns: + default: 10 + description: + SDA output hold time relative to the SCL edge. + + aspeed,cas-ns: + minimum: 39 + description: + Clock-after-start (tCAS) period driven after a START condition. + When absent it is derived from the SCL low period of the + applicable bus context, clamped to the protocol minimum. + + aspeed,cbp-ns: + minimum: 20 + description: + Clock-before-stop (tCBP) period driven before a STOP condition. + When absent it is derived from the SCL low period of the + applicable bus context, clamped to the protocol minimum. + + aspeed,sr-p-scl-low-ns: + default: 40 + description: + SCL low period used while preparing a repeated START or STOP + condition, kept separate from the push-pull SCL low period to + satisfy the MIPI I3C clock stalling requirements. + +dependencies: + aspeed,pp-scl-high-ns: + - aspeed,pp-scl-low-ns + aspeed,pp-scl-low-ns: + - aspeed,pp-scl-high-ns + aspeed,od-scl-high-ns: + - aspeed,od-scl-low-ns + aspeed,od-scl-low-ns: + - aspeed,od-scl-high-ns + +required: + - compatible + - reg + - interrupts + - clocks + - resets + - reset-names + +allOf: + - $ref: i3c.yaml# + +unevaluatedProperties: false + +examples: + - | + i3c@14c24000 { + compatible =3D "aspeed,ast2700-i3c-hci"; + reg =3D <0x14c24000 0x1000>; + interrupts =3D <100>; + clocks =3D <&syscon 20>; + resets =3D <&syscon 37>, <&syscon 34>; + reset-names =3D "core", "dma"; + bias-pull-up =3D <400>; + pinctrl-names =3D "default"; + pinctrl-0 =3D <&pinctrl_i3c4_default>; + #address-cells =3D <3>; + #size-cells =3D <0>; + }; --=20 2.34.1 From nobody Sat Sep 26 13:09:28 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (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 4104839022A; Tue, 1 Sep 2026 11:40:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262857; cv=none; b=TcQ4jryN+Oo9HVmo8s+TpoEkWuhGc4MoUK4N4NDCY1L0rVfPLBr4rtATZ+5wRfOKv558yE1UAR1IrK16X0aWPhIsrULRNG0ZzF+TX/r4mN5ObuqFlW5ToOB+HyBVm7/bBciQ7Tk+Ev2IKSxy3QY59iBGXaVbQljhQXz24/eeKtE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262857; c=relaxed/simple; bh=yCD4RYCViM2mMO/TblbVOstiyco6H5e6Skzly2QPz2w=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=clZruG+N4YZti6SE47HsOcmPgw4T/W7lE27OIC3gTOEhnVGGCtnPBvEWqx3AIhUfVolzksSKbqnPyp/RJ7QlFkrSkSSlCMbGBU8WuxpepOsLGD4WlpXoxlwHN5DIsJF8XmGbOKvPPhpH+RQbIZwKxL5yD46DlIuS9zcLcL0uu/s= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Tue, 1 Sep 2026 19:35:38 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Tue, 1 Sep 2026 19:35:38 +0800 From: Billy Tsai Date: Tue, 1 Sep 2026 19:35:29 +0800 Subject: [PATCH 2/8] i3c: mipi-i3c-hci: Support address-indexed DAT slots 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: <20260901-b4-i3c-hci-ast2700-v1-2-19909e7cbd7e@aspeedtech.com> References: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> In-Reply-To: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> To: Alexandre Belloni , Frank Li , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Philipp Zabel CC: , , , , , Billy Tsai X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788262538; l=11576; i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id; bh=yCD4RYCViM2mMO/TblbVOstiyco6H5e6Skzly2QPz2w=; b=HHpFyojL+IC4dJxfewHl7dIUzXKppiqg3u8JCaxhFqzzIxVP0WceSK9rRUCKXL7xKtSWovYaP hbBBXUbYVzIDhdH4nDhpKqKVQivgmu/Q0FrZ3kmjitB3Ugy/nXu5wbG X-Developer-Key: i=billy_tsai@aspeedtech.com; a=ed25519; pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ= HCI v1's DEV_INDEX allocation is sequential: the driver picks any free DAT slot, with no relationship between the slot number and the device's I3C address. ASPEED's I3C controllers have long needed to support more devices than the standard 5-bit DEV_INDEX field (addresses 0-31) allows, so they use a layout where the DAT slot number is itself the device's address, requiring DEV_INDEX to widen to 7 bits (addresses 0-127). Add HCI_QUIRK_DAT_INDEX_IS_ADDR for controllers with this layout: allocate address-indexed DAT slots and emit the wider DEV_INDEX field. Moving an entry on a dynamic address change can fail if its slot is already taken, so set_dynamic_addr() now returns an error that the reattach and DAA paths propagate. Controllers without the quirk keep the existing behavior. Signed-off-by: Billy Tsai Assisted-by: Claude:claude-fable-5 --- drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 35 ++++++++++++------ drivers/i3c/master/mipi-i3c-hci/core.c | 19 +++++++--- drivers/i3c/master/mipi-i3c-hci/dat.h | 5 ++- drivers/i3c/master/mipi-i3c-hci/dat_v1.c | 63 ++++++++++++++++++++++++++++= +--- drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + 5 files changed, 98 insertions(+), 25 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/= mipi-i3c-hci/cmd_v1.c index 3b9345718d273..9a11affb14bf6 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c @@ -141,6 +141,14 @@ static enum hci_cmd_mode get_i2c_mode(struct i3c_hci *= hci) return MODE_I2C_Fm; } =20 +static u32 cmd_v1_dev_index(struct i3c_hci *hci, unsigned int dat_idx) +{ + u32 mask =3D hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR ? + W0_MASK(22, 16) : W0_MASK(20, 16); + + return field_prep(mask, dat_idx); +} + static void fill_data_bytes(struct hci_xfer *xfer, u8 *data, unsigned int data_len) { @@ -195,7 +203,7 @@ static int hci_cmd_v1_prep_ccc(struct i3c_hci *hci, CMD_0_ATTR_I | CMD_I0_TID(xfer->cmd_tid) | CMD_I0_CMD(ccc_cmd) | CMD_I0_CP | - CMD_I0_DEV_INDEX(dat_idx) | + cmd_v1_dev_index(hci, dat_idx) | CMD_I0_DTT(data_len) | CMD_I0_MODE(mode); fill_data_bytes(xfer, data, data_len); @@ -205,7 +213,7 @@ static int hci_cmd_v1_prep_ccc(struct i3c_hci *hci, CMD_0_ATTR_R | CMD_R0_TID(xfer->cmd_tid) | CMD_R0_CMD(ccc_cmd) | CMD_R0_CP | - CMD_R0_DEV_INDEX(dat_idx) | + cmd_v1_dev_index(hci, dat_idx) | CMD_R0_MODE(mode) | (rnw ? CMD_R0_RNW : 0); xfer->cmd_desc[1] =3D @@ -233,7 +241,7 @@ static void hci_cmd_v1_prep_i3c_xfer(struct i3c_hci *hc= i, xfer->cmd_desc[0] =3D CMD_0_ATTR_I | CMD_I0_TID(xfer->cmd_tid) | - CMD_I0_DEV_INDEX(dat_idx) | + cmd_v1_dev_index(hci, dat_idx) | CMD_I0_DTT(data_len) | CMD_I0_MODE(mode); fill_data_bytes(xfer, data, data_len); @@ -242,7 +250,7 @@ static void hci_cmd_v1_prep_i3c_xfer(struct i3c_hci *hc= i, xfer->cmd_desc[0] =3D CMD_0_ATTR_R | CMD_R0_TID(xfer->cmd_tid) | - CMD_R0_DEV_INDEX(dat_idx) | + cmd_v1_dev_index(hci, dat_idx) | CMD_R0_MODE(mode) | (rnw ? CMD_R0_RNW : 0); xfer->cmd_desc[1] =3D @@ -268,7 +276,7 @@ static void hci_cmd_v1_prep_i2c_xfer(struct i3c_hci *hc= i, xfer->cmd_desc[0] =3D CMD_0_ATTR_I | CMD_I0_TID(xfer->cmd_tid) | - CMD_I0_DEV_INDEX(dat_idx) | + cmd_v1_dev_index(hci, dat_idx) | CMD_I0_DTT(data_len) | CMD_I0_MODE(mode); fill_data_bytes(xfer, data, data_len); @@ -277,7 +285,7 @@ static void hci_cmd_v1_prep_i2c_xfer(struct i3c_hci *hc= i, xfer->cmd_desc[0] =3D CMD_0_ATTR_R | CMD_R0_TID(xfer->cmd_tid) | - CMD_R0_DEV_INDEX(dat_idx) | + cmd_v1_dev_index(hci, dat_idx) | CMD_R0_MODE(mode) | (rnw ? CMD_R0_RNW : 0); xfer->cmd_desc[1] =3D @@ -306,19 +314,22 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) * Yes, there is room for improvements. */ for (;;) { - ret =3D mipi_i3c_hci_dat_v1.alloc_entry(hci); - if (ret < 0) - break; - dat_idx =3D ret; ret =3D i3c_master_get_free_addr(&hci->master, next_addr); if (ret < 0) break; next_addr =3D ret; + ret =3D mipi_i3c_hci_dat_v1.alloc_entry(hci, next_addr); + if (ret < 0) + break; + dat_idx =3D ret; =20 dev_dbg(&hci->master.dev, "next_addr =3D 0x%02x, DAA using DAT %d", next_addr, dat_idx); - mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dat_idx, next_addr); + ret =3D mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dat_idx, + next_addr); + if (ret < 0) + break; mipi_i3c_hci_dct_index_reset(hci); =20 xfer->cmd_tid =3D hci_get_tid(); @@ -326,7 +337,7 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) CMD_0_ATTR_A | CMD_A0_TID(xfer->cmd_tid) | CMD_A0_CMD(I3C_CCC_ENTDAA) | - CMD_A0_DEV_INDEX(dat_idx) | + cmd_v1_dev_index(hci, dat_idx) | CMD_A0_DEV_COUNT(1) | CMD_A0_ROC | CMD_A0_TOC; xfer->cmd_desc[1] =3D 0; diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mi= pi-i3c-hci/core.c index dadf049bd4b54..a624e3c40484e 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -574,7 +574,8 @@ static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *= dev) if (!dev_data) return -ENOMEM; if (hci->cmd =3D=3D &mipi_i3c_hci_cmd_v1) { - ret =3D mipi_i3c_hci_dat_v1.alloc_entry(hci); + ret =3D mipi_i3c_hci_dat_v1.alloc_entry(hci, + dev->info.dyn_addr ?: dev->info.static_addr); if (ret < 0) { kfree(dev_data); return ret; @@ -593,9 +594,17 @@ static int i3c_hci_reattach_i3c_dev(struct i3c_dev_des= c *dev, u8 old_dyn_addr) struct i3c_hci *hci =3D to_i3c_hci(m); struct i3c_hci_dev_data *dev_data =3D i3c_dev_get_master_data(dev); =20 - if (hci->cmd =3D=3D &mipi_i3c_hci_cmd_v1) - mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dev_data->dat_idx, - dev->info.dyn_addr); + if (hci->cmd =3D=3D &mipi_i3c_hci_cmd_v1) { + int ret; + + ret =3D mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, + dev_data->dat_idx, + dev->info.dyn_addr); + if (ret) + return ret; + if (hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR) + dev_data->dat_idx =3D dev->info.dyn_addr; + } return 0; } =20 @@ -623,7 +632,7 @@ static int i3c_hci_attach_i2c_dev(struct i2c_dev_desc *= dev) dev_data =3D kzalloc_obj(*dev_data); if (!dev_data) return -ENOMEM; - ret =3D mipi_i3c_hci_dat_v1.alloc_entry(hci); + ret =3D mipi_i3c_hci_dat_v1.alloc_entry(hci, dev->addr); if (ret < 0) { kfree(dev_data); return ret; diff --git a/drivers/i3c/master/mipi-i3c-hci/dat.h b/drivers/i3c/master/mip= i-i3c-hci/dat.h index 6881f19da77f2..d4c7b03724d37 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dat.h +++ b/drivers/i3c/master/mipi-i3c-hci/dat.h @@ -14,12 +14,13 @@ #define DAT_0_I2C_DEVICE W0_BIT_(31) #define DAT_0_SIR_REJECT W0_BIT_(13) #define DAT_0_IBI_PAYLOAD W0_BIT_(12) +#define HCI_DAT_ANY_ADDRESS ((unsigned int)-1) =20 struct hci_dat_ops { int (*init)(struct i3c_hci *hci); - int (*alloc_entry)(struct i3c_hci *hci); + int (*alloc_entry)(struct i3c_hci *hci, unsigned int address); void (*free_entry)(struct i3c_hci *hci, unsigned int dat_idx); - void (*set_dynamic_addr)(struct i3c_hci *hci, unsigned int dat_idx, u8 ad= dr); + int (*set_dynamic_addr)(struct i3c_hci *hci, unsigned int dat_idx, u8 add= r); void (*set_static_addr)(struct i3c_hci *hci, unsigned int dat_idx, u8 add= r); void (*set_flags)(struct i3c_hci *hci, unsigned int dat_idx, u32 w0, u32 = w1); void (*clear_flags)(struct i3c_hci *hci, unsigned int dat_idx, u32 w0, u3= 2 w1); diff --git a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c b/drivers/i3c/master/= mipi-i3c-hci/dat_v1.c index 852966aa20d98..19b0f72e4ac82 100644 --- a/drivers/i3c/master/mipi-i3c-hci/dat_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/dat_v1.c @@ -89,7 +89,7 @@ static int hci_dat_v1_init(struct i3c_hci *hci) return 0; } =20 -static int hci_dat_v1_alloc_entry(struct i3c_hci *hci) +static int hci_dat_v1_alloc_entry(struct i3c_hci *hci, unsigned int addres= s) { unsigned int dat_idx; int ret; @@ -99,9 +99,17 @@ static int hci_dat_v1_alloc_entry(struct i3c_hci *hci) if (ret) return ret; } - dat_idx =3D find_first_zero_bit(hci->DAT_data, hci->DAT_entries); - if (dat_idx >=3D hci->DAT_entries) - return -ENOENT; + if (hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR) { + if (address >=3D hci->DAT_entries) + return -EINVAL; + if (test_bit_acquire(address, hci->DAT_data)) + return -ENOENT; + dat_idx =3D address; + } else { + dat_idx =3D find_first_zero_bit(hci->DAT_data, hci->DAT_entries); + if (dat_idx >=3D hci->DAT_entries) + return -ENOENT; + } __set_bit(dat_idx, hci->DAT_data); =20 /* default flags */ @@ -118,9 +126,43 @@ static void hci_dat_v1_free_entry(struct i3c_hci *hci,= unsigned int dat_idx) __clear_bit(dat_idx, hci->DAT_data); } =20 -static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci, - unsigned int dat_idx, u8 address) +static int hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci, + unsigned int dat_idx, u8 address) { + if (hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR) { + u32 dat_w0, dat_w1; + + /* + * The controller derives the device address from the DAT slot + * index, so DAT_0_DYNAMIC_ADDRESS is never written: an address + * change moves the whole entry to the slot named by the new + * address instead. + */ + if (dat_idx =3D=3D address) + return 0; + if (address >=3D hci->DAT_entries) { + dev_err(&hci->master.dev, "invalid DAT slot %u\n", address); + return -EINVAL; + } + if (test_bit(address, hci->DAT_data)) { + dev_err(&hci->master.dev, "DAT slot %u already in use\n", + address); + return -EBUSY; + } + + dat_w0 =3D dat_w0_read(dat_idx); + dat_w1 =3D dat_w1_read(dat_idx); + + __set_bit(address, hci->DAT_data); + dat_w0_write(address, dat_w0); + dat_w1_write(address, dat_w1); + + dat_w0_write(dat_idx, 0); + dat_w1_write(dat_idx, 0); + __clear_bit(dat_idx, hci->DAT_data); + return 0; + } + u32 dat_w0; =20 dat_w0 =3D dat_w0_read(dat_idx); @@ -128,6 +170,8 @@ static void hci_dat_v1_set_dynamic_addr(struct i3c_hci = *hci, dat_w0 |=3D FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) | (parity8(address) ? 0 : DAT_0_DYNADDR_PARITY); dat_w0_write(dat_idx, dat_w0); + + return 0; } =20 static void hci_dat_v1_set_static_addr(struct i3c_hci *hci, @@ -169,6 +213,13 @@ static void hci_dat_v1_clear_flags(struct i3c_hci *hci= , unsigned int dat_idx, =20 static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr) { + if (hci->quirks & HCI_QUIRK_DAT_INDEX_IS_ADDR) { + if (dev_addr < hci->DAT_entries && + test_bit(dev_addr, hci->DAT_data)) + return dev_addr; + return -ENODEV; + } + unsigned int dat_idx; u32 dat_w0; =20 diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mip= i-i3c-hci/hci.h index ee73f6e6756a1..f50fc1e22a855 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -177,6 +177,7 @@ struct i3c_hci_dev_data { #define HCI_QUIRK_RPM_PARENT_MANAGED BIT(7) /* Runtime PM managed by pare= nt device */ #define HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET BIT(8) /* Do PIO queue SW = resets after DMA abort */ #define HCI_QUIRK_DMA_REQUIRES_HC_ABORT BIT(9) /* Use HC_CONTROL ABORT t= o abort DMA */ +#define HCI_QUIRK_DAT_INDEX_IS_ADDR BIT(10) /* DAT entries are indexed by= device address */ =20 /* global functions */ void mipi_i3c_hci_resume(struct i3c_hci *hci); --=20 2.34.1 From nobody Sat Sep 26 13:09:28 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (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 486BA3AF65C; Tue, 1 Sep 2026 11:40:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262858; cv=none; b=qSForSA3GXn5pouPupbG+tmHt7oFj1VteiHGnB6P7nn1/pPJILZ5Xg2S6Qa5O1JMV4cXI85wCssByHYk0CGJqAjvXte0XBKszeCex/BGkBYZajcsGuW5EsdTpNtzqqn1JhitVFXO73vw8jlwk+rGAG792o105vSR7c42CQxIFv8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262858; c=relaxed/simple; bh=w8DwWiTyjCgKHUh3jNegniNOD5FFOdIu9HTX8Den63I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=fwTZM/CHUyxM29ax3+pWfbxKNJq/2Ws3L5qhjfpqPxA40NT5rbnTwqLCTR5gs18cJ0EZvTpm7Xd3HMYxV4iYv3Za1jh5OslZFN4B0+8r4bdDzjckGYabo/BWw1wAHhZCcFDdQlrlIHzSoM5xyui4UDXuxY/B1TRO2Qx53xoCpwQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Tue, 1 Sep 2026 19:35:39 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Tue, 1 Sep 2026 19:35:39 +0800 From: Billy Tsai Date: Tue, 1 Sep 2026 19:35:30 +0800 Subject: [PATCH 3/8] i3c: mipi-i3c-hci: Add a quirk for 64-bit DMA addressing 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: <20260901-b4-i3c-hci-ast2700-v1-3-19909e7cbd7e@aspeedtech.com> References: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> In-Reply-To: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> To: Alexandre Belloni , Frank Li , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Philipp Zabel CC: , , , , , Billy Tsai X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788262538; l=2535; i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id; bh=w8DwWiTyjCgKHUh3jNegniNOD5FFOdIu9HTX8Den63I=; b=zakcGFU95zIF9c596EWGOrqXlXM4wUfpzGev03srlA/CbJygcFlzgNMnkc2Q3kd0uBFwf9HFP 5576jPxF/gFAjNsiAAYqwNub4CH4bWgjn3W7UY6ik5cId2GXEvjb1vU X-Developer-Key: i=billy_tsai@aspeedtech.com; a=ed25519; pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ= The HCI ring base registers are 64 bits wide by specification, but not every implementation wires the upper half, so the driver leaves the platform device at the default 32-bit DMA mask unless told otherwise. ASPEED platforms place all of DRAM above the 32-bit boundary (0x4_00000000), so under the default mask every dma_alloc_coherent() call for the DMA rings fails. Add HCI_QUIRK_DMA_64BIT for controllers whose DMA engine drives the full address width, and declare a 64-bit streaming and coherent DMA mask for them before the rings are allocated. Signed-off-by: Billy Tsai Assisted-by: Claude:claude-fable-5 --- drivers/i3c/master/mipi-i3c-hci/core.c | 7 +++++++ drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mi= pi-i3c-hci/core.c index a624e3c40484..c03c3a9cbe4f 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -1167,6 +1168,12 @@ static int i3c_hci_probe(struct platform_device *pde= v) hci->quirks =3D (unsigned long)device_get_match_data(&pdev->dev); if (!hci->quirks && platform_get_device_id(pdev)) hci->quirks =3D platform_get_device_id(pdev)->driver_data; + if (hci->quirks & HCI_QUIRK_DMA_64BIT) { + ret =3D dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "cannot set DMA mask\n"); + } =20 ret =3D devm_clk_bulk_get_all_enabled(&pdev->dev, &clks); if (ret < 0) diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mip= i-i3c-hci/hci.h index f50fc1e22a85..2110f806a53c 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -178,6 +178,7 @@ struct i3c_hci_dev_data { #define HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET BIT(8) /* Do PIO queue SW = resets after DMA abort */ #define HCI_QUIRK_DMA_REQUIRES_HC_ABORT BIT(9) /* Use HC_CONTROL ABORT t= o abort DMA */ #define HCI_QUIRK_DAT_INDEX_IS_ADDR BIT(10) /* DAT entries are indexed by= device address */ +#define HCI_QUIRK_DMA_64BIT BIT(11) /* Controller DMA supports 64-bit ad= dressing */ =20 /* global functions */ void mipi_i3c_hci_resume(struct i3c_hci *hci); --=20 2.34.1 From nobody Sat Sep 26 13:09:28 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (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 EEA423B52EE; Tue, 1 Sep 2026 11:37:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262624; cv=none; b=qPeCURckY9WsRpn7xh3P1XIQ7qWR/3dN3sASRgkfkHXR7GYZMcI0V7EAXzPrFTn01CTNklIxs0wLc2swqEpLKujDoCMvptxRw6REmyNqyJvXbg3yqGSR3Q8jhi4PdwgRbcGdMDv/oxTFenkoinpCWoZq7ADleFgH23x7sdO32kE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262624; c=relaxed/simple; bh=n6vO/Fi5jhecmVwxetC4Wj6M9A4SBSKPZgOHLJNm8ng=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=GH1oFlhAdwCOFGFp0xbOqSw5/l5ZZ17tFYmT/B0djNws8eDiKkULdkRc3tuA2Z/2YO65TUJXLkyirZp1mvQcBrKI4lfaTqAHBNqQ+nMVdGdPPCq7FZDMhG5KAHrXaM8JiexAWNKgtc3dlqfm0/sWhGbGoRZa/wrlBCLRx+fOKFM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Tue, 1 Sep 2026 19:35:39 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Tue, 1 Sep 2026 19:35:39 +0800 From: Billy Tsai Date: Tue, 1 Sep 2026 19:35:31 +0800 Subject: [PATCH 4/8] i3c: mipi-i3c-hci: Add a quirk to clear the TX start threshold 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: <20260901-b4-i3c-hci-ast2700-v1-4-19909e7cbd7e@aspeedtech.com> References: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> In-Reply-To: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> To: Alexandre Belloni , Frank Li , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Philipp Zabel CC: , , , , , Billy Tsai X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788262538; l=11122; i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id; bh=n6vO/Fi5jhecmVwxetC4Wj6M9A4SBSKPZgOHLJNm8ng=; b=HHfIRufKO0rvzVE5evGWDeOxDqIGK5LwC4DFCfClX9QJhRprAHy2Zx7GO/2FHHZR7t0GHsEpU zZFLkzvr9C7CxAPfWOiwrs+ICl6gQuAse4SxQHa4bHJWvlYaTMZn3mf X-Developer-Key: i=billy_tsai@aspeedtech.com; a=ed25519; pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ= The DATA_TX_START_THLD field of the PIO data buffer threshold register holds off the start of a TX transfer until the FIFO holds a set amount of data, which in PIO mode cuts down on the number of software writes. The field resets to 0x1, requiring at least (2 ^ 2) DWORDs (16 bytes) in the FIFO before transmission starts. HCI controllers that support both PIO and DMA can expose the two as separate register blocks; nothing in the specification says selecting DMA mode disables the PIO block's own gating logic. On ASPEED platforms it doesn't: DATA_TX_START_THLD still holds up transfer start regardless of which mode feeds the FIFO, and in DMA mode that threshold is never satisfied for some transfer sizes: - 1-4 bytes: uses the Immediate Data Transfer Command. - 13+ bytes: since the hardware fetches data in 4-byte chunks, a 13-byte transfer fetches 16 bytes into the FIFO and reaches the threshold. - 5-12 bytes: the threshold is never reached and the transfer stalls. Add HCI_QUIRK_TX_START_THLD to clear the field whenever DMA mode is selected. The clear lives in i3c_hci_set_io_mode() rather than in probe so the reset-and-restore recovery path also reapplies it after a controller soft reset. Move the PIO Access Area register and bitfield definitions from pio.c to a new pio.h so this quirk can reuse PIO_DATA_BUFFER_THLD_CTRL and DATA_TX_START_THLD instead of redefining them. Signed-off-by: Billy Tsai Assisted-by: Claude:claude-fable-5 --- drivers/i3c/master/mipi-i3c-hci/core.c | 14 +++++ drivers/i3c/master/mipi-i3c-hci/hci.h | 1 + drivers/i3c/master/mipi-i3c-hci/pio.c | 97 +---------------------------- drivers/i3c/master/mipi-i3c-hci/pio.h | 109 +++++++++++++++++++++++++++++= ++++ 4 files changed, 125 insertions(+), 96 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mi= pi-i3c-hci/core.c index c03c3a9cbe4f..2290a889701c 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -26,6 +26,7 @@ #include "cmd.h" #include "dat.h" #include "ibi.h" +#include "pio.h" =20 /* * Host Controller Capabilities and Operation Registers @@ -823,6 +824,19 @@ static int i3c_hci_set_io_mode(struct i3c_hci *hci, bo= ol dma) else reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE); =20 + /* + * On the ASPEED AST2700 the TX start threshold gates transfer start + * even in DMA mode. Clear it so DMA transfers are not held back + * waiting for a PIO FIFO level that will never be reached. + */ + if (dma && (hci->quirks & HCI_QUIRK_TX_START_THLD) && hci->PIO_regs) { + void __iomem *thld_reg =3D hci->PIO_regs + PIO_DATA_BUFFER_THLD_CTRL; + u32 thld_val =3D readl(thld_reg); + + thld_val &=3D ~DATA_TX_START_THLD; + writel(thld_val, thld_reg); + } + if (!is_version_1_1_or_newer(hci)) return 0; =20 diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mip= i-i3c-hci/hci.h index 2110f806a53c..d9c9e609a879 100644 --- a/drivers/i3c/master/mipi-i3c-hci/hci.h +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h @@ -179,6 +179,7 @@ struct i3c_hci_dev_data { #define HCI_QUIRK_DMA_REQUIRES_HC_ABORT BIT(9) /* Use HC_CONTROL ABORT t= o abort DMA */ #define HCI_QUIRK_DAT_INDEX_IS_ADDR BIT(10) /* DAT entries are indexed by= device address */ #define HCI_QUIRK_DMA_64BIT BIT(11) /* Controller DMA supports 64-bit ad= dressing */ +#define HCI_QUIRK_TX_START_THLD BIT(12) /* Clear TX start threshold in D= MA mode */ =20 /* global functions */ void mipi_i3c_hci_resume(struct i3c_hci *hci); diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mip= i-i3c-hci/pio.c index 439578a6eb54..6bd5e43364a1 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -14,6 +14,7 @@ #include "hci.h" #include "cmd.h" #include "ibi.h" +#include "pio.h" =20 /* * PIO Access Area @@ -22,102 +23,6 @@ #define pio_reg_read(r) readl(hci->PIO_regs + (PIO_##r)) #define pio_reg_write(r, v) writel(v, hci->PIO_regs + (PIO_##r)) =20 -#define PIO_COMMAND_QUEUE_PORT 0x00 -#define PIO_RESPONSE_QUEUE_PORT 0x04 -#define PIO_XFER_DATA_PORT 0x08 -#define PIO_IBI_PORT 0x0c - -#define PIO_QUEUE_THLD_CTRL 0x10 -#define QUEUE_IBI_STATUS_THLD GENMASK(31, 24) -#define QUEUE_IBI_DATA_THLD GENMASK(23, 16) -#define QUEUE_RESP_BUF_THLD GENMASK(15, 8) -#define QUEUE_CMD_EMPTY_BUF_THLD GENMASK(7, 0) - -#define PIO_DATA_BUFFER_THLD_CTRL 0x14 -#define DATA_RX_START_THLD GENMASK(26, 24) -#define DATA_TX_START_THLD GENMASK(18, 16) -#define DATA_RX_BUF_THLD GENMASK(10, 8) -#define DATA_TX_BUF_THLD GENMASK(2, 0) - -#define PIO_QUEUE_SIZE 0x18 -#define TX_DATA_BUFFER_SIZE GENMASK(31, 24) -#define RX_DATA_BUFFER_SIZE GENMASK(23, 16) -#define IBI_STATUS_SIZE GENMASK(15, 8) -#define CR_QUEUE_SIZE GENMASK(7, 0) - -#define PIO_ALT_QUEUE_SIZE 0x1C -#define EXT_IBI_QUEUE_EN BIT(28) -#define ALT_RESP_QUEUE_EN BIT(24) -#define ALT_RESP_QUEUE_SIZE GENMASK(7, 0) - -#define PIO_INTR_STATUS 0x20 -#define PIO_INTR_STATUS_ENABLE 0x24 -#define PIO_INTR_SIGNAL_ENABLE 0x28 -#define PIO_INTR_FORCE 0x2c -#define STAT_TRANSFER_BLOCKED BIT(25) -#define STAT_PERR_RESP_UFLOW BIT(24) -#define STAT_PERR_CMD_OFLOW BIT(23) -#define STAT_PERR_IBI_UFLOW BIT(22) -#define STAT_PERR_RX_UFLOW BIT(21) -#define STAT_PERR_TX_OFLOW BIT(20) -#define STAT_ERR_RESP_QUEUE_FULL BIT(19) -#define STAT_WARN_RESP_QUEUE_FULL BIT(18) -#define STAT_ERR_IBI_QUEUE_FULL BIT(17) -#define STAT_WARN_IBI_QUEUE_FULL BIT(16) -#define STAT_ERR_RX_DATA_FULL BIT(15) -#define STAT_WARN_RX_DATA_FULL BIT(14) -#define STAT_ERR_TX_DATA_EMPTY BIT(13) -#define STAT_WARN_TX_DATA_EMPTY BIT(12) -#define STAT_TRANSFER_ERR BIT(9) -#define STAT_WARN_INS_STOP_MODE BIT(7) -#define STAT_TRANSFER_ABORT BIT(5) -#define STAT_RESP_READY BIT(4) -#define STAT_CMD_QUEUE_READY BIT(3) -#define STAT_IBI_STATUS_THLD BIT(2) -#define STAT_RX_THLD BIT(1) -#define STAT_TX_THLD BIT(0) - -#define PIO_CONTROL 0x30 -#define PIO_CONTROL_ABORT BIT(2) -#define PIO_CONTROL_RS BIT(1) -#define PIO_CONTROL_ENABLE BIT(0) - -#define PIO_QUEUE_CUR_STATUS 0x38 -#define CUR_IBI_Q_LEVEL GENMASK(28, 20) -#define CUR_RESP_Q_LEVEL GENMASK(18, 10) -#define CUR_CMD_Q_EMPTY_LEVEL GENMASK(8, 0) - -#define PIO_DATA_BUFFER_CUR_STATUS 0x3c -#define CUR_RX_BUF_LVL GENMASK(26, 16) -#define CUR_TX_BUF_LVL GENMASK(10, 0) - -/* - * Handy status bit combinations - */ - -#define STAT_LATENCY_WARNINGS (STAT_WARN_RESP_QUEUE_FULL | \ - STAT_WARN_IBI_QUEUE_FULL | \ - STAT_WARN_RX_DATA_FULL | \ - STAT_WARN_TX_DATA_EMPTY | \ - STAT_WARN_INS_STOP_MODE) - -#define STAT_LATENCY_ERRORS (STAT_ERR_RESP_QUEUE_FULL | \ - STAT_ERR_IBI_QUEUE_FULL | \ - STAT_ERR_RX_DATA_FULL | \ - STAT_ERR_TX_DATA_EMPTY) - -#define STAT_PROG_ERRORS (STAT_TRANSFER_BLOCKED | \ - STAT_PERR_RESP_UFLOW | \ - STAT_PERR_CMD_OFLOW | \ - STAT_PERR_IBI_UFLOW | \ - STAT_PERR_RX_UFLOW | \ - STAT_PERR_TX_OFLOW) - -#define STAT_ALL_ERRORS (STAT_TRANSFER_ABORT | \ - STAT_TRANSFER_ERR | \ - STAT_LATENCY_ERRORS | \ - STAT_PROG_ERRORS) - struct hci_pio_dev_ibi_data { struct i3c_generic_ibi_pool *pool; unsigned int max_len; diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.h b/drivers/i3c/master/mip= i-i3c-hci/pio.h new file mode 100644 index 000000000000..72318961e991 --- /dev/null +++ b/drivers/i3c/master/mipi-i3c-hci/pio.h @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2020, MIPI Alliance, Inc. + * + * Author: Nicolas Pitre + * + * PIO Access Area register and bitfield definitions. + */ + +#ifndef PIO_H +#define PIO_H + +#define PIO_COMMAND_QUEUE_PORT 0x00 +#define PIO_RESPONSE_QUEUE_PORT 0x04 +#define PIO_XFER_DATA_PORT 0x08 +#define PIO_IBI_PORT 0x0c + +#define PIO_QUEUE_THLD_CTRL 0x10 +#define QUEUE_IBI_STATUS_THLD GENMASK(31, 24) +#define QUEUE_IBI_DATA_THLD GENMASK(23, 16) +#define QUEUE_RESP_BUF_THLD GENMASK(15, 8) +#define QUEUE_CMD_EMPTY_BUF_THLD GENMASK(7, 0) + +#define PIO_DATA_BUFFER_THLD_CTRL 0x14 +#define DATA_TX_START_THLD GENMASK(18, 16) +#define DATA_RX_START_THLD GENMASK(26, 24) +#define DATA_RX_BUF_THLD GENMASK(10, 8) +#define DATA_TX_BUF_THLD GENMASK(2, 0) + +#define PIO_QUEUE_SIZE 0x18 +#define TX_DATA_BUFFER_SIZE GENMASK(31, 24) +#define RX_DATA_BUFFER_SIZE GENMASK(23, 16) +#define IBI_STATUS_SIZE GENMASK(15, 8) +#define CR_QUEUE_SIZE GENMASK(7, 0) + +#define PIO_ALT_QUEUE_SIZE 0x1C +#define EXT_IBI_QUEUE_EN BIT(28) +#define ALT_RESP_QUEUE_EN BIT(24) +#define ALT_RESP_QUEUE_SIZE GENMASK(7, 0) + +#define PIO_INTR_STATUS 0x20 +#define PIO_INTR_STATUS_ENABLE 0x24 +#define PIO_INTR_SIGNAL_ENABLE 0x28 +#define PIO_INTR_FORCE 0x2c +#define STAT_TRANSFER_BLOCKED BIT(25) +#define STAT_PERR_RESP_UFLOW BIT(24) +#define STAT_PERR_CMD_OFLOW BIT(23) +#define STAT_PERR_IBI_UFLOW BIT(22) +#define STAT_PERR_RX_UFLOW BIT(21) +#define STAT_PERR_TX_OFLOW BIT(20) +#define STAT_ERR_RESP_QUEUE_FULL BIT(19) +#define STAT_WARN_RESP_QUEUE_FULL BIT(18) +#define STAT_ERR_IBI_QUEUE_FULL BIT(17) +#define STAT_WARN_IBI_QUEUE_FULL BIT(16) +#define STAT_ERR_RX_DATA_FULL BIT(15) +#define STAT_WARN_RX_DATA_FULL BIT(14) +#define STAT_ERR_TX_DATA_EMPTY BIT(13) +#define STAT_WARN_TX_DATA_EMPTY BIT(12) +#define STAT_TRANSFER_ERR BIT(9) +#define STAT_WARN_INS_STOP_MODE BIT(7) +#define STAT_TRANSFER_ABORT BIT(5) +#define STAT_RESP_READY BIT(4) +#define STAT_CMD_QUEUE_READY BIT(3) +#define STAT_IBI_STATUS_THLD BIT(2) +#define STAT_RX_THLD BIT(1) +#define STAT_TX_THLD BIT(0) + +#define PIO_CONTROL 0x30 +#define PIO_CONTROL_ABORT BIT(2) +#define PIO_CONTROL_RS BIT(1) +#define PIO_CONTROL_ENABLE BIT(0) + +#define PIO_QUEUE_CUR_STATUS 0x38 +#define CUR_IBI_Q_LEVEL GENMASK(28, 20) +#define CUR_RESP_Q_LEVEL GENMASK(18, 10) +#define CUR_CMD_Q_EMPTY_LEVEL GENMASK(8, 0) + +#define PIO_DATA_BUFFER_CUR_STATUS 0x3c +#define CUR_RX_BUF_LVL GENMASK(26, 16) +#define CUR_TX_BUF_LVL GENMASK(10, 0) + +/* + * Handy status bit combinations + */ + +#define STAT_LATENCY_WARNINGS (STAT_WARN_RESP_QUEUE_FULL | \ + STAT_WARN_IBI_QUEUE_FULL | \ + STAT_WARN_RX_DATA_FULL | \ + STAT_WARN_TX_DATA_EMPTY | \ + STAT_WARN_INS_STOP_MODE) + +#define STAT_LATENCY_ERRORS (STAT_ERR_RESP_QUEUE_FULL | \ + STAT_ERR_IBI_QUEUE_FULL | \ + STAT_ERR_RX_DATA_FULL | \ + STAT_ERR_TX_DATA_EMPTY) + +#define STAT_PROG_ERRORS (STAT_TRANSFER_BLOCKED | \ + STAT_PERR_RESP_UFLOW | \ + STAT_PERR_CMD_OFLOW | \ + STAT_PERR_IBI_UFLOW | \ + STAT_PERR_RX_UFLOW | \ + STAT_PERR_TX_OFLOW) + +#define STAT_ALL_ERRORS (STAT_TRANSFER_ABORT | \ + STAT_TRANSFER_ERR | \ + STAT_LATENCY_ERRORS | \ + STAT_PROG_ERRORS) + +#endif --=20 2.34.1 From nobody Sat Sep 26 13:09:28 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (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 1096738AC7D; Tue, 1 Sep 2026 11:36:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262615; cv=none; b=alaVzmdxFg33Y/ObnT/tsli1x35OsfAMDnZmxV2LhyD6VGK95svYEXtePrn3TO7rQcn2qbwW6zDUj+lytsdySYW1FB6NKdWe/7lY7HEdavOU8GQpSHpSpXgNrDvkfLiZdXn8Yn8C+34CoFFcLiHan+yMHcBOw//lFyfmvTbyN4c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262615; c=relaxed/simple; bh=JZN8eVya+rZGkGFlbIyjIdl7gDlt+jOZTjurXHCPG1g=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=AmexQrAMXqoE2sgOCwQsCulH4r3d44x5eCuD7kS/H8vXrawitG4i5T1qr/SVw9oxQMYi/hRTqPA+T73As76+kv4JnigtkM/JWElxH0+nTuDJUfg7tjdKzR+6yb2tZy9SvRmBlKfMAKUylZRvtpW4t8PPApOrJTtpherNKgW/SFU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Tue, 1 Sep 2026 19:35:39 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Tue, 1 Sep 2026 19:35:39 +0800 From: Billy Tsai Date: Tue, 1 Sep 2026 19:35:32 +0800 Subject: [PATCH 5/8] i3c: mipi-i3c-hci: Add support for the AST2700 I3C controller 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: <20260901-b4-i3c-hci-ast2700-v1-5-19909e7cbd7e@aspeedtech.com> References: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> In-Reply-To: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> To: Alexandre Belloni , Frank Li , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Philipp Zabel CC: , , , , , Billy Tsai X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788262538; l=32011; i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id; bh=JZN8eVya+rZGkGFlbIyjIdl7gDlt+jOZTjurXHCPG1g=; b=ACV2ja0s6XQvi//NsSaN9wVoQV3sHwuu4XGopd5dhlVOQHUZ4dMG+u74OpoYgJSIBbNcbBT+L CN2b07gqGgsALz2hyaqweAmeTBXyECypH+8lYVz9wtoUZywT6cnn/Aa X-Developer-Key: i=billy_tsai@aspeedtech.com; a=ed25519; pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ= The AST2700 I3C controller carries an ASPEED vendor extended capability describing an in-house control block and a PHY programming window. Recognize the ASPEED MIPI vendor ID in the extended capability parser and cache both register bases via the generic vendor_data pointer, which other vendors (e.g. NXP) also populate; add is_aspeed() to identify ASPEED specifically. Bringing the controller up for transfers needs more than the generic HCI reset sequence: the vendor block has to be switched to master mode, PHY timing registers programmed from the selected bus rates (with aspeed,* device tree properties to override values derived under nominal bus loading), and all interrupts funneled through a vendor summary register whose handler dispatches to the same core and IO handlers as the generic path. Master clock stall is enabled alongside master-mode init so an underrun pauses and resumes the transfer instead of aborting it. Hook this initialization into bus setup and resume behind is_aspeed(), and acquire the core clock and reset-names resources the binding describes for it. During normal operation, the vendor DAA index registers must be told which DAT slot is being assigned during ENTDAA, and the PIO/IBI FIFOs need resetting after a DMA error or abort =E2=80=94 which the core already implements behind HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET, so set that quirk rather than open-coding a separate recovery path. With the required support in place, make the "aspeed,ast2700-i3c-hci" compatible matchable with the DAT_INDEX_IS_ADDR, DMA_64BIT, DMA_ABORT_REQUIRES_PIO_RESET and TX_START_THLD quirks, and set is_aspeed() from the same compatible. Signed-off-by: Billy Tsai Assisted-by: Claude:claude-fable-5 --- drivers/i3c/master/mipi-i3c-hci/Makefile | 2 +- drivers/i3c/master/mipi-i3c-hci/cmd_v1.c | 21 +++ drivers/i3c/master/mipi-i3c-hci/core.c | 175 ++++++++++++++++-- drivers/i3c/master/mipi-i3c-hci/ext_caps.c | 15 ++ drivers/i3c/master/mipi-i3c-hci/ext_caps.h | 1 + drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c | 234 ++++++++++++++++++++= ++++ drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h | 174 ++++++++++++++++++ 7 files changed, 606 insertions(+), 16 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/Makefile b/drivers/i3c/master/= mipi-i3c-hci/Makefile index e3d3ef757035f..f4f048786c95b 100644 --- a/drivers/i3c/master/mipi-i3c-hci/Makefile +++ b/drivers/i3c/master/mipi-i3c-hci/Makefile @@ -4,5 +4,5 @@ obj-$(CONFIG_MIPI_I3C_HCI) +=3D mipi-i3c-hci.o mipi-i3c-hci-y :=3D core.o ext_caps.o pio.o dma.o \ cmd_v1.o cmd_v2.o \ dat_v1.o dct_v1.o \ - hci_quirks.o + hci_quirks.o vendor_aspeed.o obj-$(CONFIG_MIPI_I3C_HCI_PCI) +=3D mipi-i3c-hci-pci.o diff --git a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c b/drivers/i3c/master/= mipi-i3c-hci/cmd_v1.c index 9a11affb14bf6..bf097d55f18cf 100644 --- a/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c +++ b/drivers/i3c/master/mipi-i3c-hci/cmd_v1.c @@ -14,6 +14,7 @@ #include "cmd.h" #include "dat.h" #include "dct.h" +#include "vendor_aspeed.h" =20 /* * Address Assignment Command @@ -293,6 +294,24 @@ static void hci_cmd_v1_prep_i2c_xfer(struct i3c_hci *h= ci, } } =20 +/* + * The DAA index registers name the DAT slot being assigned during ENTDAA; + * they live in the vendor capability block discovered at probe time. + */ +static void aspeed_i3c_set_daa_index(struct i3c_hci *hci, unsigned int dat= _idx) +{ + void __iomem *inhouse_regs =3D to_aspeed_vendor_data(hci)->inhouse_regs; + + if (dat_idx < 32) + writel(BIT(dat_idx), inhouse_regs + ASPEED_I3C_DAA_INDEX0); + else if (dat_idx < 64) + writel(BIT(dat_idx - 32), inhouse_regs + ASPEED_I3C_DAA_INDEX1); + else if (dat_idx < 96) + writel(BIT(dat_idx - 64), inhouse_regs + ASPEED_I3C_DAA_INDEX2); + else + writel(BIT(dat_idx - 96), inhouse_regs + ASPEED_I3C_DAA_INDEX3); +} + static int hci_cmd_v1_daa(struct i3c_hci *hci) { struct hci_xfer *xfer; @@ -322,6 +341,8 @@ static int hci_cmd_v1_daa(struct i3c_hci *hci) if (ret < 0) break; dat_idx =3D ret; + if (is_aspeed(hci)) + aspeed_i3c_set_daa_index(hci, dat_idx); =20 dev_dbg(&hci->master.dev, "next_addr =3D 0x%02x, DAA using DAT %d", diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mi= pi-i3c-hci/core.c index 2290a889701cd..e0ee8148aa2f8 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -20,6 +20,7 @@ #include #include #include +#include =20 #include "hci.h" #include "ext_caps.h" @@ -27,6 +28,7 @@ #include "dat.h" #include "ibi.h" #include "pio.h" +#include "vendor_aspeed.h" =20 /* * Host Controller Capabilities and Operation Registers @@ -136,6 +138,25 @@ struct device *i3c_hci_sysdev(struct device *dev) return dev->parent && dev_is_pci(dev->parent) ? dev->parent : dev; } =20 +static int aspeed_i3c_bus_setup(struct i3c_hci *hci) +{ + if (!to_aspeed_vendor_data(hci)->phy_regs) + return -ENODEV; + + /* + * Enable master clock stall: when the controller cannot keep the + * data pipeline fed it holds SCL low instead of underrunning, so a + * transfer pauses and resumes cleanly rather than aborting. + */ + aspeed_i3c_write(hci, ASPEED_I3C_CTRL, + ASPEED_I3C_CTRL_CLOCK_STALL_EN | + ASPEED_I3C_CTRL_INIT | + FIELD_PREP(ASPEED_I3C_CTRL_INIT_MODE, + ASPEED_I3C_INIT_MST_MODE)); + + return aspeed_i3c_phy_init(hci); +} + static void i3c_hci_set_master_dyn_addr(struct i3c_hci *hci) { reg_write(MASTER_DEVICE_ADDR, @@ -149,6 +170,12 @@ static int i3c_hci_bus_init(struct i3c_master_controll= er *m) struct i3c_device_info info; int ret; =20 + if (is_aspeed(hci)) { + ret =3D aspeed_i3c_bus_setup(hci); + if (ret) + return ret; + } + if (hci->cmd =3D=3D &mipi_i3c_hci_cmd_v1) { ret =3D mipi_i3c_hci_dat_v1.init(hci); if (ret) @@ -766,22 +793,12 @@ static const struct i3c_master_controller_ops i3c_hci= _ops =3D { .disable_hotjoin =3D i3c_hci_disable_hotjoin, }; =20 -static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) +static irqreturn_t i3c_hci_core_irq(struct i3c_hci *hci) +__must_hold(&hci->lock) { - struct i3c_hci *hci =3D dev_id; irqreturn_t result =3D IRQ_NONE; u32 val; =20 - guard(spinlock)(&hci->lock); - - /* - * The IRQ can be shared, so the handler may be called when the IRQ is - * due to a different device. That could happen when runtime suspended, - * so exit immediately if IRQs are not expected for this device. - */ - if (hci->irq_inactive) - return IRQ_NONE; - val =3D reg_read(INTR_STATUS); reg_write(INTR_STATUS, val); dev_dbg(&hci->master.dev, "INTR_STATUS %#x", val); @@ -804,12 +821,73 @@ static irqreturn_t i3c_hci_irq_handler(int irq, void = *dev_id) dev_warn_once(&hci->master.dev, "unexpected INTR_STATUS %#x\n", val); =20 + return result; +} + +static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id) +{ + irqreturn_t result =3D IRQ_NONE; + struct i3c_hci *hci =3D dev_id; + + guard(spinlock)(&hci->lock); + + /* + * The IRQ can be shared, so the handler may be called when the IRQ is + * due to a different device. That could happen when runtime suspended, + * so exit immediately if IRQs are not expected for this device. + */ + if (hci->irq_inactive) + return IRQ_NONE; + + result =3D i3c_hci_core_irq(hci); + if (hci->io->irq_handler(hci)) result =3D IRQ_HANDLED; =20 return result; } =20 +static irqreturn_t i3c_aspeed_irq_handler(int irq, void *dev_id) +{ + struct i3c_hci *hci =3D dev_id; + u32 status, inhouse_status; + + guard(spinlock)(&hci->lock); + + if (hci->irq_inactive) + return IRQ_NONE; + + /* + * The vendor summary register ORs together the core HCI status + * (CAP), the PIO/DMA ring status (PIO/RHS) and the in-house block's + * own status (INHOUSE) into single bits, so each source is + * dispatched to the handler that knows how to service it. + */ + status =3D aspeed_i3c_read(hci, ASPEED_I3C_INTR_SUM_STATUS); + if (!status) + return IRQ_NONE; + + if (status & ASPEED_INTR_SUM_CAP) + i3c_hci_core_irq(hci); + if (status & (ASPEED_INTR_SUM_PIO | ASPEED_INTR_SUM_RHS)) + hci->io->irq_handler(hci); + if (status & ASPEED_INTR_SUM_INHOUSE) { + /* + * The in-house block has no dedicated handler; just + * acknowledge its status bits so the summary IRQ clears. + */ + inhouse_status =3D aspeed_i3c_read(hci, ASPEED_I3C_INTR_STATUS); + aspeed_i3c_write(hci, ASPEED_I3C_INTR_STATUS, inhouse_status); + } + + /* + * Unlike i3c_hci_irq_handler(), this IRQ is not shared + * (IRQF_SHARED is not passed at request_irq() time), so a nonzero + * summary status is always ours to handle. + */ + return IRQ_HANDLED; +} + static inline bool is_version_1_1_or_newer(struct i3c_hci *hci) { return hci->version_major > 1 || (hci->version_major =3D=3D 1 && hci->ver= sion_minor > 0); @@ -866,6 +944,11 @@ static int i3c_hci_reset_and_init(struct i3c_hci *hci) * Bit 0:5 are defined in IP version < 0.8 but not handled by PIO code */ reg_write(INTR_STATUS_ENABLE, GENMASK(31, 10)); + if (is_aspeed(hci)) { + aspeed_i3c_write(hci, ASPEED_I3C_INTR_SIGNAL_ENABLE, 0); + aspeed_i3c_write(hci, ASPEED_I3C_INTR_STATUS_ENABLE, + GENMASK(31, 0)); + } =20 /* Make sure our data ordering fits the host's */ regval =3D reg_read(HC_CONTROL); @@ -949,6 +1032,12 @@ static int i3c_hci_do_reset_and_restore(struct i3c_hc= i *hci) if (ret) return -EIO; =20 + if (is_aspeed(hci)) { + ret =3D aspeed_i3c_bus_setup(hci); + if (ret) + return ret; + } + i3c_hci_set_master_dyn_addr(hci); =20 mipi_i3c_hci_dat_v1.restore(hci); @@ -1146,10 +1235,17 @@ static int i3c_hci_init(struct i3c_hci *hci) return i3c_hci_reset_and_init(hci); } =20 +static void i3c_hci_dma_rst_assert(void *data) +{ + reset_control_assert(data); +} + static int i3c_hci_probe(struct platform_device *pdev) { const struct mipi_i3c_hci_platform_data *pdata =3D pdev->dev.platform_dat= a; struct clk_bulk_data *clks; + irq_handler_t irq_handler; + unsigned long irq_flags; struct i3c_hci *hci; int irq, ret; =20 @@ -1176,8 +1272,12 @@ static int i3c_hci_probe(struct platform_device *pde= v) } =20 platform_set_drvdata(pdev, hci); - /* temporary for dev_printk's, to be replaced in i3c_master_register */ + /* + * Temporary for dev_printk's and is_aspeed(), both replaced with + * the real values by i3c_master_register(). + */ hci->master.dev.init_name =3D dev_name(&pdev->dev); + hci->master.dev.of_node =3D pdev->dev.of_node; =20 hci->quirks =3D (unsigned long)device_get_match_data(&pdev->dev); if (!hci->quirks && platform_get_device_id(pdev)) @@ -1189,6 +1289,40 @@ static int i3c_hci_probe(struct platform_device *pde= v) "cannot set DMA mask\n"); } =20 + if (is_aspeed(hci)) { + struct aspeed_i3c_vendor_data *vd; + + vd =3D devm_kzalloc(&pdev->dev, sizeof(*vd), GFP_KERNEL); + if (!vd) + return -ENOMEM; + hci->vendor_data =3D vd; + + vd->rst =3D devm_reset_control_get_optional_exclusive(&pdev->dev, "core"= ); + if (IS_ERR(vd->rst)) + return PTR_ERR(vd->rst); + ret =3D reset_control_deassert(vd->rst); + if (ret) + return ret; + + vd->dma_rst =3D devm_reset_control_get_optional_shared(&pdev->dev, "dma"= ); + if (IS_ERR(vd->dma_rst)) + return PTR_ERR(vd->dma_rst); + if (vd->dma_rst) { + ret =3D reset_control_deassert(vd->dma_rst); + if (ret) + return ret; + ret =3D devm_add_action_or_reset(&pdev->dev, + i3c_hci_dma_rst_assert, + vd->dma_rst); + if (ret) + return ret; + } + + vd->clk =3D devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(vd->clk)) + return PTR_ERR(vd->clk); + } + ret =3D devm_clk_bulk_get_all_enabled(&pdev->dev, &clks); if (ret < 0) return dev_err_probe(&pdev->dev, ret, @@ -1200,9 +1334,15 @@ static int i3c_hci_probe(struct platform_device *pde= v) =20 hci->irq_inactive =3D true; =20 + irq_handler =3D i3c_hci_irq_handler; + irq_flags =3D IRQF_SHARED; + if (is_aspeed(hci)) { + irq_handler =3D i3c_aspeed_irq_handler; + irq_flags =3D 0; + } + irq =3D platform_get_irq(pdev, 0); - ret =3D devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler, - IRQF_SHARED, NULL, hci); + ret =3D devm_request_irq(&pdev->dev, irq, irq_handler, irq_flags, NULL, h= ci); if (ret) return ret; =20 @@ -1230,6 +1370,11 @@ static const __maybe_unused struct of_device_id i3c_= hci_of_match[] =3D { { .compatible =3D "microchip,sama7d65-i3c-hci", .data =3D (void *)(ulong)(HCI_QUIRK_PIO_MODE | HCI_QUIRK_OD_PP_TIMING | HCI_QUIRK_RESP_BUF_THLD) }, + { .compatible =3D "aspeed,ast2700-i3c-hci", + .data =3D (void *)(ulong)(HCI_QUIRK_DAT_INDEX_IS_ADDR | + HCI_QUIRK_DMA_64BIT | + HCI_QUIRK_DMA_ABORT_REQUIRES_PIO_RESET | + HCI_QUIRK_TX_START_THLD) }, {}, }; MODULE_DEVICE_TABLE(of, i3c_hci_of_match); diff --git a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c b/drivers/i3c/maste= r/mipi-i3c-hci/ext_caps.c index 77840fd4aa51f..b97306d498715 100644 --- a/drivers/i3c/master/mipi-i3c-hci/ext_caps.c +++ b/drivers/i3c/master/mipi-i3c-hci/ext_caps.c @@ -14,6 +14,7 @@ =20 #include "hci.h" #include "ext_caps.h" +#include "vendor_aspeed.h" #include "xfer_mode_rate.h" =20 /* Extended Capability Header */ @@ -207,6 +208,19 @@ static int hci_extcap_vendor_NXP(struct i3c_hci *hci, = void __iomem *base) return 0; } =20 +static int hci_extcap_vendor_ASPEED(struct i3c_hci *hci, void __iomem *bas= e) +{ + struct aspeed_i3c_vendor_data *vd =3D to_aspeed_vendor_data(hci); + + if (!vd) + return 0; + + vd->inhouse_regs =3D hci->base_regs + readl(base + 0x04); + vd->phy_regs =3D hci->base_regs + readl(base + 0x08); + + return 0; +} + struct hci_ext_cap_vendor_specific { u32 vendor; u8 cap; @@ -221,6 +235,7 @@ struct hci_ext_cap_vendor_specific { =20 static const struct hci_ext_cap_vendor_specific vendor_ext_caps[] =3D { EXT_CAP_VENDOR(NXP, 0xc0, 0x20), + EXT_CAP_VENDOR(ASPEED, 0xc0, 0x08), }; =20 static int hci_extcap_vendor_specific(struct i3c_hci *hci, void __iomem *b= ase, diff --git a/drivers/i3c/master/mipi-i3c-hci/ext_caps.h b/drivers/i3c/maste= r/mipi-i3c-hci/ext_caps.h index b15e629951f03..46dd83c0d4c54 100644 --- a/drivers/i3c/master/mipi-i3c-hci/ext_caps.h +++ b/drivers/i3c/master/mipi-i3c-hci/ext_caps.h @@ -12,6 +12,7 @@ =20 /* MIPI vendor IDs */ #define MIPI_VENDOR_NXP 0x11b +#define MIPI_VENDOR_ASPEED 0x3f6 =20 int i3c_hci_parse_ext_caps(struct i3c_hci *hci); =20 diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c b/drivers/i3c/= master/mipi-i3c-hci/vendor_aspeed.c new file mode 100644 index 0000000000000..b521fce4c084a --- /dev/null +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2026 ASPEED Technology Inc. + * + * AST2700 vendor register access and PHY timing initialization. + */ + +#include +#include +#include +#include +#include +#include + +#include "hci.h" +#include "vendor_aspeed.h" + +u32 aspeed_i3c_read(struct i3c_hci *hci, u32 reg) +{ + return readl(to_aspeed_vendor_data(hci)->inhouse_regs + reg); +} + +void aspeed_i3c_write(struct i3c_hci *hci, u32 reg, u32 val) +{ + writel(val, to_aspeed_vendor_data(hci)->inhouse_regs + reg); +} + +static void aspeed_i3c_phy_write(struct i3c_hci *hci, u32 reg, u32 val) +{ + writel(val, to_aspeed_vendor_data(hci)->phy_regs + reg); +} + +static u16 aspeed_i3c_ns_to_cnt(unsigned int ns, unsigned long period_ns) +{ + unsigned long cycles =3D DIV_ROUND_CLOSEST(ns, period_ns); + + /* All PHY counter fields are 11 bits wide */ + return min_t(unsigned long, max_t(unsigned long, cycles, 1) - 1, + ASPEED_I3C_PHY_CNT_MAX); +} + +static u32 aspeed_i3c_get_sdr_ctrl0_reg(struct i3c_hci *hci) +{ + struct i3c_bus *bus =3D i3c_master_get_bus(&hci->master); + + if (bus->scl_rate.i3c > 8000000) + return ASPEED_I3C_PHY_I3C_SDR0_CTRL0; + if (bus->scl_rate.i3c > 6000000) + return ASPEED_I3C_PHY_I3C_SDR1_CTRL0; + if (bus->scl_rate.i3c > 4000000) + return ASPEED_I3C_PHY_I3C_SDR2_CTRL0; + if (bus->scl_rate.i3c > 2000000) + return ASPEED_I3C_PHY_I3C_SDR3_CTRL0; + + return ASPEED_I3C_PHY_I3C_SDR4_CTRL0; +} + +int aspeed_i3c_phy_init(struct i3c_hci *hci) +{ + u16 hcnt, lcnt, total_cnt, min_tbit_cnt, cas_lcnt, cas_cnt, cbp_cnt; + u32 sda_tx_hold =3D 0, cas_ns =3D 0, cbp_ns =3D 0, sr_p_low =3D 0; + struct i3c_bus *bus =3D i3c_master_get_bus(&hci->master); + u32 pp_high =3D 0, pp_low =3D 0, od_high =3D 0, od_low =3D 0; + struct device_node *np =3D hci->master.dev.of_node; + u32 sdr_ctrl0_reg, ctrl0, ctrl1, ctrl2; + unsigned long core_rate, period_ns; + + core_rate =3D clk_get_rate(to_aspeed_vendor_data(hci)->clk); + if (!core_rate) { + dev_err(&hci->master.dev, "invalid core clock rate\n"); + return -EINVAL; + } + + period_ns =3D DIV_ROUND_UP(NSEC_PER_SEC, core_rate); + + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FM_CAS_NS, period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FM_SU_STO_NS, period_ns); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I2C_FM_CTRL0, + FIELD_PREP(ASPEED_I3C_PHY_I2C_FM_CTRL0_CAS, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I2C_FM_CTRL0_SU_STO, lcnt)); + + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FM_SCL_H_NS, period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FM_SCL_L_NS, period_ns); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I2C_FM_CTRL1, + FIELD_PREP(ASPEED_I3C_PHY_I2C_FM_CTRL1_SCL_H, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I2C_FM_CTRL1_SCL_L, lcnt)); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I2C_FM_CTRL2, + FIELD_PREP(ASPEED_I3C_PHY_I2C_FM_CTRL2_ACK_H, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I2C_FM_CTRL2_ACK_L, hcnt)); + + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FM_HD_DAT_NS, period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FM_AHD_DAT_NS, period_ns= ); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I2C_FM_CTRL3, + FIELD_PREP(ASPEED_I3C_PHY_I2C_FM_CTRL3_HD_DAT, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I2C_FM_CTRL3_AHD_DAT, lcnt)); + + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FMP_CAS_NS, period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FMP_SU_STO_NS, period_ns= ); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I2C_FMP_CTRL0, + FIELD_PREP(ASPEED_I3C_PHY_I2C_FMP_CTRL0_CAS, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I2C_FMP_CTRL0_SU_STO, lcnt)); + + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FMP_SCL_H_NS, period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FMP_SCL_L_NS, period_ns); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I2C_FMP_CTRL1, + FIELD_PREP(ASPEED_I3C_PHY_I2C_FMP_CTRL1_SCL_H, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I2C_FMP_CTRL1_SCL_L, lcnt)); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I2C_FMP_CTRL2, + FIELD_PREP(ASPEED_I3C_PHY_I2C_FMP_CTRL2_ACK_H, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I2C_FMP_CTRL2_ACK_L, hcnt)); + + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FMP_HD_DAT_NS, period_ns= ); + lcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FMP_AHD_DAT_NS, period_n= s); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I2C_FMP_CTRL3, + FIELD_PREP(ASPEED_I3C_PHY_I2C_FMP_CTRL3_HD_DAT, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I2C_FMP_CTRL3_AHD_DAT, lcnt)); + + of_property_read_u32(np, "aspeed,pp-scl-high-ns", &pp_high); + of_property_read_u32(np, "aspeed,pp-scl-low-ns", &pp_low); + if (pp_high && pp_low) { + hcnt =3D aspeed_i3c_ns_to_cnt(pp_high, period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(pp_low, period_ns); + } else { + /* + * Each of hcnt/lcnt is programmed as N-1 (the PHY counts N + * cycles for a stored value of N-1), so pre-subtract 2 here + * to cancel the two implicit +1s and match the requested SCL + * period exactly. Clamp the pre-subtraction value so + * total_cnt cannot underflow when an unrealistically high + * scl_rate leaves less than 3 cycles per period. + */ + unsigned long cycles_per_scl =3D DIV_ROUND_UP(core_rate, bus->scl_rate.i= 3c); + + total_cnt =3D max_t(unsigned long, cycles_per_scl, 3) - 2; + if (hci->master.bus.mode =3D=3D I3C_BUS_MODE_PURE) { + hcnt =3D DIV_ROUND_DOWN_ULL(total_cnt * 2, 5); + lcnt =3D total_cnt - hcnt; + } else { + hcnt =3D DIV_ROUND_UP(I3C_BUS_THIGH_MIXED_MAX_NS, + period_ns) - 1; + lcnt =3D total_cnt - hcnt; + } + } + + ctrl0 =3D FIELD_PREP(ASPEED_I3C_PHY_I3C_CTRL0_SCL_H, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I3C_CTRL0_SCL_L, lcnt); + sdr_ctrl0_reg =3D aspeed_i3c_get_sdr_ctrl0_reg(hci); + aspeed_i3c_phy_write(hci, sdr_ctrl0_reg, ctrl0); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_SDR0_CTRL0, ctrl0); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_DDR_CTRL0, ctrl0); + + min_tbit_cnt =3D DIV_ROUND_UP(60, period_ns) - 1; + ctrl1 =3D FIELD_PREP(ASPEED_I3C_PHY_I3C_CTRL1_TBIT_H, + max_t(u16, hcnt, min_tbit_cnt)) | + FIELD_PREP(ASPEED_I3C_PHY_I3C_CTRL1_TBIT_L, + max_t(u16, lcnt, min_tbit_cnt)); + aspeed_i3c_phy_write(hci, sdr_ctrl0_reg + ASPEED_I3C_PHY_CTRL1_OFFSET, + ctrl1); + aspeed_i3c_phy_write(hci, + ASPEED_I3C_PHY_I3C_SDR0_CTRL0 + + ASPEED_I3C_PHY_CTRL1_OFFSET, ctrl1); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_DDR_CTRL1, ctrl1); + + /* + * tCAS and tCBP default to a value derived from the SCL low period + * of the applicable bus context, clamped to the MIPI I3C minima + * (tCAS >=3D 38.4 ns, tCBP >=3D 19.2 ns). Explicit device tree values + * are used as-is; the binding carries the minima as constraints. + */ + if (hci->master.bus.mode =3D=3D I3C_BUS_MODE_PURE) + cas_lcnt =3D lcnt; + else + cas_lcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_I2C_FM_SCL_L_NS, + period_ns); + of_property_read_u32(np, "aspeed,cas-ns", &cas_ns); + of_property_read_u32(np, "aspeed,cbp-ns", &cbp_ns); + cas_cnt =3D cas_ns ? aspeed_i3c_ns_to_cnt(cas_ns, period_ns) + : max_t(u16, cas_lcnt, + DIV_ROUND_UP(ASPEED_I3C_PHY_OD_MIN_CAS_NS_X10, + period_ns * 10) - 1); + cbp_cnt =3D cbp_ns ? aspeed_i3c_ns_to_cnt(cbp_ns, period_ns) + : max_t(u16, cas_lcnt, + DIV_ROUND_UP(ASPEED_I3C_PHY_OD_MIN_CBP_NS_X10, + period_ns * 10) - 1); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_OD_CTRL0, + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL0_CAS, cas_cnt) | + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL0_CBP, cbp_cnt)); + + of_property_read_u32(np, "aspeed,od-scl-high-ns", &od_high); + of_property_read_u32(np, "aspeed,od-scl-low-ns", &od_low); + hcnt =3D aspeed_i3c_ns_to_cnt(od_high ?: ASPEED_I3C_PHY_OD_SCL_H_NS, + period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(od_low ?: ASPEED_I3C_PHY_OD_SCL_L_NS, + period_ns); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_OD_CTRL1, + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL1_SCL_H, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL1_SCL_L, lcnt)); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_OD_CTRL2, + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL2_ACK_H, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL2_ACK_L, lcnt)); + + of_property_read_u32(np, "aspeed,sda-tx-hold-ns", &sda_tx_hold); + hcnt =3D aspeed_i3c_ns_to_cnt(sda_tx_hold ?: ASPEED_I3C_PHY_OD_HD_DAT_NS, + period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(sda_tx_hold ?: ASPEED_I3C_PHY_OD_AHD_DAT_NS, + period_ns); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_OD_CTRL3, + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL3_HD_DAT, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL3_AHD_DAT, lcnt)); + ctrl2 =3D FIELD_PREP(ASPEED_I3C_PHY_I3C_CTRL2_HD_PP, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_I3C_CTRL2_TBIT_HD_PP, lcnt); + aspeed_i3c_phy_write(hci, sdr_ctrl0_reg + ASPEED_I3C_PHY_CTRL2_OFFSET, + ctrl2); + aspeed_i3c_phy_write(hci, + ASPEED_I3C_PHY_I3C_SDR0_CTRL0 + + ASPEED_I3C_PHY_CTRL2_OFFSET, ctrl2); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_DDR_CTRL2, ctrl2); + + of_property_read_u32(np, "aspeed,sr-p-scl-low-ns", &sr_p_low); + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_SR_P_HD_NS, period_ns); + lcnt =3D aspeed_i3c_ns_to_cnt(sr_p_low ?: ASPEED_I3C_PHY_SR_P_SCL_L_NS, + period_ns); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_SR_P_PREPARE_CTRL, + FIELD_PREP(ASPEED_I3C_PHY_SR_P_PREPARE_HD, hcnt) | + FIELD_PREP(ASPEED_I3C_PHY_SR_P_PREPARE_SCL_L, lcnt)); + + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_PULLUP_EN, 0); + + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_OD_DAP_NS, period_ns); + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_OD_CTRL4, + FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL4_DAP, hcnt)); + + return 0; +} diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/= master/mipi-i3c-hci/vendor_aspeed.h new file mode 100644 index 0000000000000..8760384d1c6f5 --- /dev/null +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2026 ASPEED Technology Inc. + * + * AST2700 specific MIPI I3C HCI definitions + */ + +#ifndef VENDOR_ASPEED_H +#define VENDOR_ASPEED_H + +#include +#include + +#include "ext_caps.h" + +struct clk; +struct reset_control; + +/* + * The AST2700 vendor extended capability points to an in-house control + * block and a PHY programming window inside the controller's register + * space. The core clock and reset lines are only specified in the + * AST2700 binding as well. All of it is ASPEED-specific, so it is kept + * out of the generic struct i3c_hci and reached instead through its + * vendor_data pointer. + */ +struct aspeed_i3c_vendor_data { + void __iomem *inhouse_regs; + void __iomem *phy_regs; + struct reset_control *rst; + struct reset_control *dma_rst; + struct clk *clk; +}; + +/* + * hci->master.dev.of_node is only valid once i3c_master_register() has + * run device_set_node() on it; probe() pre-populates it before that + * point (see i3c_hci_probe()) so this works from early init onward too. + */ +static inline bool is_aspeed(struct i3c_hci *hci) +{ + return of_device_is_compatible(hci->master.dev.of_node, + "aspeed,ast2700-i3c-hci"); +} + +static inline struct aspeed_i3c_vendor_data *to_aspeed_vendor_data(struct = i3c_hci *hci) +{ + return hci->vendor_data; +} + +u32 aspeed_i3c_read(struct i3c_hci *hci, u32 reg); +void aspeed_i3c_write(struct i3c_hci *hci, u32 reg, u32 val); +int aspeed_i3c_phy_init(struct i3c_hci *hci); + +#define ASPEED_I3C_CTRL 0x00 +#define ASPEED_I3C_CTRL_CLOCK_STALL_EN BIT(14) +#define ASPEED_I3C_CTRL_INIT BIT(4) +#define ASPEED_I3C_CTRL_INIT_MODE GENMASK(1, 0) +#define ASPEED_I3C_INIT_MST_MODE 0 + +#define ASPEED_I3C_DAA_INDEX0 0x10 +#define ASPEED_I3C_DAA_INDEX1 0x14 +#define ASPEED_I3C_DAA_INDEX2 0x18 +#define ASPEED_I3C_DAA_INDEX3 0x1c + +#define ASPEED_I3C_INTR_STATUS 0xe0 +#define ASPEED_I3C_INTR_STATUS_ENABLE 0xe4 +#define ASPEED_I3C_INTR_SIGNAL_ENABLE 0xe8 +#define ASPEED_I3C_INTR_SUM_STATUS 0xf0 +#define ASPEED_INTR_SUM_INHOUSE BIT(3) +#define ASPEED_INTR_SUM_RHS BIT(2) +#define ASPEED_INTR_SUM_PIO BIT(1) +#define ASPEED_INTR_SUM_CAP BIT(0) + +#define ASPEED_I3C_PHY_I2C_FM_CTRL0 0x08 +#define ASPEED_I3C_PHY_I2C_FM_CTRL0_CAS GENMASK(26, 16) +#define ASPEED_I3C_PHY_I2C_FM_CTRL0_SU_STO GENMASK(10, 0) +#define ASPEED_I3C_PHY_I2C_FM_CTRL1 0x0c +#define ASPEED_I3C_PHY_I2C_FM_CTRL1_SCL_H GENMASK(26, 16) +#define ASPEED_I3C_PHY_I2C_FM_CTRL1_SCL_L GENMASK(10, 0) +#define ASPEED_I3C_PHY_I2C_FM_CTRL2 0x10 +#define ASPEED_I3C_PHY_I2C_FM_CTRL2_ACK_H GENMASK(26, 16) +#define ASPEED_I3C_PHY_I2C_FM_CTRL2_ACK_L GENMASK(10, 0) +#define ASPEED_I3C_PHY_I2C_FM_CTRL3 0x14 +#define ASPEED_I3C_PHY_I2C_FM_CTRL3_HD_DAT GENMASK(26, 16) +#define ASPEED_I3C_PHY_I2C_FM_CTRL3_AHD_DAT GENMASK(10, 0) + +#define ASPEED_I3C_PHY_I2C_FMP_CTRL0 0x18 +#define ASPEED_I3C_PHY_I2C_FMP_CTRL0_CAS GENMASK(26, 16) +#define ASPEED_I3C_PHY_I2C_FMP_CTRL0_SU_STO GENMASK(10, 0) +#define ASPEED_I3C_PHY_I2C_FMP_CTRL1 0x1c +#define ASPEED_I3C_PHY_I2C_FMP_CTRL1_SCL_H GENMASK(26, 16) +#define ASPEED_I3C_PHY_I2C_FMP_CTRL1_SCL_L GENMASK(10, 0) +#define ASPEED_I3C_PHY_I2C_FMP_CTRL2 0x20 +#define ASPEED_I3C_PHY_I2C_FMP_CTRL2_ACK_H GENMASK(26, 16) +#define ASPEED_I3C_PHY_I2C_FMP_CTRL2_ACK_L GENMASK(10, 0) +#define ASPEED_I3C_PHY_I2C_FMP_CTRL3 0x24 +#define ASPEED_I3C_PHY_I2C_FMP_CTRL3_HD_DAT GENMASK(26, 16) +#define ASPEED_I3C_PHY_I2C_FMP_CTRL3_AHD_DAT GENMASK(10, 0) + +#define ASPEED_I3C_PHY_I3C_OD_CTRL0 0x28 +#define ASPEED_I3C_PHY_I3C_OD_CTRL0_CAS GENMASK(26, 16) +#define ASPEED_I3C_PHY_I3C_OD_CTRL0_CBP GENMASK(10, 0) +#define ASPEED_I3C_PHY_I3C_OD_CTRL1 0x2c +#define ASPEED_I3C_PHY_I3C_OD_CTRL1_SCL_H GENMASK(26, 16) +#define ASPEED_I3C_PHY_I3C_OD_CTRL1_SCL_L GENMASK(10, 0) +#define ASPEED_I3C_PHY_I3C_OD_CTRL2 0x30 +#define ASPEED_I3C_PHY_I3C_OD_CTRL2_ACK_H GENMASK(26, 16) +#define ASPEED_I3C_PHY_I3C_OD_CTRL2_ACK_L GENMASK(10, 0) +#define ASPEED_I3C_PHY_I3C_OD_CTRL3 0x34 +#define ASPEED_I3C_PHY_I3C_OD_CTRL3_HD_DAT GENMASK(26, 16) +#define ASPEED_I3C_PHY_I3C_OD_CTRL3_AHD_DAT GENMASK(10, 0) +#define ASPEED_I3C_PHY_I3C_OD_CTRL4 0xd8 +#define ASPEED_I3C_PHY_I3C_OD_CTRL4_DAP GENMASK(26, 16) + +#define ASPEED_I3C_PHY_I3C_SDR0_CTRL0 0x38 +#define ASPEED_I3C_PHY_I3C_SDR1_CTRL0 0x44 +#define ASPEED_I3C_PHY_I3C_SDR2_CTRL0 0x50 +#define ASPEED_I3C_PHY_I3C_SDR3_CTRL0 0x5c +#define ASPEED_I3C_PHY_I3C_SDR4_CTRL0 0x68 +#define ASPEED_I3C_PHY_I3C_CTRL0_SCL_H GENMASK(26, 16) +#define ASPEED_I3C_PHY_I3C_CTRL0_SCL_L GENMASK(10, 0) +#define ASPEED_I3C_PHY_I3C_CTRL1_TBIT_H GENMASK(26, 16) +#define ASPEED_I3C_PHY_I3C_CTRL1_TBIT_L GENMASK(10, 0) +#define ASPEED_I3C_PHY_I3C_CTRL2_HD_PP GENMASK(26, 16) +#define ASPEED_I3C_PHY_I3C_CTRL2_TBIT_HD_PP GENMASK(10, 0) + +#define ASPEED_I3C_PHY_I3C_DDR_CTRL0 0x74 +#define ASPEED_I3C_PHY_I3C_DDR_CTRL1 0x78 +#define ASPEED_I3C_PHY_I3C_DDR_CTRL2 0x7c + +#define ASPEED_I3C_PHY_SR_P_PREPARE_CTRL 0x80 +#define ASPEED_I3C_PHY_SR_P_PREPARE_HD GENMASK(26, 16) +#define ASPEED_I3C_PHY_SR_P_PREPARE_SCL_L GENMASK(10, 0) + +#define ASPEED_I3C_PHY_PULLUP_EN 0x98 + +#define ASPEED_I3C_PHY_CTRL1_OFFSET 0x04 +#define ASPEED_I3C_PHY_CTRL2_OFFSET 0x08 + +#define ASPEED_I3C_PHY_CNT_MAX GENMASK(10, 0) + +#define ASPEED_I3C_PHY_I2C_FM_CAS_NS 1130 +#define ASPEED_I3C_PHY_I2C_FM_SU_STO_NS 1370 +#define ASPEED_I3C_PHY_I2C_FM_SCL_H_NS 1130 +#define ASPEED_I3C_PHY_I2C_FM_SCL_L_NS 1370 +#define ASPEED_I3C_PHY_I2C_FM_HD_DAT_NS 10 +#define ASPEED_I3C_PHY_I2C_FM_AHD_DAT_NS 10 + +#define ASPEED_I3C_PHY_I2C_FMP_CAS_NS 380 +#define ASPEED_I3C_PHY_I2C_FMP_SU_STO_NS 620 +#define ASPEED_I3C_PHY_I2C_FMP_SCL_H_NS 380 +#define ASPEED_I3C_PHY_I2C_FMP_SCL_L_NS 620 +#define ASPEED_I3C_PHY_I2C_FMP_HD_DAT_NS 10 +#define ASPEED_I3C_PHY_I2C_FMP_AHD_DAT_NS 10 + +#define ASPEED_I3C_PHY_OD_SCL_H_NS 380 +#define ASPEED_I3C_PHY_OD_SCL_L_NS 620 +#define ASPEED_I3C_PHY_OD_HD_DAT_NS 10 +#define ASPEED_I3C_PHY_OD_AHD_DAT_NS 10 +#define ASPEED_I3C_PHY_OD_DAP_NS 12 + +#define ASPEED_I3C_PHY_SR_P_HD_NS 16 +#define ASPEED_I3C_PHY_SR_P_SCL_L_NS 40 + +/* + * MIPI I3C minimum timing for the open-drain start/stop, in tenths of a + * nanosecond to keep the fractional spec values (tCAS >=3D 38.4 ns, + * tCBP >=3D 19.2 ns) in integer arithmetic. + */ +#define ASPEED_I3C_PHY_OD_MIN_CAS_NS_X10 384 +#define ASPEED_I3C_PHY_OD_MIN_CBP_NS_X10 192 + +#endif --=20 2.34.1 From nobody Sat Sep 26 13:09:28 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (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 3CBB536196C; Tue, 1 Sep 2026 11:36:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262617; cv=none; b=qIEhRP8u7SdQh+bBXm6jVHRdX3ah9BoZ6E458Rwh3nCNHXajDkzf8O5HtTETLuX9e+Ahx/UQKuN6Iwf7n3iEa5bS5cxuZNAi/rRUrfZ6G7fZ8pngLNI1ZSLRRWJi2m2SQWQ1Axj5AaNB4nrLHbqM5PgNqep6JlNM48RA/kVz/kU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262617; c=relaxed/simple; bh=xOl9+Kul4dBwt8rxRnAINY0vnOHwaByTCuM9DbK+p3I=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=dihZLShqZJ0mVRYerGczu8cGMJz7O2UYYrkvPcwG3vWDuF+1NdGdL6jXVQ2JOsg82MspYW0c7CXfZKJ8rOmgZgMiXNUnCROBYX0bvgRuGcgBtUTERE4sbBcoTuun9gOx+njVloYOtHojG/KS4pQpqaljEpSiPDX7Zrjdf1hj53U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Tue, 1 Sep 2026 19:35:39 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Tue, 1 Sep 2026 19:35:39 +0800 From: Billy Tsai Date: Tue, 1 Sep 2026 19:35:33 +0800 Subject: [PATCH 6/8] i3c: mipi-i3c-hci: Program AST2700 IBI termination threshold 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: <20260901-b4-i3c-hci-ast2700-v1-6-19909e7cbd7e@aspeedtech.com> References: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> In-Reply-To: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> To: Alexandre Belloni , Frank Li , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Philipp Zabel CC: , , , , , Billy Tsai X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788262538; l=4578; i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id; bh=xOl9+Kul4dBwt8rxRnAINY0vnOHwaByTCuM9DbK+p3I=; b=qWtlv5VOUEegPE0YGyLI/BFbXn5JqLuelcacCgbJoGHuQrR+O8yF30Ewl63FfhKPpWughtyAl CyKmYJFamIQD4ibRMn+c7kGx+ZNZ05JfxmCg1tulc3aHN/RrVyURlUY X-Developer-Key: i=billy_tsai@aspeedtech.com; a=ed25519; pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ= For an IBI with payload, it's normally the device that decides when to stop sending payload bytes. A misbehaving device that never terminates can leave the controller stuck waiting on the transfer. AST2700 has an in-house IBI payload length threshold that, once enabled, forces termination once the received payload reaches it. When payload IBIs are requested, grow the AST2700 termination threshold to at least the requested payload length and enable termination on that value. The register is controller-wide while IBI requests for different devices can run in parallel, so the read-modify-write is serialized under the controller lock. Controllers without the vendor capability block keep the existing behavior. Signed-off-by: Billy Tsai Assisted-by: Claude:claude-fable-5 --- drivers/i3c/master/mipi-i3c-hci/core.c | 14 ++++++++++---- drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c | 20 ++++++++++++++++++++ drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h | 5 +++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mi= pi-i3c-hci/core.c index e0ee8148aa2f..3d52d8f277ad 100644 --- a/drivers/i3c/master/mipi-i3c-hci/core.c +++ b/drivers/i3c/master/mipi-i3c-hci/core.c @@ -690,15 +690,21 @@ static void i3c_hci_detach_i2c_dev(struct i2c_dev_des= c *dev) static int i3c_hci_request_ibi(struct i3c_dev_desc *dev, const struct i3c_ibi_setup *req) { - struct i3c_master_controller *m =3D i3c_dev_get_master(dev); - struct i3c_hci *hci =3D to_i3c_hci(m); struct i3c_hci_dev_data *dev_data =3D i3c_dev_get_master_data(dev); + struct i3c_master_controller *m =3D i3c_dev_get_master(dev); + unsigned int ibi_max_len =3D req->max_payload_len; unsigned int dat_idx =3D dev_data->dat_idx; + struct i3c_hci *hci =3D to_i3c_hci(m); =20 - if (req->max_payload_len !=3D 0) + if (req->max_payload_len !=3D 0) { mipi_i3c_hci_dat_v1.set_flags(hci, dat_idx, DAT_0_IBI_PAYLOAD, 0); - else + + if (is_aspeed(hci)) + aspeed_i3c_ibi_grow_threshold(hci, ibi_max_len); + } else { mipi_i3c_hci_dat_v1.clear_flags(hci, dat_idx, DAT_0_IBI_PAYLOAD, 0); + } + return hci->io->request_ibi(hci, dev, req); } =20 diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c b/drivers/i3c/= master/mipi-i3c-hci/vendor_aspeed.c index b521fce4c084..e32dea0d5fc3 100644 --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c @@ -25,6 +25,26 @@ void aspeed_i3c_write(struct i3c_hci *hci, u32 reg, u32 = val) writel(val, to_aspeed_vendor_data(hci)->inhouse_regs + reg); } =20 +void aspeed_i3c_ibi_grow_threshold(struct i3c_hci *hci, unsigned int ibi_m= ax_len) +{ + u32 mrl, cur_len; + + /* + * The termination threshold is controller-wide while IBI requests + * for different devices can run in parallel, so serialize the + * read-modify-write. + */ + guard(spinlock_irqsave)(&hci->lock); + + mrl =3D aspeed_i3c_read(hci, ASPEED_I3C_MST_MRL); + cur_len =3D FIELD_GET(ASPEED_I3C_IBI_TERMINATE_LEN, mrl); + + ibi_max_len =3D max(ibi_max_len, cur_len); + aspeed_i3c_write(hci, ASPEED_I3C_MST_MRL, + ASPEED_I3C_IBI_TERMINATE_EN | + FIELD_PREP(ASPEED_I3C_IBI_TERMINATE_LEN, ibi_max_len)); +} + static void aspeed_i3c_phy_write(struct i3c_hci *hci, u32 reg, u32 val) { writel(val, to_aspeed_vendor_data(hci)->phy_regs + reg); diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/= master/mipi-i3c-hci/vendor_aspeed.h index 8760384d1c6f..84afb2bec956 100644 --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h @@ -51,6 +51,7 @@ static inline struct aspeed_i3c_vendor_data *to_aspeed_ve= ndor_data(struct i3c_hc u32 aspeed_i3c_read(struct i3c_hci *hci, u32 reg); void aspeed_i3c_write(struct i3c_hci *hci, u32 reg, u32 val); int aspeed_i3c_phy_init(struct i3c_hci *hci); +void aspeed_i3c_ibi_grow_threshold(struct i3c_hci *hci, unsigned int ibi_m= ax_len); =20 #define ASPEED_I3C_CTRL 0x00 #define ASPEED_I3C_CTRL_CLOCK_STALL_EN BIT(14) @@ -58,6 +59,10 @@ int aspeed_i3c_phy_init(struct i3c_hci *hci); #define ASPEED_I3C_CTRL_INIT_MODE GENMASK(1, 0) #define ASPEED_I3C_INIT_MST_MODE 0 =20 +#define ASPEED_I3C_MST_MRL 0x08 +#define ASPEED_I3C_IBI_TERMINATE_EN BIT(16) +#define ASPEED_I3C_IBI_TERMINATE_LEN GENMASK(15, 0) + #define ASPEED_I3C_DAA_INDEX0 0x10 #define ASPEED_I3C_DAA_INDEX1 0x14 #define ASPEED_I3C_DAA_INDEX2 0x18 --=20 2.34.1 From nobody Sat Sep 26 13:09:28 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (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 BA6BA39281B; Tue, 1 Sep 2026 11:36:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262619; cv=none; b=FQsXHhXMfmZNUkG15ro9YDoE0uhctpR0PDm9z49VOFXB+T8n7e8rtBB5K1OUG0tkKwvu4gRbfdBzAAXI8ZGTrLGMQmg1MFgHmT4XxNx4w5B4Qpgion5gBR1Q3/HmQV0qAV/tL0OulevI/Btpw6ZK9vOcdZQBRCDTB4UWduWTqbE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262619; c=relaxed/simple; bh=oaf8rylZXdyfnF1JAoViZJfEllE3F20K/vr6f/aHROE=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=APeMjWA86k7GJxR8MwuXeUraOY6Njt5dwGGW2pnNRGLaJGy69JdNLton8FI6VS+90EdSBpBSRS6A/vDf8lWHchIT3BHejKglsvJj3ALsqtQD9nHDlE9Xja5sZkIhah9gzhuEYt4y//op2NxgYi2SWDTl+06XxefD/8L8iqqmD2I= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Tue, 1 Sep 2026 19:35:39 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Tue, 1 Sep 2026 19:35:39 +0800 From: Billy Tsai Date: Tue, 1 Sep 2026 19:35:34 +0800 Subject: [PATCH 7/8] i3c: mipi-i3c-hci: Improve AST2700 PIO TX queue utilization 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: <20260901-b4-i3c-hci-ast2700-v1-7-19909e7cbd7e@aspeedtech.com> References: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> In-Reply-To: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> To: Alexandre Belloni , Frank Li , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Philipp Zabel CC: , , , , , Billy Tsai X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788262538; l=5389; i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id; bh=oaf8rylZXdyfnF1JAoViZJfEllE3F20K/vr6f/aHROE=; b=RYohPmXVU7MlqRAhyOatovdjRP4ydQzRQ7LkGAAOgee7Mv0uUdh/XLOduNYCIoVJYOpr5oaV6 WSWWqbdoS/iCxtJEqyf0uGiKli6UpiCiqM3aA+Od6cRFNwFmpZvFpmL X-Developer-Key: i=billy_tsai@aspeedtech.com; a=ed25519; pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ= The PIO TX path only pushes data once STAT_TX_THLD reports that tx_thresh_size words are free, with the threshold set to half the TX FIFO. The FIFO is therefore never filled beyond half from a single wait, which under-utilizes the queue, and sending buffers larger than the threshold makes software race against the hardware draining the FIFO, resulting in an unstable flow. The AST2700 vendor block exposes the TX FIFO read and write pointers, which give the exact number of free entries. When is_aspeed() is true, size each burst from those pointers, using CIRC_SPACE() to derive the free-entry count, so the FIFO can be used up to its full depth. Other controllers keep the existing threshold-based flow. Signed-off-by: Billy Tsai Assisted-by: Claude:claude-fable-5 --- drivers/i3c/master/mipi-i3c-hci/pio.c | 25 +++++++++++++++++++++= ---- drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c | 11 +++++++++++ drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h | 6 ++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/drivers/i3c/master/mipi-i3c-hci/pio.c b/drivers/i3c/master/mip= i-i3c-hci/pio.c index 6bd5e43364a1..3d70257dcc1d 100644 --- a/drivers/i3c/master/mipi-i3c-hci/pio.c +++ b/drivers/i3c/master/mipi-i3c-hci/pio.c @@ -15,6 +15,7 @@ #include "cmd.h" #include "ibi.h" #include "pio.h" +#include "vendor_aspeed.h" =20 /* * PIO Access Area @@ -295,11 +296,23 @@ static bool hci_pio_do_tx(struct i3c_hci *hci, struct= hci_pio_data *pio) p +=3D (xfer->data_len - xfer->data_left) / 4; =20 while (xfer->data_left >=3D 4) { - /* bail out if FIFO free space is below set threshold */ - if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD)) + unsigned int avail =3D pio->tx_thresh_size; + + if (is_aspeed(hci)) { + /* + * The vendor FIFO pointers give the exact free space, + * allowing better TX FIFO utilization than the + * threshold status. + */ + avail =3D aspeed_i3c_avail_tx_entries(hci); + if (!avail) + return false; + } else if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD)) { + /* bail out if FIFO free space is below set threshold */ return false; + } /* we can fill up to that TX threshold */ - nr_words =3D min(xfer->data_left / 4, pio->tx_thresh_size); + nr_words =3D min(xfer->data_left / 4, avail); /* push data into the FIFO */ xfer->data_left -=3D nr_words * 4; dev_dbg(&hci->master.dev, "now %d left %d", @@ -316,8 +329,12 @@ static bool hci_pio_do_tx(struct i3c_hci *hci, struct = hci_pio_data *pio) * also get some bytes past the actual buffer but no one * should care as they won't be sent out. */ - if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD)) + if (is_aspeed(hci)) { + if (!aspeed_i3c_avail_tx_entries(hci)) + return false; + } else if (!(pio_reg_read(INTR_STATUS) & STAT_TX_THLD)) { return false; + } dev_dbg(&hci->master.dev, "trailing %d", xfer->data_left); pio_reg_write(XFER_DATA_PORT, *p); xfer->data_left =3D 0; diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c b/drivers/i3c/= master/mipi-i3c-hci/vendor_aspeed.c index e32dea0d5fc3..182111b857c9 100644 --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c @@ -6,6 +6,7 @@ */ =20 #include +#include #include #include #include @@ -45,6 +46,16 @@ void aspeed_i3c_ibi_grow_threshold(struct i3c_hci *hci, = unsigned int ibi_max_len FIELD_PREP(ASPEED_I3C_IBI_TERMINATE_LEN, ibi_max_len)); } =20 +/* Free TX FIFO entries derived from the vendor FIFO read/write pointers */ +unsigned int aspeed_i3c_avail_tx_entries(struct i3c_hci *hci) +{ + u32 ptr =3D aspeed_i3c_read(hci, ASPEED_I3C_QUEUE_PTR0); + unsigned int w =3D FIELD_GET(ASPEED_I3C_QUEUE_PTR0_TX_W, ptr); + unsigned int r =3D FIELD_GET(ASPEED_I3C_QUEUE_PTR0_TX_R, ptr); + + return CIRC_SPACE(w, r, ASPEED_I3C_TX_QUEUE_ENTRIES); +} + static void aspeed_i3c_phy_write(struct i3c_hci *hci, u32 reg, u32 val) { writel(val, to_aspeed_vendor_data(hci)->phy_regs + reg); diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/= master/mipi-i3c-hci/vendor_aspeed.h index 84afb2bec956..7147abea830d 100644 --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h @@ -52,6 +52,7 @@ u32 aspeed_i3c_read(struct i3c_hci *hci, u32 reg); void aspeed_i3c_write(struct i3c_hci *hci, u32 reg, u32 val); int aspeed_i3c_phy_init(struct i3c_hci *hci); void aspeed_i3c_ibi_grow_threshold(struct i3c_hci *hci, unsigned int ibi_m= ax_len); +unsigned int aspeed_i3c_avail_tx_entries(struct i3c_hci *hci); =20 #define ASPEED_I3C_CTRL 0x00 #define ASPEED_I3C_CTRL_CLOCK_STALL_EN BIT(14) @@ -68,6 +69,11 @@ void aspeed_i3c_ibi_grow_threshold(struct i3c_hci *hci, = unsigned int ibi_max_len #define ASPEED_I3C_DAA_INDEX2 0x18 #define ASPEED_I3C_DAA_INDEX3 0x1c =20 +#define ASPEED_I3C_QUEUE_PTR0 0xd8 +#define ASPEED_I3C_QUEUE_PTR0_TX_R GENMASK(24, 20) +#define ASPEED_I3C_QUEUE_PTR0_TX_W GENMASK(16, 12) +#define ASPEED_I3C_TX_QUEUE_ENTRIES 32 + #define ASPEED_I3C_INTR_STATUS 0xe0 #define ASPEED_I3C_INTR_STATUS_ENABLE 0xe4 #define ASPEED_I3C_INTR_SIGNAL_ENABLE 0xe8 --=20 2.34.1 From nobody Sat Sep 26 13:09:28 2026 Received: from twmbx01.aspeedtech.com (mail.aspeedtech.com [211.20.114.72]) (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 D182B36196C; Tue, 1 Sep 2026 11:36:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=211.20.114.72 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262621; cv=none; b=ikUQNRBL206qjRU0fednjUwEyCyU/jPlDgdPpXmnPh6sVJcLadBlNn5dK8rory9mmNoTmyOG3On6K9hJORWxX4cn0aI7hz/Ca6BMtza8KH4vdf/h5GXUb0g1COHfDuWd6ZM/uLoRJyDeIQBPWaYJljQecJlklk6mfzgbgvMdoeQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788262621; c=relaxed/simple; bh=xkzAfCKLYQ/Xwwsdimh/tEMuOhSZZrRcVSqry9lnz3A=; h=From:Date:Subject:MIME-Version:Content-Type:Message-ID:References: In-Reply-To:To:CC; b=CebB1EMEjUK1o1Q+000wq9XRspu0eedRTcbk9c0cZJ98sxIuzL2ujFxKIJbIYBS6z57stkeVn03L0D06Oh54fy2VXktZkKM1BW1stSPI4qsZS2qHl0xqm9uii/Ho39pd8aNK26V7CsOIvLwfXx0IoCkvLyRF4A7C7oxoTRkd+mU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com; spf=pass smtp.mailfrom=aspeedtech.com; arc=none smtp.client-ip=211.20.114.72 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=aspeedtech.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=aspeedtech.com Received: from TWMBX01.aspeed.com (192.168.0.62) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1748.10; Tue, 1 Sep 2026 19:35:39 +0800 Received: from [127.0.1.1] (192.168.10.13) by TWMBX01.aspeed.com (192.168.0.62) with Microsoft SMTP Server id 15.2.1748.10 via Frontend Transport; Tue, 1 Sep 2026 19:35:39 +0800 From: Billy Tsai Date: Tue, 1 Sep 2026 19:35:35 +0800 Subject: [PATCH 8/8] i3c: mipi-i3c-hci: Support the AST2700 internal pull-ups 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: <20260901-b4-i3c-hci-ast2700-v1-8-19909e7cbd7e@aspeedtech.com> References: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> In-Reply-To: <20260901-b4-i3c-hci-ast2700-v1-0-19909e7cbd7e@aspeedtech.com> To: Alexandre Belloni , Frank Li , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Joel Stanley , Andrew Jeffery , Philipp Zabel CC: , , , , , Billy Tsai X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=ed25519-sha256; t=1788262538; l=6058; i=billy_tsai@aspeedtech.com; s=20251118; h=from:subject:message-id; bh=xkzAfCKLYQ/Xwwsdimh/tEMuOhSZZrRcVSqry9lnz3A=; b=azBxWolQHPu9pCSptGyEx4x+4PYzUuzgnmWwmp6rvIzxR3+3V97LKaUSwq+r2jQLcmZR6UNNQ CEEgKtwyI5jDGGMoQMqqH8cLz61sGNtIdz8hXv2feGg480YH4Kk43Ne X-Developer-Key: i=billy_tsai@aspeedtech.com; a=ed25519; pk=/A8qvgZ6CPfnwKgT6/+k+nvXOkN477MshEGJvVdzeeQ= Boards that do not populate external I3C pull-up resistors can use the pull-ups built into the AST2700 PHY. When bias-pull-up is present, determine the pin voltage domain from the pinctrl-0 group already selected for the bus and enable the internal pull-ups on both SCL and SDA at the requested strength. Currently only pinctrl-0 is inspected; a node selecting its group through a different pinctrl state is not yet supported. The controller reset clears the SW force control register, so the internal pull-ups start disabled unless bias-pull-up requests otherwise. A resistance with no entry in the domain's table, or an unrecognized pinctrl-0 group, is rejected with an error. Signed-off-by: Billy Tsai Assisted-by: Claude:claude-fable-5 --- drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c | 118 ++++++++++++++++++++= ++++ drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h | 6 ++ 2 files changed, 124 insertions(+) diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c b/drivers/i3c/= master/mipi-i3c-hci/vendor_aspeed.c index 182111b857c9..d28d5680d326 100644 --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.c @@ -12,6 +12,7 @@ #include #include #include +#include =20 #include "hci.h" #include "vendor_aspeed.h" @@ -86,6 +87,100 @@ static u32 aspeed_i3c_get_sdr_ctrl0_reg(struct i3c_hci = *hci) return ASPEED_I3C_PHY_I3C_SDR4_CTRL0; } =20 +struct aspeed_i3c_pullup_ohms { + u32 ohms; + u8 code; +}; + +/* SW_FORCE_CTRL strength selector to resistance, by pin voltage domain */ +static const struct aspeed_i3c_pullup_ohms aspeed_i3c_pullup_hv[] =3D { + { 600, 1 }, + { 470, 2 }, + { 400, 3 }, +}; + +static const struct aspeed_i3c_pullup_ohms aspeed_i3c_pullup_lv[] =3D { + { 750, 1 }, + { 550, 2 }, + { 317, 3 }, + { 400, 4 }, + { 261, 5 }, + { 231, 6 }, + { 177, 7 }, +}; + +/* pinctrl-0 group names for each pin voltage domain */ +static const char * const aspeed_i3c_hv_groups[] =3D { + "HVI3C0", "HVI3C1", "HVI3C2", "HVI3C3", + "HVI3C12", "HVI3C13", "HVI3C14", "HVI3C15", +}; + +static const char * const aspeed_i3c_lv_groups[] =3D { + "I3C4", "I3C5", "I3C6", "I3C7", + "I3C8", "I3C9", "I3C10", "I3C11", +}; + +static int aspeed_i3c_pin_is_hv(struct i3c_hci *hci, bool *is_hv) +{ + struct device_node *np =3D hci->master.dev.of_node; + struct device_node *state; + const char *group; + int ret, i; + + state =3D of_parse_phandle(np, "pinctrl-0", 0); + if (!state) + return -ENODEV; + + ret =3D of_property_read_string(state, "groups", &group); + if (ret) + goto put_state; + + for (i =3D 0; i < ARRAY_SIZE(aspeed_i3c_hv_groups); i++) { + if (!strcmp(group, aspeed_i3c_hv_groups[i])) { + *is_hv =3D true; + goto put_state; + } + } + + for (i =3D 0; i < ARRAY_SIZE(aspeed_i3c_lv_groups); i++) { + if (!strcmp(group, aspeed_i3c_lv_groups[i])) { + *is_hv =3D false; + goto put_state; + } + } + + dev_err(&hci->master.dev, "unrecognized pinctrl-0 group \"%s\"\n", + group); + ret =3D -EINVAL; + +put_state: + of_node_put(state); + return ret; +} + +static int aspeed_i3c_pullup_code(struct i3c_hci *hci, bool is_hv, u32 ohm= s) +{ + const struct aspeed_i3c_pullup_ohms *map; + size_t i, n; + + if (is_hv) { + map =3D aspeed_i3c_pullup_hv; + n =3D ARRAY_SIZE(aspeed_i3c_pullup_hv); + } else { + map =3D aspeed_i3c_pullup_lv; + n =3D ARRAY_SIZE(aspeed_i3c_pullup_lv); + } + + for (i =3D 0; i < n; i++) + if (map[i].ohms =3D=3D ohms) + return map[i].code; + + dev_err(&hci->master.dev, + "unsupported internal pull-up %u ohms for %s-voltage domain\n", + ohms, is_hv ? "high" : "low"); + return -EINVAL; +} + int aspeed_i3c_phy_init(struct i3c_hci *hci) { u16 hcnt, lcnt, total_cnt, min_tbit_cnt, cas_lcnt, cas_cnt, cbp_cnt; @@ -95,6 +190,7 @@ int aspeed_i3c_phy_init(struct i3c_hci *hci) struct device_node *np =3D hci->master.dev.of_node; u32 sdr_ctrl0_reg, ctrl0, ctrl1, ctrl2; unsigned long core_rate, period_ns; + u32 pullup_ohms =3D 0; =20 core_rate =3D clk_get_rate(to_aspeed_vendor_data(hci)->clk); if (!core_rate) { @@ -257,6 +353,28 @@ int aspeed_i3c_phy_init(struct i3c_hci *hci) =20 aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_PULLUP_EN, 0); =20 + of_property_read_u32(np, "bias-pull-up", &pullup_ohms); + if (pullup_ohms) { + int ret, strength; + bool is_hv; + + ret =3D aspeed_i3c_pin_is_hv(hci, &is_hv); + if (ret) + return ret; + + strength =3D aspeed_i3c_pullup_code(hci, is_hv, pullup_ohms); + if (strength < 0) + return strength; + + aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_SW_FORCE_CTRL, + ASPEED_I3C_PHY_SW_FORCE_SCL_PU_EN | + ASPEED_I3C_PHY_SW_FORCE_SDA_PU_EN | + FIELD_PREP(ASPEED_I3C_PHY_SW_FORCE_SCL_PU_VAL, + strength) | + FIELD_PREP(ASPEED_I3C_PHY_SW_FORCE_SDA_PU_VAL, + strength)); + } + hcnt =3D aspeed_i3c_ns_to_cnt(ASPEED_I3C_PHY_OD_DAP_NS, period_ns); aspeed_i3c_phy_write(hci, ASPEED_I3C_PHY_I3C_OD_CTRL4, FIELD_PREP(ASPEED_I3C_PHY_I3C_OD_CTRL4_DAP, hcnt)); diff --git a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h b/drivers/i3c/= master/mipi-i3c-hci/vendor_aspeed.h index 7147abea830d..80938c580800 100644 --- a/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h +++ b/drivers/i3c/master/mipi-i3c-hci/vendor_aspeed.h @@ -83,6 +83,12 @@ unsigned int aspeed_i3c_avail_tx_entries(struct i3c_hci = *hci); #define ASPEED_INTR_SUM_PIO BIT(1) #define ASPEED_INTR_SUM_CAP BIT(0) =20 +#define ASPEED_I3C_PHY_SW_FORCE_CTRL 0x04 +#define ASPEED_I3C_PHY_SW_FORCE_SCL_PU_EN BIT(28) +#define ASPEED_I3C_PHY_SW_FORCE_SDA_PU_EN BIT(24) +#define ASPEED_I3C_PHY_SW_FORCE_SCL_PU_VAL GENMASK(10, 8) +#define ASPEED_I3C_PHY_SW_FORCE_SDA_PU_VAL GENMASK(2, 0) + #define ASPEED_I3C_PHY_I2C_FM_CTRL0 0x08 #define ASPEED_I3C_PHY_I2C_FM_CTRL0_CAS GENMASK(26, 16) #define ASPEED_I3C_PHY_I2C_FM_CTRL0_SU_STO GENMASK(10, 0) --=20 2.34.1