From nobody Fri Sep 25 22:19:12 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 6DF753A2543; Tue, 8 Sep 2026 07:42:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788853351; cv=none; b=ADvMIfP6wGU3gE2F8OwYhXIpYyhBhCDp+KdUqFIoY0k1Jtze5jyn38OJQm4km1QFOpTUbO3tN1FeYKD+1LhnWqzJfqv9lM6K4rIBIxanvgtJ8N1dVKIzGqKu09mi9dNckjRZNCVcOUkqBcSgNuLrM2F6zHLw/AkKoOmXkxLVu8A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788853351; c=relaxed/simple; bh=rk95kKGG/SkoEMHzqicwZcRd7bMRmZDFgL6+fZ2Pbbc=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=LIhi7Qn/mH6fPq+muEn+qwl6x/H5pLd36GdLce3dFN6+ezbhZlb89zRhQcBPYO+qN2h+iMws4MKvuY5QfJOCblrZQ7h+vc1vsGBSCzfsRkFrZw3q25tQoJoCaZ7bxuF88rnVVJ7EEUF8VvznTBbCVYIcyjMiatHL2EzfIKGDw2Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=e6um0j6E; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="e6um0j6E" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9567B1F00A3D; Tue, 8 Sep 2026 07:42:24 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788853349; bh=tV9IUwoo59NJmLmcHHMkUhsXWOY7J5vyTiJOpbYBP9Y=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=e6um0j6ESrucysFwkf5r6HNWPzsTc0+Gd93YE8eQkDV0O5ssn78IWL99LdE7Lj/vZ AHWFpovcNjiZJhO8dfW8UzVJ6bgv1U+zlq4t9kgCn5HsE5ZrcbYE24InaIXEWBfvZz 2rWidil0WTTqxbmU8KrvMfWhrgGhqnH1edKo8n3+7ipLW9xCoHnOfVwQvSgJhRNzQn tRnQuk4K2XFFvtn2KeGgZ9altvoovhbdIYSmKHULsC+URcWcXuiVMZryg6Wi8aeRAH W0aCMb8ME7XL6+6OWX0dYkGL3TxUpGe37TGFp28t/wuht9lgp93RS7K2cxa3NV5J9Q ll3C5m+AL6RJg== From: Yixun Lan Date: Tue, 08 Sep 2026 07:42:03 +0000 Subject: [PATCH v4 1/3] scsi: ufs: spacemit: dt-bindings: Add UFS controller for K3 SoC 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: <20260908-08-k3-ufs-support-v4-1-22aa2bd97d3b@kernel.org> References: <20260908-08-k3-ufs-support-v4-0-22aa2bd97d3b@kernel.org> In-Reply-To: <20260908-08-k3-ufs-support-v4-0-22aa2bd97d3b@kernel.org> To: Alim Akhtar , Avri Altman , Bart Van Assche , Rob Herring , Krzysztof Kozlowski , Conor Dooley , "James E.J. Bottomley" , Philipp Zabel , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , "Martin K. Petersen" Cc: Anirudh Srinivasan , Aurelien Jarno , Jennifer Berringer , linux-scsi@vger.kernel.org, devicetree@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Yixun Lan X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2205; i=dlan@kernel.org; h=from:subject:message-id; bh=rk95kKGG/SkoEMHzqicwZcRd7bMRmZDFgL6+fZ2Pbbc=; b=owEB6QIW/ZANAwAKATGq6kdZTbvtAcsmYgBqn7xR5wtNpffnuxRFGP1wvMK84k0sxnS1iCz8q o8c4X4MiRWJAq8EAAEKAJkWIQS1urjJwxtxFWcCI9wxqupHWU277QUCap+8URsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDJfFIAAAAAALgAoaXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5 maWZ0aGhvcnNlbWFuLm5ldEI1QkFCOEM5QzMxQjcxMTU2NzAyMjNEQzMxQUFFQTQ3NTk0REJCRU QACgkQMarqR1lNu+1zbQ/6Aq+/pye8H0hu9O5u1yZRP39vD1lsFvltjrRqjX6kcGov/2fIGVAUO rfEqQRIsvy2aYsYk/L8qynu4+dEjBr0f1mJlJrQP6c/8hGoyETROQSDIYlR4tNq4aF6sjsu9Pzt +lCt8vs+X9p64Ap1UfviF5/73t1e0wvLg4v/E2YRFPJ6sAvzqOgPN18u4wO/FdE9rS61ppwzdfu kvuQ1IkXmmYKgK5xZiR15cMEH0RVVNFqXBrs/D+wDWf3RaA0Qy8vlgNo75Fb39ILLA5piDpCDFY g2sRw++KrXCp1ULNU/0uIbtzEvGDsdnlF220SlULNzVYkiiF4ChPKNuGVoDM3gi9SJLOFEmmr2A zI7kYYfWqm1vfqSn9rktTdMFtbPTkF5tlhMuJDtO370fOuyNB7/PuGIxrK4GZ+zfCox9AXXcq68 su9H13StUaDlb3AplqsgPxLINMpAxnyx25Nxsn5PynFpFSPIwqaX0aheEYTEpUy19iZZQrj3NRC 70+3RcjFfKnqmHJZaAJZAu1BxKEonLwcCj41/q4Jkox8M/PgVBQFM3cmBtBJSg/gMWPp9L3OHYn DvWZKHATf282ZJW4uOxcnUYvc92m3pulDqRi8EGv350OTof8+kbFy9rLqnRTo+P6ZuE+k5/Gq2f yaSezlEZCgwTGq5hq1tUTBDBqclruI= X-Developer-Key: i=dlan@kernel.org; a=openpgp; fpr=50B03A1A5CBCD33576EF8CD7920C0DBCAABEFD55 Document the compatible for UFS (Universal Flash Storage) Host Controller which found in SpacemiT K3 SoC chip. Its features are listed below: - Compliant with MIPI UniPro v1.61 specification. - Compliant with M-PHY v3.0 specification. - Compliant with UFS HCI v2.1 specification. - Supports up to 2 Tx and 2 Rx lanes, up to HS-GEAR3 5.8 Gbps per lane. - Supports standard low-power hibernate to reduce power consumption. Signed-off-by: Yixun Lan Tested-by: Anirudh Srinivasan Tested-by: Aurelien Jarno --- .../devicetree/bindings/ufs/spacemit,k3-ufshc.yaml | 56 ++++++++++++++++++= ++++ 1 file changed, 56 insertions(+) diff --git a/Documentation/devicetree/bindings/ufs/spacemit,k3-ufshc.yaml b= /Documentation/devicetree/bindings/ufs/spacemit,k3-ufshc.yaml new file mode 100644 index 000000000000..dda533c852e8 --- /dev/null +++ b/Documentation/devicetree/bindings/ufs/spacemit,k3-ufshc.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ufs/spacemit,k3-ufshc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: SpacemiT K3 SoC UFS Host Controller + +maintainers: + - Yixun Lan + +properties: + compatible: + const: spacemit,k3-ufshc + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: aclk + - const: ref_clk + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - resets + +allOf: + - $ref: ufs-common.yaml + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + ufs@c0e00000 { + compatible =3D "spacemit,k3-ufshc"; + reg =3D <0xc0e00000 0x40000>; + clocks =3D <&syscon_apmu CLK_APMU_UFS_ACLK>, + <&syscon_apmu CLK_APMU_UFS_REFCLK>; + clock-names =3D "aclk", "ref_clk"; + resets =3D <&syscon_apmu RESET_APMU_UFS_ACLK>; + interrupts =3D <135 IRQ_TYPE_LEVEL_HIGH>; + lanes-per-direction =3D <2>; + }; --=20 2.55.0 From nobody Fri Sep 25 22:19:12 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 269643A875E; Tue, 8 Sep 2026 07:42:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788853357; cv=none; b=luf7onWvZz0HLmVuJ1GbIe88bB+vmjVWxvmHJXIOoEM32nAUrUwf3ozPEGBJJWN86Ml2IWgcOxBiZe1VQcWeKVrpeAEOjMrI0uyMMs2opTzzlyIJF7ZJ5DS9I/XHWM8dTHWE79o2mgGnqrHyG+D57/YImsTkwT12ErXobbb09ss= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788853357; c=relaxed/simple; bh=J4JB0mzmx6XMkmHi42KLxPWirymLlKEd4EGsjDU83mw=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=gjVYYiYLZbdivtXCJs9k6FFbeMASowgTnD5+rkY6JY98nuVeuZdSvHB+P+dT+pDHGobcuTIn13XfRzpRsihDSzXDHmEhK/DfaSGD/SRGSYPxdG5Kv58dLOFx7wW6D1VWDqRwDZz2c/u2jMx1BJu+GQvyfkHYX0sH84DSnFPRk0M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bNIGk44/; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bNIGk44/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 67E951F00A3A; Tue, 8 Sep 2026 07:42:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788853354; bh=W1+Hd63BCv/xmxcBBHKcGXmM/Zm0T6FQy39gmZYH7Bc=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=bNIGk44/z64Rj1vtzK5mDkxvWOtcCMSalMvmhm1rgEBWd6RObE7jhMEye2Jhr0LPJ ckI5pxAAn5yy3YyGkstrxahXQqXq2fw94mvpTe9Pd0qMwC7v4WEoy0Yj0p970W8Bhn OlQ4IWXguI1nhwifNNQzohXlFLBmnGdpB9A58woB44Uyt+E9fkcPFjcclhpG1KkTVh YoRI7D+Try/Hnuuh40kT0Ll5Ze8aQoKoD98QLL5rs/8ECcKUfIOKd67KvxeJLq5snU GedU/P+j2x66RS5kEnSqcgAj2YCamA+vlXoaZYXIYH5F8YNbJaWdO3yerdG9flUQEi 4waC9YFIBKcMw== From: Yixun Lan Date: Tue, 08 Sep 2026 07:42:04 +0000 Subject: [PATCH v4 2/3] scsi: ufs: spacemit: k3: Add UFS Host Controller driver 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: <20260908-08-k3-ufs-support-v4-2-22aa2bd97d3b@kernel.org> References: <20260908-08-k3-ufs-support-v4-0-22aa2bd97d3b@kernel.org> In-Reply-To: <20260908-08-k3-ufs-support-v4-0-22aa2bd97d3b@kernel.org> To: Alim Akhtar , Avri Altman , Bart Van Assche , Rob Herring , Krzysztof Kozlowski , Conor Dooley , "James E.J. Bottomley" , Philipp Zabel , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , "Martin K. Petersen" Cc: Anirudh Srinivasan , Aurelien Jarno , Jennifer Berringer , linux-scsi@vger.kernel.org, devicetree@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Yixun Lan X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=26489; i=dlan@kernel.org; h=from:subject:message-id; bh=J4JB0mzmx6XMkmHi42KLxPWirymLlKEd4EGsjDU83mw=; b=owEB6QIW/ZANAwAKATGq6kdZTbvtAcsmYgBqn7xUKvxESNBUiVYg92VY2nd9iFlgxALsCLhQP TVIDSJ06ZCJAq8EAAEKAJkWIQS1urjJwxtxFWcCI9wxqupHWU277QUCap+8VBsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDJfFIAAAAAALgAoaXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5 maWZ0aGhvcnNlbWFuLm5ldEI1QkFCOEM5QzMxQjcxMTU2NzAyMjNEQzMxQUFFQTQ3NTk0REJCRU QACgkQMarqR1lNu+3Olw/+K4mLZlMb+zEnbdVeXH6+Cebd6u3rc2TcAISf0Cd5OMVRtk5X4XotY NL2GVYbD0cD8nKFywqZ+fRmFzD9Fl7CcXvQmplRaq/p9riCJogv6wLmHRir+pgYFwcuPZVVE6Ka xNs29QPquwTiiZQ6Nt/aZtly9hI0VkytR1bTHd80K8IF3AFUR/9rDIw6hxWZBeuoj7g8vO5HQO/ MlDHEMMfINkNPvqqLhukYvuVBpEmS516nsqMCxrbnkIR2i2/dL5YWQzUItrLp6Lye4KH1d3UvkV hUlWpdlkMhMd1VKAlAFz7NEDc0fZSDkQ26YR96gJanNA6Wmcmsu16nUFmAbBjfGL50qu09q13wo T+vBi7IlpGTiyW1apK4tWQwfjz3cO3jO8VbTTTIwAgcSV07sfzxwt0hR4hDIxNWnn1UUig7oF55 nDwP1K5rZ9Jb6XYxBX6TC5aVoPdF7/6UcHOr4Iiw2CHHntcr0ByTst+NrlA3KiLDz9r7Pjecyvp Y2WwyaD8ejYwTxHhcJcaYSxkerqhD7rOg+KN7JFBAIOXKO1eyaPTw5nVKFkh3yB/Te5PSnaavG5 +19XlSnLzJev7gYHLipGLVRDo6kkopqjrwmP7MozSgbHAz8QVnxr/ateZ+/gM1HfvzWTnLWxptk PsKLT83ouVIyzhhuIk6pjl9MkVfp18= X-Developer-Key: i=dlan@kernel.org; a=openpgp; fpr=50B03A1A5CBCD33576EF8CD7920C0DBCAABEFD55 SpacemiT K3 SoC consist of UFS (Universal Flash Storage) Host Controller which has features compatible with JEDEC UFS 2.2, MIPI UniPro v1.61 and M-PHY v3.0 standard. Tested-by: Aurelien Jarno Signed-off-by: Yixun Lan Tested-by: Anirudh Srinivasan --- drivers/ufs/host/Kconfig | 12 + drivers/ufs/host/Makefile | 1 + drivers/ufs/host/ufs-spacemit.c | 678 ++++++++++++++++++++++++++++++++++++= ++++ drivers/ufs/host/ufs-spacemit.h | 79 +++++ 4 files changed, 770 insertions(+) diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig index ff170c0b6da0..6146872fcecc 100644 --- a/drivers/ufs/host/Kconfig +++ b/drivers/ufs/host/Kconfig @@ -132,6 +132,18 @@ config SCSI_UFS_VARIABLE_SG_ENTRY_SIZE bool default y if SCSI_UFS_EXYNOS && SCSI_UFS_CRYPTO =20 +config SCSI_UFS_SPACEMIT + tristate "SpacemiT UFS controller driver" + depends on SCSI_UFSHCD_PLATFORM && (ARCH_SPACEMIT || COMPILE_TEST) + help + This selects the SpacemiT specific additions to UFSHCD platform driver. + UFS host on SpacemiT needs some vendor specific configuration before + accessing the hardware which includes PHY configuration and vendor + specific registers. + + Select this if you have UFS controller on SpacemiT chipset. + If unsure, say N. + config SCSI_UFS_SPRD tristate "Unisoc specific hooks to UFS controller platform driver" depends on SCSI_UFSHCD_PLATFORM && (ARCH_SPRD || COMPILE_TEST) diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile index 7d8db67eb23c..a3c447b8fc21 100644 --- a/drivers/ufs/host/Makefile +++ b/drivers/ufs/host/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_SCSI_UFS_HISI) +=3D ufs-hisi.o obj-$(CONFIG_SCSI_UFS_MEDIATEK) +=3D ufs-mediatek.o obj-$(CONFIG_SCSI_UFS_RENESAS) +=3D ufs-renesas.o obj-$(CONFIG_SCSI_UFS_ROCKCHIP) +=3D ufs-rockchip.o +obj-$(CONFIG_SCSI_UFS_SPACEMIT) +=3D ufs-spacemit.o obj-$(CONFIG_SCSI_UFS_SPRD) +=3D ufs-sprd.o obj-$(CONFIG_SCSI_UFS_TI_J721E) +=3D ti-j721e-ufs.o obj-$(CONFIG_SCSI_UFS_AMD_VERSAL2) +=3D ufs-amd-versal2.o ufshcd-dwc.o diff --git a/drivers/ufs/host/ufs-spacemit.c b/drivers/ufs/host/ufs-spacemi= t.c new file mode 100644 index 000000000000..9d037d90ac5e --- /dev/null +++ b/drivers/ufs/host/ufs-spacemit.c @@ -0,0 +1,678 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2026 SpacemiT (Hangzhou) Technology Co. Ltd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "ufshcd-pltfrm.h" +#include "ufs-spacemit.h" + +/* PA Layer Gettable and settable M-PHY Specific Attributes */ +#define PA_TXHSG1SYNCLENGTH 0x1552 +#define PA_TXHSG1PREPARELENGTH 0x1553 +#define PA_TXHSG2SYNCLENGTH 0x1554 +#define PA_TXHSG2PREPARELENGTH 0x1555 +#define PA_TXHSG3SYNCLENGTH 0x1556 +#define PA_TXHSG3PREPARELENGTH 0x1557 +#define PA_TXMK2EXTENSION 0x155A +#define PA_PEERSCRAMBLING 0x155B +#define PA_TXSKIP 0x155C +#define PA_TXSKIPPERIOD 0x155D +#define PA_PEER_TX_LCC_ENABLE 0x155F + +#define PA_SCRAMBLING 0x1585 +#define PA_MK2EXTENSIONGUARDBAND 0x15AB + +/* Special TX/RX Configuration Attributes */ +#define RX_LS_PRE_LEN_CAP 0x008D +#define RX_LANE_HB8_BKDOOR_ATTR 0x00F4 +#define RX_PWRM_CLOSURE_LEN_CAP 0x008E +#define RX_MIN_STALL_CAP 0x0088 +#define RX_LANE_SOF_BKDOOR_ATT 0x00F2 +#define RX_GARBAGE_COUNT_OFFSET 0x00F2 + +/* Special analog register */ +#define ANA_EQ_CTRL_REG_ATTR 0x00CD +#define ANA_HSGEAR_CTRL_ATTR 0x00C1 + +/* + * Keep UFS ACLK at a lower parent rate (409.6MHz) for stable init. + * This mirrors the "ufs-low-aclk-freq" change from the other environment. + */ +#define UFS_ACLK_LOW_FREQ_HZ 409600000UL + +/* PHY register magic values */ +#define MPHY_PU_ALL 0x87f +#define MPHY_PU_WITH_HB8_RESET 0xb7f +#define MPHY_DEVICE_REFCLK_OE BIT(8) +#define MPHY_DEVICE_HW_RESET BIT(0) + +#define MPHY_PLL_LOCK_BIT BIT(31) +#define MPHY_PLL_LOCK_TIMEOUT_US 10000 + +/* FSM states */ +#define FSM_STATE_HIBERN8 0x1 +#define FSM_STATE_ACTIVE 0x3 +#define FSM_STATE_LS_BURST 0x5 + +#define VENDOR_DUMP_BUF_SIZE 2048 +#define VENDOR_MAX_OFFSET 0xE0 + +/* M-PHY FSM states */ +#define MPHY_RX_FSM_STATE 0xC1 +#define MPHY_TX_FSM_STATE 0x41 + +static int ufs_spacemit_dme_set(struct ufs_hba *hba, + const struct ufshcd_dme_attr_val *v, int n) +{ + int ret =3D 0; + int attr_node =3D 0; + + for (attr_node =3D 0; attr_node < n; attr_node++) { + ret =3D ufshcd_dme_set(hba, v[attr_node].attr_sel, + v[attr_node].mib_val); + + if (ret) + return ret; + } + + return 0; +} + +static int ufs_spacemit_check_hibern8(struct ufs_hba *hba) +{ + u32 tx_fsm_val_0 =3D 0; + u32 tx_fsm_val_1 =3D 0; + int retries =3D DIV_ROUND_UP(HBRN8_POLL_TOUT_MS * 1000, 100); + int err =3D 0; + + do { + err =3D ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), + &tx_fsm_val_0); + if (err) + break; + + err =3D ufshcd_dme_get(hba, + UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), + &tx_fsm_val_1); + if (err || (tx_fsm_val_0 =3D=3D TX_FSM_HIBERN8 && + tx_fsm_val_1 =3D=3D TX_FSM_HIBERN8)) + break; + + usleep_range(100, 200); + } while (--retries > 0); + + if (err) { + dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n", + __func__, err); + } else if (tx_fsm_val_0 !=3D TX_FSM_HIBERN8 || + tx_fsm_val_1 !=3D TX_FSM_HIBERN8) { + err =3D -ETIMEDOUT; + dev_err(hba->dev, + "%s: invalid TX_FSM_STATE, lane0 =3D %u, lane1 =3D %u\n", + __func__, tx_fsm_val_0, tx_fsm_val_1); + } + + return err; +} + +static u32 ufs_spacemit_get_sys1clk_1us(struct ufs_hba *hba) +{ + struct ufs_clk_info *clki, *ufs_aclk =3D NULL; + struct list_head *head =3D &hba->clk_list_head; + unsigned long rate_hz =3D 0; + + if (!list_empty(head)) { + list_for_each_entry(clki, head, list) { + if (clki->name && !strcmp(clki->name, "aclk") && clki->clk) { + ufs_aclk =3D clki; + break; + } + } + } + + if (ufs_aclk && ufs_aclk->clk) + rate_hz =3D clk_get_rate(ufs_aclk->clk); + + if (!rate_hz) + return 0; + + return DIV_ROUND_CLOSEST(rate_hz, 1000000); +} + +static int ufs_spacemit_wait_mphy_pll_lock(struct ufs_hba *hba) +{ + u32 val; + int err; + + err =3D read_poll_timeout(ufshcd_readl, val, val & MPHY_PLL_LOCK_BIT, + 10, MPHY_PLL_LOCK_TIMEOUT_US, false, hba, + UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + + if (err) + dev_err(hba->dev, "M-PHY PLL lock timeout\n"); + + return err; +} + +/** + * ufs_spacemit_mphy_init + * @hba: host controller instance + */ +static int ufs_spacemit_mphy_init(struct ufs_hba *hba) +{ + int ret; + + /* reset all mphy logical */ + ufshcd_writel(hba, 0x003, UFS_PHY_MNG_BASE + UFS_MPHY_RST_CTRL); + + /* power up all */ + ufshcd_writel(hba, MPHY_PU_ALL, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + + /* asserted ana_rx_hb8_reset */ + ufshcd_writel(hba, 0xb7f, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + fsleep(500); + + /* deasserted ana_rx_hb8_reset */ + ufshcd_writel(hba, MPHY_PU_ALL, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + + /* Enable reference clock & deassert ufs device reset */ + ufshcd_writel(hba, MPHY_DEVICE_REFCLK_OE | MPHY_DEVICE_HW_RESET, + UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + + fsleep(1000); + + ret =3D ufs_spacemit_wait_mphy_pll_lock(hba); + if (ret < 0) + return ret; + + dev_dbg(hba->dev, "M-PHY PLL locked successfully\n"); + + ufshcd_writel(hba, 0x1, UFS_PHY_MNG_BASE + UFS_MPHY_BKDR_CTRL); + + ufshcd_writel(hba, 0x40, UFS_ATOP_BASE + (0xC2 << 2)); + + ufshcd_writel(hba, 0x0, UFS_PHY_MNG_BASE + UFS_MPHY_BKDR_CTRL); + + fsleep(2000); + + dev_dbg(hba->dev, "M-PHY init completed\n"); + + return 0; +} + +static int ufs_spacemit_uniprov1p6_init(struct ufs_hba *hba) +{ + static const struct ufshcd_dme_attr_val setup_attrs[] =3D { + { UIC_ARG_MIB(0x1552), 0x4f }, /* PA_TXHSG1SYNCLENGTH */ + { UIC_ARG_MIB(0x1553), 0xf }, /* PA_TXHSG1PREPARELENGTH */ + { UIC_ARG_MIB(0x1554), 0x4f }, /* PA_TXHSG2SYNCLENGTH */ + { UIC_ARG_MIB(0x1555), 0xf }, /* PA_TXHSG2PREPARELENGTH */ + { UIC_ARG_MIB(0x1556), 0x4f }, /* PA_TXHSG3SYNCLENGTH */ + { UIC_ARG_MIB(0x1557), 0xf }, /* PA_TXHSG3PREPARELENGTH */ + { UIC_ARG_MIB(0x155A), 0x0 }, /* PA_TXMK2EXTENSION */ + { UIC_ARG_MIB(0x155B), 0x1}, /* PA_PEERSCRAMBLING */ + { UIC_ARG_MIB(0x155C), 0x1 }, /* PA_TXSKIP */ + { UIC_ARG_MIB(0x155D), 250 }, /* PA_TXSKIPPERIOD */ + { UIC_ARG_MIB(0x155E), 0x0 }, /* PA_LOCAL_TX_LCC_ENABLE */ + { UIC_ARG_MIB(0x155F), 0x0 }, /* PA_PEER_TX_LCC_ENABLE */ + { UIC_ARG_MIB(0x1585), 0x1 }, /* PA_SCRAMBLING */ + { UIC_ARG_MIB(0x15AA), 0x1 }, /* PA_GRANULARITY */ + { UIC_ARG_MIB(0x15AB), 0x0 }, /* PA_MK2EXTENSIONGUARDBAND */ + { UIC_ARG_MIB(0x15A3), 15 }, /* PA_STALLNOCONFIGTIME */ + { UIC_ARG_MIB(0x15A8), 0x64 }, /* PA_TACTIVATE */ + { UIC_ARG_MIB(0x1564), 0x64 }, /* PA_TXTRAILINGCLOCKS */ + { UIC_ARG_MIB_SEL(0x008D, 4), 0x0B }, /* RX_LS_PREPARELEN_TIME RX0 */ + { UIC_ARG_MIB_SEL(0x008D, 5), 0x0B }, /* RX_LS_PREPARELEN_TIME RX1 */ + + /* RX_HIBERNATE_BKEN RX0 */ + { UIC_ARG_MIB_SEL(0x00F4, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x9F }, + /* RX_HIBERNATE_BKEN RX1 */ + { UIC_ARG_MIB_SEL(0x00F4, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x9F }, + /* PWM_BURST_closure_length */ + { UIC_ARG_MIB_SEL(0x008E, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 15 }, + { UIC_ARG_MIB_SEL(0x008E, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 15 }, + /* min_stall_not_config_time */ + { UIC_ARG_MIB_SEL(0x0088, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0xFF }, + { UIC_ARG_MIB_SEL(0x0088, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0xFF }, + /* TX HB8_TIME CAP */ + { UIC_ARG_MIB_SEL(0x000F, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0x64 }, + { UIC_ARG_MIB_SEL(0x000F, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), 0x64 }, + /* RX HB8_TIME CAP */ + { UIC_ARG_MIB_SEL(0x0092, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x64 }, + { UIC_ARG_MIB_SEL(0x0092, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x64 }, + /* TX EQ 3DB */ + { UIC_ARG_MIB_SEL(0x00CD, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0x5 }, + /* RX garbage cnt =3D 32 SI */ + { UIC_ARG_MIB_SEL(0x00F2, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x9F }, + { UIC_ARG_MIB_SEL(0x00F2, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x9F }, + }; + + return ufs_spacemit_dme_set(hba, setup_attrs, ARRAY_SIZE(setup_attrs)); +} + +static void ufs_spacemit_set_dev_cap(struct ufs_host_params *host_params, = u32 pwr_hs) +{ + if (!host_params) + return; + + memset(host_params, 0, sizeof(struct ufs_host_params)); + host_params->tx_lanes =3D UFS_SPACEMIT_K3_LIMIT_NUM_LANES_TX; + host_params->rx_lanes =3D UFS_SPACEMIT_K3_LIMIT_NUM_LANES_RX; + host_params->hs_rx_gear =3D UFS_SPACEMIT_K3_LIMIT_HSGEAR_RX; + host_params->hs_tx_gear =3D UFS_SPACEMIT_K3_LIMIT_HSGEAR_TX; + host_params->pwm_rx_gear =3D UFS_SPACEMIT_K3_LIMIT_PWMGEAR_RX; + host_params->pwm_tx_gear =3D UFS_SPACEMIT_K3_LIMIT_PWMGEAR_TX; + host_params->rx_pwr_pwm =3D UFS_SPACEMIT_K3_LIMIT_RX_PWR_PWM; + host_params->tx_pwr_pwm =3D UFS_SPACEMIT_K3_LIMIT_TX_PWR_PWM; + host_params->rx_pwr_hs =3D pwr_hs; + host_params->tx_pwr_hs =3D pwr_hs; + host_params->hs_rate =3D UFS_SPACEMIT_K3_LIMIT_HS_RATE; + host_params->desired_working_mode =3D UFS_HS_MODE; +} + +static int ufs_spacemit_link_startup_pre_change(struct ufs_hba *hba) +{ + u32 value, sys1clk_1us; + int err; + + err =3D ufs_spacemit_mphy_init(hba); + if (err < 0) + return err; + + err =3D ufs_spacemit_uniprov1p6_init(hba); + if (err < 0) + return err; + + /* config sysclk and tx symbol clk before link startup */ + value =3D UFS_MAX_LINKSTARTUP_TIMER; + + /* clear bit0~bit3, select b0 design */ + value &=3D ~0xf; + + ufshcd_writel(hba, value, UFS_PA_LINK_STARTUP_TIMER); + + sys1clk_1us =3D ufs_spacemit_get_sys1clk_1us(hba); + if (!sys1clk_1us) + sys1clk_1us =3D DIV_ROUND_CLOSEST(UFS_ACLK_LOW_FREQ_HZ, 1000000); + ufshcd_writel(hba, sys1clk_1us, UFS_SYS1CLK_1US); + ufshcd_writel(hba, UFS_TX_SYMBO_CLK, UFS_TX_SYMBOL_CLK_NS_US); + + dev_dbg(hba->dev, "REG_UFS_SYS1CLK_1US: 0x%x\n", + ufshcd_readl(hba, UFS_SYS1CLK_1US)); + dev_dbg(hba->dev, "REG_UFS_TX_SYMBOL_CLK_NS_US: 0x%x\n", + ufshcd_readl(hba, UFS_TX_SYMBOL_CLK_NS_US)); + + return 0; +} + +static int ufs_spacemit_link_startup_post_change(struct ufs_hba *hba) +{ + /* Add 0xe8 make UFS2.1 run GEAR3 + 2Lane@409M */ + static const struct ufshcd_dme_attr_val setup_attrs[] =3D { + { UIC_ARG_MIB_SEL(0xe8, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0x97 }, + { UIC_ARG_MIB_SEL(0xe8, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0xd7 }, + { UIC_ARG_MIB_SEL(0xe8, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0x17 }, + { UIC_ARG_MIB(DL_AFC0REQTIMEOUTVAL), UFS_DL_AFC0REQTIMEOUTVAL_MAX }, + }; + + return ufs_spacemit_dme_set(hba, setup_attrs, ARRAY_SIZE(setup_attrs)); +} + +static int ufs_spacemit_link_startup_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) +{ + int err =3D 0; + + switch (status) { + case PRE_CHANGE: + err =3D ufs_spacemit_link_startup_pre_change(hba); + break; + case POST_CHANGE: + err =3D ufs_spacemit_link_startup_post_change(hba); + break; + default: + break; + } + + return err; +} + +static int ufs_spacemit_negotiate_pwr_mode(struct ufs_hba *hba, + const struct ufs_pa_layer_attr *dev_max_params, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_host_params host_params; + int ret; + + ufs_spacemit_set_dev_cap(&host_params, FAST_MODE); + ret =3D ufshcd_negotiate_pwr_params(&host_params, dev_max_params, + dev_req_params); + if (ret) { + dev_err(hba->dev, "Failed to negotiate power params: %d\n", ret); + return ret; + } + + dev_dbg(hba->dev, + "Power mode config - gear_rx:%d, gear_tx:%d, lane_rx:%d, lane_tx:%d, pwr= _rx:%d, pwr_tx:%d, hs_rate:%d\n", + dev_req_params->gear_rx, dev_req_params->gear_tx, dev_req_params->lane_r= x, + dev_req_params->lane_tx, dev_req_params->pwr_rx, dev_req_params->pwr_tx, + dev_req_params->hs_rate); + + return ret; +} + +static int ufs_spacemit_pwr_change_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status, + struct ufs_pa_layer_attr *dev_req_params) +{ + struct ufs_spacemit_host *host =3D ufshcd_get_variant(hba); + int ret =3D 0; + + if (!dev_req_params) { + dev_err(hba->dev, "Invalid Parameters\n"); + return -EINVAL; + } + + switch (status) { + case PRE_CHANGE: + break; + case POST_CHANGE: + /* Cache the power mode parameters to use internally */ + memcpy(&host->dev_req_params, dev_req_params, sizeof(*dev_req_params)); + ret =3D ufs_spacemit_wait_mphy_pll_lock(hba); + if (ret < 0) + return ret; + + dev_dbg(hba->dev, "M-PHY PLL locked after power mode change\n"); + /* Set ANA_HSGEAR_CTRL_ATTR back to default value */ + ufshcd_dme_set(hba, UIC_ARG_MIB(ANA_HSGEAR_CTRL_ATTR), 0x00); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int ufs_spacemit_quirk_host_pa_saveconfigtime(struct ufs_hba *hba) +{ + int err; + u32 pa_vs_config_reg1; + + err =3D ufshcd_dme_get(hba, UIC_ARG_MIB(UFS_PA_VS_CONFIG_REG1), &pa_vs_co= nfig_reg1); + if (err) + return err; + + /* Allow extension of MSB bits of PA_SaveConfigTime attribute */ + return ufshcd_dme_set(hba, UIC_ARG_MIB(UFS_PA_VS_CONFIG_REG1), + (pa_vs_config_reg1 | (1 << 12))); +} + +static int ufs_spacemit_apply_dev_quirks(struct ufs_hba *hba) +{ + static const struct ufshcd_dme_attr_val setup_attrs[] =3D { + /* LCC_DISABLE */ + { UIC_ARG_MIB_SEL(TX_LCC_ENABLE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)), 0 }, + { UIC_ARG_MIB_SEL(TX_LCC_ENABLE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1)), 0 }, + /* TX_Min_ActivateTime */ + { UIC_ARG_MIB_SEL(TX_MIN_ACTIVATETIME, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0))= , 0x0 }, + { UIC_ARG_MIB_SEL(TX_MIN_ACTIVATETIME, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(1))= , 0x0 }, + { UIC_ARG_MIB(ANA_HSGEAR_CTRL_ATTR), 0x25 }, + }; + int err; + + if (hba->dev_quirks & UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME) + ufs_spacemit_quirk_host_pa_saveconfigtime(hba); + + if (hba->dev_info.wmanufacturerid =3D=3D UFS_VENDOR_WDC) + hba->dev_quirks |=3D UFS_DEVICE_QUIRK_HOST_PA_TACTIVATE; + + err =3D ufs_spacemit_dme_set(hba, setup_attrs, ARRAY_SIZE(setup_attrs)); + if (err < 0) + return err; + + return ufs_spacemit_wait_mphy_pll_lock(hba); +} + +/** + * ufs_spacemit_advertise_quirks - advertise the known Spacemit UFS contro= ller quirks + * @hba: host controller instance + * + * Spacemit UFS host controller might have some non standard behaviours (q= uirks) + * than what is specified by UFSHCI specification. Advertise all such + * quirks to standard UFS host controller driver so standard takes them in= to + * account. + */ +static void ufs_spacemit_advertise_quirks(struct ufs_hba *hba) +{ + hba->quirks |=3D UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; +} + +/** + * ufs_spacemit_init - init phy and prepare clk + * @hba: host controller instance + */ +static int ufs_spacemit_init(struct ufs_hba *hba) +{ + struct device *dev =3D hba->dev; + struct ufs_spacemit_host *host; + struct reset_control *rst; + + host =3D devm_kzalloc(dev, sizeof(*host), GFP_KERNEL); + if (!host) + return -ENOMEM; + + rst =3D devm_reset_control_get_exclusive_deasserted(dev, NULL); + if (IS_ERR(rst)) + return dev_err_probe(dev, PTR_ERR(rst), "Failed to get reset control\n"); + + host->hba =3D hba; + ufshcd_set_variant(hba, host); + ufs_spacemit_advertise_quirks(hba); + + return 0; +} + +/** + * ufs_spacemit_device_reset - Toggle device reset line + * @hba: per-adapter instance + * + * Toggles the reset line to reset the attached UFS device. + * + * Returns: 0 on success + */ +static int ufs_spacemit_device_reset(struct ufs_hba *hba) +{ + /* Disable reference clock & asserted ufs device reset */ + ufshcd_writel(hba, 0x000, UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + usleep_range(10, 15); + + /* Enable reference clock & deassert ufs device reset */ + ufshcd_writel(hba, MPHY_DEVICE_REFCLK_OE | MPHY_DEVICE_HW_RESET, + UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + usleep_range(10, 15); + + return 0; +} + +/** + * ufs_spacemit_event_notify - Handle UFS error events + * @hba: host controller instance + * @evt: event type + * @data: event-specific data + * + * Handles error events from UFS core, print warning messages + */ +static void ufs_spacemit_event_notify(struct ufs_hba *hba, enum ufs_event_= type evt, void *data) +{ + switch (evt) { + case UFS_EVT_PA_ERR: + if (data) + dev_warn(hba->dev, "PA error event, INT errors:0x%x, PA_ERR_CODE:0x%x\n= ", + hba->errors, *(u32 *)data); + + break; + case UFS_EVT_DL_ERR: + if (data) + dev_warn(hba->dev, "DL error event, INT errors:0x%x, DL_ERR:0x%x\n", + hba->errors, *(u32 *)data); + + break; + case UFS_EVT_ABORT: + dev_warn(hba->dev, "Abort event, INT errors:0x%x\n", hba->errors); + break; + default: + break; + } +} + +static void ufs_spacemit_pre_hibern8(struct ufs_hba *hba, enum uic_cmd_dme= cmd) +{ + static const struct ufshcd_dme_attr_val setup_attrs[] =3D { + { UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x84 }, + { UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x84 }, + { UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x85 }, + { UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x85 }, + }; + int ret; + + if (cmd =3D=3D UIC_CMD_DME_HIBER_EXIT) { + /* Enable reference clock & deassert ufs device reset */ + ufshcd_writel(hba, MPHY_DEVICE_REFCLK_OE | MPHY_DEVICE_HW_RESET, + UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + + /* Power up all */ + ufshcd_writel(hba, MPHY_PU_ALL, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + + /* Assert ana_rx_hb8_reset */ + ufshcd_writel(hba, MPHY_PU_WITH_HB8_RESET, + UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + fsleep(500); + + /* Deassert ana_rx_hb8_reset */ + ufshcd_writel(hba, MPHY_PU_ALL, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + + ret =3D ufs_spacemit_wait_mphy_pll_lock(hba); + if (ret < 0) + return; + + ufshcd_dme_set(hba, UIC_ARG_MIB(0xdd), 0x57); + ufshcd_dme_set(hba, UIC_ARG_MIB(0xe8), 0x57); + } + + if (cmd =3D=3D UIC_CMD_DME_HIBER_ENTER) + ufs_spacemit_dme_set(hba, setup_attrs, ARRAY_SIZE(setup_attrs)); +} + +static void ufs_spacemit_post_hibern8(struct ufs_hba *hba, enum uic_cmd_dm= e cmd) +{ + static const struct ufshcd_dme_attr_val setup_attrs[] =3D { + { UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x84 }, + { UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x84 }, + { UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 0x80 }, + { UIC_ARG_MIB_SEL(0xf1, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(1)), 0x80 }, + { UIC_ARG_MIB(0xdd), 0x57 }, + { UIC_ARG_MIB(0xdd), 0xd7 }, + { UIC_ARG_MIB(0xe8), 0x57 }, + { UIC_ARG_MIB(0xe8), 0xd7 }, + }; + int err; + + if (cmd =3D=3D UIC_CMD_DME_HIBER_ENTER) { + err =3D ufs_spacemit_check_hibern8(hba); + if (err) + dev_err(hba->dev, "ufs_spacemit_check_hibern8 error\n"); + + ufs_spacemit_dme_set(hba, setup_attrs, ARRAY_SIZE(setup_attrs)); + + /* Power down M-PHY */ + ufshcd_writel(hba, 0x0, UFS_PHY_MNG_BASE + UFS_MPHY_PU_CTRL); + + /* Disable reference clock, keep device reset deasserted */ + ufshcd_writel(hba, MPHY_DEVICE_HW_RESET, + UFS_PHY_MNG_BASE + UFS_DEVICE_IO_CTRL); + } +} + +/** + * ufs_spacemit_hibern8_notify - Handle hibernate enter/exit + * @hba: host controller instance + * @cmd: UIC command (HIBER_ENTER or HIBER_EXIT) + * @status: notification status + * + * Manages M-PHY power state during hibernate transitions. + */ +static void ufs_spacemit_hibern8_notify(struct ufs_hba *hba, enum uic_cmd_= dme cmd, + enum ufs_notify_change_status status) +{ + if (status =3D=3D PRE_CHANGE) + ufs_spacemit_pre_hibern8(hba, cmd); + + if (status =3D=3D POST_CHANGE) + ufs_spacemit_post_hibern8(hba, cmd); +} + +/** + * struct ufs_hba_spacemit_vops - UFS Spacemit specific variant operations + * + * The variant operations configure the necessary controller and PHY + * handshake during initialization. + */ +static const struct ufs_hba_variant_ops ufs_hba_spacemit_vops =3D { + .name =3D "ufshcd-spacemit", + .init =3D ufs_spacemit_init, + .link_startup_notify =3D ufs_spacemit_link_startup_notify, + .negotiate_pwr_mode =3D ufs_spacemit_negotiate_pwr_mode, + .pwr_change_notify =3D ufs_spacemit_pwr_change_notify, + .device_reset =3D ufs_spacemit_device_reset, + .event_notify =3D ufs_spacemit_event_notify, + .apply_dev_quirks =3D ufs_spacemit_apply_dev_quirks, + .hibern8_notify =3D ufs_spacemit_hibern8_notify, +}; + +static const struct of_device_id __maybe_unused ufs_spacemit_of_match[] = =3D { + { .compatible =3D "spacemit,k3-ufshc" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ufs_spacemit_of_match); + +static int ufs_spacemit_probe(struct platform_device *pdev) +{ + return ufshcd_pltfrm_init(pdev, &ufs_hba_spacemit_vops); +} + +static void ufs_spacemit_remove(struct platform_device *pdev) +{ + ufshcd_pltfrm_remove(pdev); +} + +static struct platform_driver ufs_spacemit_pltform =3D { + .probe =3D ufs_spacemit_probe, + .remove =3D ufs_spacemit_remove, + .driver =3D { + .name =3D "ufshcd-spacemit", + .of_match_table =3D of_match_ptr(ufs_spacemit_of_match), + }, +}; +module_platform_driver(ufs_spacemit_pltform); + +MODULE_DESCRIPTION("SpacemiT UFS Host Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ufs/host/ufs-spacemit.h b/drivers/ufs/host/ufs-spacemi= t.h new file mode 100644 index 000000000000..5e459f6ba243 --- /dev/null +++ b/drivers/ufs/host/ufs-spacemit.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SpacemiT UFS Host Controller driver + * + * Copyright (c) 2026 SpacemiT (Hangzhou) Technology Co. Ltd + */ + +#ifndef _UFS_SPACEMIT_H_ +#define _UFS_SPACEMIT_H_ + +#include +#include +#include + +/* Spacemit K3 UFS host controller vendor specific registers */ +#define UFS_SYS1CLK_1US 0xC0 +#define UFS_TX_SYMBOL_CLK_NS_US 0xC4 +#define UFS_LOCAL_PORT_ID_REG 0xC8 +#define UFS_PA_ERR_CODE 0xCC +#define UFS_RETRY_TIMER_REG 0xD0 +#define UFS_PA_LINK_STARTUP_TIMER 0xD8 +#define UFS_CFG1 0xDC + +/* MPHY control registers */ +#define UFS_PHY_MNG_BASE 0x1B00 +#define UFS_MPHY_RST_CTRL 0x0 +#define UFS_MPHY_PU_CTRL 0x4 +#define UFS_MPHY_BKDR_CTRL 0x8 +#define UFS_DEVICE_IO_CTRL 0xC + +/* ATOP base*/ +#define UFS_ATOP_BASE 0x1C00 + +#define UFS_SYSCLK 499 +#define UFS_TX_SYMBO_CLK 0x800 +#define UFS_MAX_LINKSTARTUP_TIMER 0xFFFFFFFF +#define UFS_DL_AFC0REQTIMEOUTVAL_MAX 0xFFFF + +#define MPHY_TX_FSM_STATE 0x41 +#define TX_FSM_HIBERN8 0x1 +#define HBRN8_POLL_TOUT_MS 100 +#define DEFAULT_CLK_RATE_HZ 1000000 +#define BUS_VECTOR_NAME_LEN 32 + +#define UFS_HW_VER_MAJOR_SHFT 28 +#define UFS_HW_VER_MAJOR_MASK (0x000F << UFS_HW_VER_MAJOR_SHFT) +#define UFS_HW_VER_MINOR_SHFT 16 +#define UFS_HW_VER_MINOR_MASK (0x0FFF << UFS_HW_VER_MINOR_SHFT) +#define UFS_HW_VER_STEP_SHFT 0 +#define UFS_HW_VER_STEP_MASK (0xFFFF << UFS_HW_VER_STEP_SHFT) + +#define UFS_SPACEMIT_K3_LIMIT_NUM_LANES_RX 2 +#define UFS_SPACEMIT_K3_LIMIT_NUM_LANES_TX 2 +#define UFS_SPACEMIT_K3_LIMIT_HSGEAR_RX UFS_HS_G3 +#define UFS_SPACEMIT_K3_LIMIT_HSGEAR_TX UFS_HS_G3 +#define UFS_SPACEMIT_K3_LIMIT_PWMGEAR_RX UFS_PWM_G4 +#define UFS_SPACEMIT_K3_LIMIT_PWMGEAR_TX UFS_PWM_G4 +#define UFS_SPACEMIT_K3_LIMIT_RX_PWR_PWM SLOW_MODE +#define UFS_SPACEMIT_K3_LIMIT_TX_PWR_PWM SLOW_MODE +#define UFS_SPACEMIT_K3_LIMIT_RX_PWR_HS FAST_MODE +#define UFS_SPACEMIT_K3_LIMIT_TX_PWR_HS FAST_MODE +#define UFS_SPACEMIT_K3_LIMIT_HS_RATE PA_HS_MODE_B +#define UFS_SPACEMIT_K3_LIMIT_DESIRED_MODE 2 + +#define UFS_PA_VS_CONFIG_REG1 0x9000 +#define UFS_DME_VS_CORE_CLK_CTRL 0xD002 + +#define UFS_HCLKDIV_REG 0xFC + +struct ufs_spacemit_host { + struct ufs_hba *hba; + struct ufs_pa_layer_attr dev_req_params; +}; + +struct ufshcd_dme_attr_val { + u32 attr_sel; + u32 mib_val; +}; +#endif /* _UFS_SPACEMIT_H_ */ --=20 2.55.0 From nobody Fri Sep 25 22:19:12 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 70F9D3A9D9A; Tue, 8 Sep 2026 07:42:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788853362; cv=none; b=MQ+OsH5JzHr1iymR9qmkjv0nzQZdSkKWo45Hg48EdZLlSjpmCg2zUvVfSgM5ps9GRtWSBptDTRoUK+P+GTTi9++qqZ33umi/fwlojNG1X7Cqvn9cKkBJkb5BF2pppda3iYbbOgiVfJnxCd8b3gPG5trwW3J2nY4JlBHr+vU4JqU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788853362; c=relaxed/simple; bh=rXPgP4vqes+SSdiq8m/XbuntuYSVI7j++o0JqVbGZh8=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:References: In-Reply-To:To:Cc; b=WJ9GdS181gU6TR/2ujMaRYxTXd6PcDtX/nHGY5PUFyNmVWnTaUCM6MPueGnc5p5+O1wSRcZfXVIAvu0RkUyswWRms4xLV77aCSPvneEOI3gtUVEb2rF8asSZg2BXx5Ki70raMssu7Kcr4x3ZsucSi05TA1T0nwtFZplRYPicqls= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FNVRszPG; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="FNVRszPG" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1F6AF1F00A3E; Tue, 8 Sep 2026 07:42:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1788853361; bh=3oXpl+cAhOXilhzd2THpb81XUKM3ZmVG18zD0Do4tnk=; h=From:Date:Subject:References:In-Reply-To:To:Cc; b=FNVRszPGPHIJ1I1naUyZH3rt9LzQzHmvIHrNEtDbKyH8JbYraaPOuZ/lrR2kVyQjp 3vm/P1J9KxfHo7NOLnqLlM/j+QQCw/0ibZVC7f9l9ut9RE/UQE8pgarhBg3IAZzcxA TyQ78cojxhrbIJWZflYs/qmLY104rBXoYkIM49Tms/r9d4mVYLH1QrXD9TVjVOBNj1 QxPZe12l6xA97sCJyk7Ucbq3nbAc6XZJCqDfVC1ZEmxmSUksyR4nhovYaZmPHRCpOE ptwMHy3TuLKwynMg7mMzT/7us0onwOf0Gvw+oaYuxCEbbUysjP0AtGIS0+gYSygq/K EQcOGnOJ3ONDQ== From: Yixun Lan Date: Tue, 08 Sep 2026 07:42:05 +0000 Subject: [PATCH v4 3/3] riscv: dts: spacemit: k3: Add UFS support Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260908-08-k3-ufs-support-v4-3-22aa2bd97d3b@kernel.org> References: <20260908-08-k3-ufs-support-v4-0-22aa2bd97d3b@kernel.org> In-Reply-To: <20260908-08-k3-ufs-support-v4-0-22aa2bd97d3b@kernel.org> To: Alim Akhtar , Avri Altman , Bart Van Assche , Rob Herring , Krzysztof Kozlowski , Conor Dooley , "James E.J. Bottomley" , Philipp Zabel , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , "Martin K. Petersen" Cc: Anirudh Srinivasan , Aurelien Jarno , Jennifer Berringer , linux-scsi@vger.kernel.org, devicetree@vger.kernel.org, linux-riscv@lists.infradead.org, spacemit@lists.linux.dev, linux-kernel@vger.kernel.org, Yixun Lan X-Mailer: b4 0.16.0 X-Developer-Signature: v=1; a=openpgp-sha256; l=2292; i=dlan@kernel.org; h=from:subject:message-id; bh=rXPgP4vqes+SSdiq8m/XbuntuYSVI7j++o0JqVbGZh8=; b=owEB6QIW/ZANAwAKATGq6kdZTbvtAcsmYgBqn7xYqMxLbATL8V3wklbt+Smix+P61sIZ311IS bHc8of/EDeJAq8EAAEKAJkWIQS1urjJwxtxFWcCI9wxqupHWU277QUCap+8WBsUgAAAAAAEAA5t YW51MiwyLjUrMS4xMiwyLDJfFIAAAAAALgAoaXNzdWVyLWZwckBub3RhdGlvbnMub3BlbnBncC5 maWZ0aGhvcnNlbWFuLm5ldEI1QkFCOEM5QzMxQjcxMTU2NzAyMjNEQzMxQUFFQTQ3NTk0REJCRU QACgkQMarqR1lNu+2vfA/+NrZfduXKP9hZ2XCJBScqdSDVBuP4Euoa7ixdAw9DUMHA+biwb5dyQ B9kzqN6sgVRGqvmqGwM3/oANjmlpgeK0bueAmzYTWptU6+k+wORkk1cgF1R5g1lbYaRmwMhnQwg zihkcFyUJbHNnUeYLWgqIX+hBXJUuC8Cf+fN+XV1KHhxeJKD7HBV72dUk4KmoMt8oEPl2iZWB1N lh3D/VypQJQKcGU938pcvCiVFN33/X4pFJi1Kez4AJcqIoM9fiPKRlPGYpNq/SjzS38SxJW9Z5d rQmcTRXzj/nW/HWia1Kr7j+ZhflIQV9QVax3c45xteRjEikXjL7fADcqa+3ibTbfsVu6z6R5gWd XolQ1+aI58weEGk9XWZeU3Po/5UlN3PLneuQMnf6lIhkyAP23RVEWTku5UTiCpdrcHVKudOWOTw gFmGOU4xCJzcKQqdxM7sweYHaVCcm1oOGjWDc4dIsIUC7bFNZMFTrdVsL2qTyt07azZWMQqMGE/ QC8n4j/Tg8Njh3aTWlcsCmXZ2HkUen9JzvyotjXo+9xFtjkcvhQxw+GuLInQklN7VehLOhChDqv wG8KPC6HO6PcFp8vqOqlp2wkRFZIGL+V6P+aF191pxnQVHlTxsYJP9/JqjecyosQitMDTtWPmO0 Sm8Ug01/ojY5IqZ0TnGGvrsn7mPeCM= X-Developer-Key: i=dlan@kernel.org; a=openpgp; fpr=50B03A1A5CBCD33576EF8CD7920C0DBCAABEFD55 Add UFS Host Controller support for SpacemiT K3 SoC, and enable it both on Pico-ITX and CoM260-IFX boards. Tested-by: Aurelien Jarno Signed-off-by: Yixun Lan Tested-by: Anirudh Srinivasan --- arch/riscv/boot/dts/spacemit/k3-com260.dtsi | 4 ++++ arch/riscv/boot/dts/spacemit/k3-pico-itx.dts | 4 ++++ arch/riscv/boot/dts/spacemit/k3.dtsi | 22 ++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/arch/riscv/boot/dts/spacemit/k3-com260.dtsi b/arch/riscv/boot/= dts/spacemit/k3-com260.dtsi index 37627963a649..3251ee8405de 100644 --- a/arch/riscv/boot/dts/spacemit/k3-com260.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3-com260.dtsi @@ -220,3 +220,7 @@ &uart0 { pinctrl-0 =3D <&uart0_0_cfg>; status =3D "okay"; }; + +&ufshc { + status =3D "okay"; +}; diff --git a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts b/arch/riscv/boot= /dts/spacemit/k3-pico-itx.dts index b084a35aa00e..6369602e81da 100644 --- a/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts +++ b/arch/riscv/boot/dts/spacemit/k3-pico-itx.dts @@ -307,6 +307,10 @@ &uart0 { status =3D "okay"; }; =20 +&ufshc { + status =3D "okay"; +}; + &usb2_host { #address-cells =3D <1>; #size-cells =3D <0>; diff --git a/arch/riscv/boot/dts/spacemit/k3.dtsi b/arch/riscv/boot/dts/spa= cemit/k3.dtsi index c3f2dce0969c..436c0e7e2838 100644 --- a/arch/riscv/boot/dts/spacemit/k3.dtsi +++ b/arch/riscv/boot/dts/spacemit/k3.dtsi @@ -513,6 +513,28 @@ usb2_phy: phy@c0a20000 { status =3D "disabled"; }; =20 + ufshc: ufshc@c0e00000 { + compatible =3D "spacemit,k3-ufshc"; + reg =3D <0x0 0xc0e00000 0x0 0x40000>; + clocks =3D <&syscon_apmu CLK_APMU_UFS_ACLK>, + <&syscon_apmu CLK_APMU_UFS_REFCLK>; + clock-names =3D "aclk", "ref_clk"; + resets =3D <&syscon_apmu RESET_APMU_UFS_ACLK>; + interrupts =3D <135 IRQ_TYPE_LEVEL_HIGH>; + lanes-per-direction =3D <2>; + operating-points-v2 =3D <&ufs_opp_table>; + status =3D "disabled"; + + ufs_opp_table: opp-table { + compatible =3D "operating-points-v2"; + + opp-1 { + opp-hz =3D /bits/ 64 <491520000>, + /bits/ 64 <19200000>; + }; + }; + }; + eth0: ethernet@cac80000 { compatible =3D "spacemit,k3-dwmac", "snps,dwmac-5.40a"; reg =3D <0x0 0xcac80000 0x0 0x2000>; --=20 2.55.0