Devicetree
 help / color / mirror / Atom feed
* [PATCH 00/10] Add S32N79RDB UFS support
@ 2026-08-26 13:40 Larisa Grigore
  2026-08-26 13:40 ` [PATCH 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h Larisa Grigore
                   ` (9 more replies)
  0 siblings, 10 replies; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

This series adds support for the UFS host controller found on the NXP S32N79
platform. The controller is based on the Synopsys DesignWare (DWC) UFS host
architecture and requires S32N79-specific M-PHY initialization/calibration
sequence. It is compliant with JESD-223D "Universal Flash Storage Host
Controller Interface" v3.0, and supports up to HS-Gear 4 speed grade. It is
compatible with UFS 2.1, 3.0 and 3.1 devices. The MIPI UniPro is compliant with
"MIPI Alliance Specification for Unified Protocol", v1.8. The MIPI M-PHY is
compliant to version 4.1.

In order to keep the S32N79 platform driver small and avoid duplicating common
DWC code across platform glue drivers, the series also refactors and exports a
few reusable DWC helpers. In particular, M-PHY CREG (CBCREG*) register access
helpers are moved from the existing AMD Versal2 glue driver into the common
ufshcd-dwc layer, and are then used by the platform drivers. The series also
introduces a generic Hibern8 TX-FSM polling helper so that multiple platforms
can reuse the same pre-link checks.

Device tree binding documentation for the S32N79 UFS controller is included,
along with SoC and board DTS updates to instantiate and enable the controller.

Larisa Grigore (10):
  ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h
  ufs: unipro: Add TX/RX FSM state attributes
  ufs: host: Add common Hibern8 TX FSM polling helper
  scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc
  scsi: ufs: dwc: Export common clock divider and link status helpers
  dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform
    driver
  scsi: ufs: Add NXP S32N79 UFS host controller driver
  arm64: dts: freescale: s32n79: Add UFS host controller
  arm64: dts: freescale: s32n79-rdb: Enable UFS
  MAINTAINERS: Add NXP S32N7 UFS host controller entry

 .../bindings/ufs/nxp,s32n7-ufshc.yaml         |  99 ++++
 MAINTAINERS                                   |   8 +
 arch/arm64/boot/dts/freescale/s32n79-rdb.dts  |   6 +
 arch/arm64/boot/dts/freescale/s32n79.dtsi     |  11 +
 drivers/ufs/host/Kconfig                      |  12 +
 drivers/ufs/host/Makefile                     |   1 +
 drivers/ufs/host/ufs-amd-versal2.c            |  89 +---
 drivers/ufs/host/ufs-hisi.c                   |  48 +-
 drivers/ufs/host/ufs-hisi.h                   |   6 -
 drivers/ufs/host/ufs-nxp-s32n7.c              | 464 ++++++++++++++++++
 drivers/ufs/host/ufs-qcom.c                   |  42 +-
 drivers/ufs/host/ufs-qcom.h                   |   2 -
 drivers/ufs/host/ufshcd-dwc.c                 |  83 +++-
 drivers/ufs/host/ufshcd-dwc.h                 |  36 +-
 drivers/ufs/host/ufshcd-pltfrm.c              |  79 +++
 drivers/ufs/host/ufshcd-pltfrm.h              |   2 +
 include/ufs/unipro.h                          |  21 +
 17 files changed, 817 insertions(+), 192 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
 create mode 100644 drivers/ufs/host/ufs-nxp-s32n7.c

-- 
2.43.0


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 15:05   ` Frank Li
  2026-08-26 13:40 ` [PATCH 02/10] ufs: unipro: Add TX/RX FSM state attributes Larisa Grigore
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Move the Tx/Rx FSM state definitions from ufshcd-dwc.h to
include/ufs/unipro.h.

These state values are UniPro/M-PHY related and are not specific to
the DesignWare UFS host controller implementation, so unipro.h is a
more appropriate location for sharing them.

As part of this move, drop the driver-private TX_FSM_HIBERN8 define in
ufs-hisi and ufs-qcom and repoint their Hibern8 checks at the shared
TX_STATE_HIBERN8 enum constant. This is a no-op change: TX_FSM_HIBERN8
was 0x1 and TX_STATE_HIBERN8 is 1, so the compared values are identical.

This also prepares for using these definitions in a subsequent commit.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 drivers/ufs/host/ufs-hisi.c   |  8 ++++----
 drivers/ufs/host/ufs-hisi.h   |  1 -
 drivers/ufs/host/ufs-qcom.c   |  4 ++--
 drivers/ufs/host/ufs-qcom.h   |  1 -
 drivers/ufs/host/ufshcd-dwc.h | 19 -------------------
 include/ufs/unipro.h          | 19 +++++++++++++++++++
 6 files changed, 25 insertions(+), 27 deletions(-)

diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
index 993e20ac211d..2d214ce9620e 100644
--- a/drivers/ufs/host/ufs-hisi.c
+++ b/drivers/ufs/host/ufs-hisi.c
@@ -34,8 +34,8 @@ static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
 				      &tx_fsm_val_0);
 		err |= ufshcd_dme_get(hba,
 		    UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), &tx_fsm_val_1);
-		if (err || (tx_fsm_val_0 == TX_FSM_HIBERN8 &&
-			tx_fsm_val_1 == TX_FSM_HIBERN8))
+		if (err || (tx_fsm_val_0 == TX_STATE_HIBERN8 &&
+			tx_fsm_val_1 == TX_STATE_HIBERN8))
 			break;
 
 		/* sleep for max. 200us */
@@ -56,8 +56,8 @@ static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
 	if (err) {
 		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
 			__func__, err);
-	} else if (tx_fsm_val_0 != TX_FSM_HIBERN8 ||
-			 tx_fsm_val_1 != TX_FSM_HIBERN8) {
+	} else if (tx_fsm_val_0 != TX_STATE_HIBERN8 ||
+			 tx_fsm_val_1 != TX_STATE_HIBERN8) {
 		err = -1;
 		dev_err(hba->dev, "%s: invalid TX_FSM_STATE, lane0 = %d, lane1 = %d\n",
 			__func__, tx_fsm_val_0, tx_fsm_val_1);
diff --git a/drivers/ufs/host/ufs-hisi.h b/drivers/ufs/host/ufs-hisi.h
index 5a90c0f4e90c..6af6a268458d 100644
--- a/drivers/ufs/host/ufs-hisi.h
+++ b/drivers/ufs/host/ufs-hisi.h
@@ -43,7 +43,6 @@
  * M-TX Configuration Attributes for Hixxxx
  */
 #define MPHY_TX_FSM_STATE	0x41
-#define TX_FSM_HIBERN8	0x1
 
 /*
  * Hixxxx UFS HC specific Registers
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 291c43448764..75f569dd5cfe 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -393,7 +393,7 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
 				UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE,
 					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
 				&tx_fsm_val);
-		if (err || tx_fsm_val == TX_FSM_HIBERN8)
+		if (err || tx_fsm_val == TX_STATE_HIBERN8)
 			break;
 
 		/* sleep for max. 200us */
@@ -413,7 +413,7 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
 	if (err) {
 		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
 				__func__, err);
-	} else if (tx_fsm_val != TX_FSM_HIBERN8) {
+	} else if (tx_fsm_val != TX_STATE_HIBERN8) {
 		err = tx_fsm_val;
 		dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n",
 				__func__, err);
diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
index e20b3ca50577..b7b68333fc32 100644
--- a/drivers/ufs/host/ufs-qcom.h
+++ b/drivers/ufs/host/ufs-qcom.h
@@ -11,7 +11,6 @@
 #include <ufs/ufshcd.h>
 
 #define MPHY_TX_FSM_STATE       0x41
-#define TX_FSM_HIBERN8          0x1
 #define HBRN8_POLL_TOUT_MS      100
 #define DEFAULT_CLK_RATE_HZ     1000000
 #define MAX_SUPP_MAC		64
diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
index c618bb914904..a5e40e7604a1 100644
--- a/drivers/ufs/host/ufshcd-dwc.h
+++ b/drivers/ufs/host/ufshcd-dwc.h
@@ -39,25 +39,6 @@
 #define RX_AFE_CTLE_IDAC(n)	(0x4001 + ((n) * 0x100))
 #define FW_CALIB_CCFG(n)	(0x404D + ((n) * 0x100))
 
-/* Tx/Rx FSM state */
-enum rx_fsm_state {
-	RX_STATE_DISABLED = 0,
-	RX_STATE_HIBERN8 = 1,
-	RX_STATE_SLEEP = 2,
-	RX_STATE_STALL = 3,
-	RX_STATE_LSBURST = 4,
-	RX_STATE_HSBURST = 5,
-};
-
-enum tx_fsm_state {
-	TX_STATE_DISABLED = 0,
-	TX_STATE_HIBERN8 = 1,
-	TX_STATE_SLEEP = 2,
-	TX_STATE_STALL = 3,
-	TX_STATE_LSBURST = 4,
-	TX_STATE_HSBURST = 5,
-};
-
 struct ufshcd_dme_attr_val {
 	u32 attr_sel;
 	u32 mib_val;
diff --git a/include/ufs/unipro.h b/include/ufs/unipro.h
index 9c168703b104..bf063edc284d 100644
--- a/include/ufs/unipro.h
+++ b/include/ufs/unipro.h
@@ -466,4 +466,23 @@ enum {
 	CPORT_CONNECTED,
 };
 
+/* Tx/Rx FSM state */
+enum ufs_rx_fsm_state {
+	RX_STATE_DISABLED = 0,
+	RX_STATE_HIBERN8 = 1,
+	RX_STATE_SLEEP = 2,
+	RX_STATE_STALL = 3,
+	RX_STATE_LSBURST = 4,
+	RX_STATE_HSBURST = 5,
+};
+
+enum ufs_tx_fsm_state {
+	TX_STATE_DISABLED = 0,
+	TX_STATE_HIBERN8 = 1,
+	TX_STATE_SLEEP = 2,
+	TX_STATE_STALL = 3,
+	TX_STATE_LSBURST = 4,
+	TX_STATE_HSBURST = 5,
+};
+
 #endif /* _UNIPRO_H_ */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 02/10] ufs: unipro: Add TX/RX FSM state attributes
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
  2026-08-26 13:40 ` [PATCH 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 15:09   ` Frank Li
  2026-08-26 13:40 ` [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper Larisa Grigore
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Add TX_FSM_STATE and RX_FSM_STATE to include/ufs/unipro.h, as these are
UniPro M-PHY attribute definitions rather than host controller specific
ones.

Remove the duplicated local definitions from the HiSilicon, Qualcomm, AMD
Versal and DesignWare UFS headers.

This also prepares for using these definitions in a subsequent commit.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 drivers/ufs/host/ufs-amd-versal2.c | 4 ++--
 drivers/ufs/host/ufs-hisi.c        | 8 ++++----
 drivers/ufs/host/ufs-hisi.h        | 5 -----
 drivers/ufs/host/ufs-qcom.c        | 4 ++--
 drivers/ufs/host/ufs-qcom.h        | 1 -
 drivers/ufs/host/ufshcd-dwc.h      | 4 ----
 include/ufs/unipro.h               | 2 ++
 7 files changed, 10 insertions(+), 18 deletions(-)

diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c
index 2154d6286817..0d4d5e98ad65 100644
--- a/drivers/ufs/host/ufs-amd-versal2.c
+++ b/drivers/ufs/host/ufs-amd-versal2.c
@@ -112,7 +112,7 @@ static int ufs_versal2_enable_phy(struct ufs_hba *hba)
 		u32 time_left, mibsel;
 
 		time_left = TIMEOUT_MICROSEC;
-		mibsel = UIC_ARG_MIB_SEL(MTX_FSM_STATE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(offset));
+		mibsel = UIC_ARG_MIB_SEL(TX_FSM_STATE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(offset));
 		do {
 			ret = ufshcd_dme_get(hba, mibsel, &reg);
 			if (ret)
@@ -132,7 +132,7 @@ static int ufs_versal2_enable_phy(struct ufs_hba *hba)
 		}
 
 		time_left = TIMEOUT_MICROSEC;
-		mibsel = UIC_ARG_MIB_SEL(MRX_FSM_STATE, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(offset));
+		mibsel = UIC_ARG_MIB_SEL(RX_FSM_STATE, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(offset));
 		do {
 			ret = ufshcd_dme_get(hba, mibsel, &reg);
 			if (ret)
diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
index 2d214ce9620e..bd223bda1ce2 100644
--- a/drivers/ufs/host/ufs-hisi.c
+++ b/drivers/ufs/host/ufs-hisi.c
@@ -30,10 +30,10 @@ static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
 	unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
 
 	do {
-		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 0),
+		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
 				      &tx_fsm_val_0);
 		err |= ufshcd_dme_get(hba,
-		    UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), &tx_fsm_val_1);
+		    UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
 		if (err || (tx_fsm_val_0 == TX_STATE_HIBERN8 &&
 			tx_fsm_val_1 == TX_STATE_HIBERN8))
 			break;
@@ -47,10 +47,10 @@ static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
 	 * check the state again.
 	 */
 	if (time_after(jiffies, timeout)) {
-		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 0),
+		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
 				     &tx_fsm_val_0);
 		err |= ufshcd_dme_get(hba,
-		 UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), &tx_fsm_val_1);
+		 UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
 	}
 
 	if (err) {
diff --git a/drivers/ufs/host/ufs-hisi.h b/drivers/ufs/host/ufs-hisi.h
index 6af6a268458d..08dccfbec404 100644
--- a/drivers/ufs/host/ufs-hisi.h
+++ b/drivers/ufs/host/ufs-hisi.h
@@ -39,11 +39,6 @@
 #define MASK_UFS_DEVICE_RESET		(0x1 << 16)
 #define BIT_UFS_DEVICE_RESET		(0x1)
 
-/*
- * M-TX Configuration Attributes for Hixxxx
- */
-#define MPHY_TX_FSM_STATE	0x41
-
 /*
  * Hixxxx UFS HC specific Registers
  */
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 75f569dd5cfe..b2081ed6197a 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -390,7 +390,7 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
 
 	do {
 		err = ufshcd_dme_get(hba,
-				UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE,
+				UIC_ARG_MIB_SEL(TX_FSM_STATE,
 					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
 				&tx_fsm_val);
 		if (err || tx_fsm_val == TX_STATE_HIBERN8)
@@ -406,7 +406,7 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
 	 */
 	if (time_after(jiffies, timeout))
 		err = ufshcd_dme_get(hba,
-				UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE,
+				UIC_ARG_MIB_SEL(TX_FSM_STATE,
 					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
 				&tx_fsm_val);
 
diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
index b7b68333fc32..fc24ed995ff3 100644
--- a/drivers/ufs/host/ufs-qcom.h
+++ b/drivers/ufs/host/ufs-qcom.h
@@ -10,7 +10,6 @@
 #include <soc/qcom/ice.h>
 #include <ufs/ufshcd.h>
 
-#define MPHY_TX_FSM_STATE       0x41
 #define HBRN8_POLL_TOUT_MS      100
 #define DEFAULT_CLK_RATE_HZ     1000000
 #define MAX_SUPP_MAC		64
diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
index a5e40e7604a1..0406f2bb576b 100644
--- a/drivers/ufs/host/ufshcd-dwc.h
+++ b/drivers/ufs/host/ufshcd-dwc.h
@@ -27,10 +27,6 @@
 
 #define CBREFREFCLK_GATE_OVR_EN		BIT(7)
 
-/* M-PHY Attributes */
-#define MTX_FSM_STATE		0x41
-#define MRX_FSM_STATE		0xC1
-
 /* M-PHY registers */
 #define RX_OVRD_IN_1(n)		(0x3006 + ((n) * 0x100))
 #define RX_PCS_OUT(n)		(0x300F + ((n) * 0x100))
diff --git a/include/ufs/unipro.h b/include/ufs/unipro.h
index bf063edc284d..d9fcdf922482 100644
--- a/include/ufs/unipro.h
+++ b/include/ufs/unipro.h
@@ -34,6 +34,7 @@
 #define TX_PWM_G6_G7_SYNC_LENGTH		0x0034
 #define TX_HS_DEEMPHASIS_SETTING		0x0037
 #define TX_HS_PRESHOOT_SETTING			0x003B
+#define TX_FSM_STATE				0x0041
 #define TX_REFCLKFREQ				0x00EB
 #define TX_CFGCLKFREQVAL			0x00EC
 #define	CFGEXTRATTR				0x00F0
@@ -72,6 +73,7 @@
 #define CFGRXCDR8				0x00BA
 #define CFGRXOVR8				0x00BD
 #define CFGRXOVR6				0x00BF
+#define RX_FSM_STATE				0x00C1
 #define RX_FOM					0x00C2
 #define RXDIRECTCTRL2				0x00C7
 #define CFGRXOVR4				0x00E9
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
  2026-08-26 13:40 ` [PATCH 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h Larisa Grigore
  2026-08-26 13:40 ` [PATCH 02/10] ufs: unipro: Add TX/RX FSM state attributes Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 15:17   ` Frank Li
                     ` (3 more replies)
  2026-08-26 13:40 ` [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc Larisa Grigore
                   ` (6 subsequent siblings)
  9 siblings, 4 replies; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Factor out the common logic used to poll TX_FSM_STATE until the TX lanes
enter Hibern8 into ufshcd_check_hibern8().

The HiSilicon and Qualcomm drivers currently implement similar checks
using the UniPro TX_FSM_STATE attribute after link/PHY bring-up. Move
this logic to a common helper in ufshcd-pltfrm so it can be shared
across host controller drivers.

Switch the HiSilicon and Qualcomm drivers to use the new helper and drop
their local implementations.

This also prepares for reusing the same UniPro-specific Hibern8 check in
a subsequent commit.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 drivers/ufs/host/ufs-hisi.c      | 48 +------------------
 drivers/ufs/host/ufs-qcom.c      | 42 +----------------
 drivers/ufs/host/ufshcd-pltfrm.c | 79 ++++++++++++++++++++++++++++++++
 drivers/ufs/host/ufshcd-pltfrm.h |  2 +
 4 files changed, 84 insertions(+), 87 deletions(-)

diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
index bd223bda1ce2..76d77bc30322 100644
--- a/drivers/ufs/host/ufs-hisi.c
+++ b/drivers/ufs/host/ufs-hisi.c
@@ -22,50 +22,6 @@
 #include <ufs/ufshci.h>
 #include <ufs/ufs_quirks.h>
 
-static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
-{
-	int err = 0;
-	u32 tx_fsm_val_0 = 0;
-	u32 tx_fsm_val_1 = 0;
-	unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
-
-	do {
-		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
-				      &tx_fsm_val_0);
-		err |= ufshcd_dme_get(hba,
-		    UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
-		if (err || (tx_fsm_val_0 == TX_STATE_HIBERN8 &&
-			tx_fsm_val_1 == TX_STATE_HIBERN8))
-			break;
-
-		/* sleep for max. 200us */
-		usleep_range(100, 200);
-	} while (time_before(jiffies, timeout));
-
-	/*
-	 * we might have scheduled out for long during polling so
-	 * check the state again.
-	 */
-	if (time_after(jiffies, timeout)) {
-		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
-				     &tx_fsm_val_0);
-		err |= ufshcd_dme_get(hba,
-		 UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
-	}
-
-	if (err) {
-		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
-			__func__, err);
-	} else if (tx_fsm_val_0 != TX_STATE_HIBERN8 ||
-			 tx_fsm_val_1 != TX_STATE_HIBERN8) {
-		err = -1;
-		dev_err(hba->dev, "%s: invalid TX_FSM_STATE, lane0 = %d, lane1 = %d\n",
-			__func__, tx_fsm_val_0, tx_fsm_val_1);
-	}
-
-	return err;
-}
-
 static void ufs_hisi_clk_init(struct ufs_hba *hba)
 {
 	struct ufs_hisi_host *host = ufshcd_get_variant(hba);
@@ -224,9 +180,9 @@ static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
 
 	/* Unipro VS_mphy_disable */
 	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), 0x0);
-	err = ufs_hisi_check_hibern8(hba);
+	err = ufshcd_check_hibern8(hba, 2, HBRN8_POLL_TOUT_MS);
 	if (err)
-		dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n");
+		dev_err(hba->dev, "hibern8 TX FSM check failed\n");
 
 	if (!(host->caps & UFS_HISI_CAP_PHY10nm))
 		ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV);
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index b2081ed6197a..c94488661bcf 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -382,46 +382,6 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host)
 	return 0;
 }
 
-static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
-{
-	int err;
-	u32 tx_fsm_val;
-	unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
-
-	do {
-		err = ufshcd_dme_get(hba,
-				UIC_ARG_MIB_SEL(TX_FSM_STATE,
-					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
-				&tx_fsm_val);
-		if (err || tx_fsm_val == TX_STATE_HIBERN8)
-			break;
-
-		/* sleep for max. 200us */
-		usleep_range(100, 200);
-	} while (time_before(jiffies, timeout));
-
-	/*
-	 * we might have scheduled out for long during polling so
-	 * check the state again.
-	 */
-	if (time_after(jiffies, timeout))
-		err = ufshcd_dme_get(hba,
-				UIC_ARG_MIB_SEL(TX_FSM_STATE,
-					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
-				&tx_fsm_val);
-
-	if (err) {
-		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
-				__func__, err);
-	} else if (tx_fsm_val != TX_STATE_HIBERN8) {
-		err = tx_fsm_val;
-		dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n",
-				__func__, err);
-	}
-
-	return err;
-}
-
 static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host)
 {
 	ufshcd_rmwl(host->hba, QUNIPRO_SEL, QUNIPRO_SEL, REG_UFS_CFG1);
@@ -607,7 +567,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
 		break;
 	case POST_CHANGE:
 		/* check if UFS PHY moved from DISABLED to HIBERN8 */
-		err = ufs_qcom_check_hibern8(hba);
+		err = ufshcd_check_hibern8(hba, 1, HBRN8_POLL_TOUT_MS);
 		ufs_qcom_enable_hw_clk_gating(hba);
 		ufs_qcom_ice_enable(host);
 		ufs_qcom_config_ice_allocator(host);
diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
index c2dafb583cf5..034ee988924a 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.c
+++ b/drivers/ufs/host/ufshcd-pltfrm.c
@@ -9,6 +9,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_opp.h>
@@ -563,6 +564,84 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
 }
 EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
 
+/**
+ * ufshcd_check_hibern8 - Check if all TX lanes entered Hibern8 state
+ * @hba: host controller instance
+ * @num_lanes: number of TX lanes to check
+ * @timeout_ms: timeout in milliseconds for all lanes
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
+			 unsigned int timeout_ms)
+{
+	unsigned long timeout;
+	u32 tx_fsm_val = 0;
+	unsigned int i;
+	bool success;
+	int err;
+
+	if (!num_lanes)
+		return -EINVAL;
+
+	timeout = jiffies + msecs_to_jiffies(timeout_ms);
+
+	do {
+		success = true;
+
+		for (i = 0; i < num_lanes; i++) {
+			err = ufshcd_dme_get(hba,
+					UIC_ARG_MIB_SEL(TX_FSM_STATE,
+						UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
+					&tx_fsm_val);
+			if (err) {
+				dev_err(hba->dev,
+						"%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
+						__func__, i, err);
+				return err;
+			}
+
+			if (tx_fsm_val != TX_STATE_HIBERN8) {
+				success = false;
+				break;
+			}
+		}
+
+		if (success)
+			return 0;
+
+		/* sleep for max. 200us */
+		usleep_range(100, 200);
+	} while (time_before(jiffies, timeout));
+
+	/*
+	 * We might have been scheduled out for long during polling, so do
+	 * one final check before reporting timeout.
+	 */
+	for (i = 0; i < num_lanes; i++) {
+		err = ufshcd_dme_get(hba,
+				UIC_ARG_MIB_SEL(TX_FSM_STATE,
+					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
+				&tx_fsm_val);
+		if (err) {
+			dev_err(hba->dev,
+					"%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
+					__func__, i, err);
+			return err;
+		}
+
+		if (tx_fsm_val != TX_STATE_HIBERN8) {
+			dev_err(hba->dev,
+					"%s: timeout waiting for lane %u to enter HIBERN8, TX_FSM_STATE=%u\n",
+					__func__, i, tx_fsm_val);
+			return -ETIMEDOUT;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(ufshcd_check_hibern8);
+
 MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
 MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
 MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h
index 0a18a8aed94d..ec2a8013e975 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.h
+++ b/drivers/ufs/host/ufshcd-pltfrm.h
@@ -35,5 +35,7 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
 void ufshcd_pltfrm_remove(struct platform_device *pdev);
 int ufshcd_populate_vreg(struct device *dev, const char *name,
 			 struct ufs_vreg **out_vreg, bool skip_current);
+int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
+			 unsigned int timeout_ms);
 
 #endif /* UFSHCD_PLTFRM_H_ */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
                   ` (2 preceding siblings ...)
  2026-08-26 13:40 ` [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 15:20   ` Frank Li
                     ` (2 more replies)
  2026-08-26 13:40 ` [PATCH 05/10] scsi: ufs: dwc: Export common clock divider and link status helpers Larisa Grigore
                   ` (5 subsequent siblings)
  9 siblings, 3 replies; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

The AMD Versal Gen 2 UFS platform driver carries local helpers to access
M-PHY control registers through the UniPro CREG interface.

Move these helpers into the common Synopsys DWC layer as
ufshcd_dwc_phy_reg_read() and ufshcd_dwc_phy_reg_write(). This avoids code
duplication in DWC-based glue drivers and provides a single implementation
for future reuse.
Dropped `static` from `phy_write_attrs` and `phy_read_attrs` since there
is no benefit in keeping them persistent.

Update the AMD Versal2 glue driver to use the new ufshcd-dwc helpers.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 drivers/ufs/host/ufs-amd-versal2.c | 85 ++++++------------------------
 drivers/ufs/host/ufshcd-dwc.c      | 77 +++++++++++++++++++++++++++
 drivers/ufs/host/ufshcd-dwc.h      |  2 +
 3 files changed, 96 insertions(+), 68 deletions(-)

diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c
index 0d4d5e98ad65..c7da95999c85 100644
--- a/drivers/ufs/host/ufs-amd-versal2.c
+++ b/drivers/ufs/host/ufs-amd-versal2.c
@@ -43,57 +43,6 @@ struct ufs_versal2_host {
 	u8 ctlecompval1;
 };
 
-static int ufs_versal2_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
-{
-	static struct ufshcd_dme_attr_val phy_write_attrs[] = {
-		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
-		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
-		{ UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
-		{ UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
-		{ UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
-		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
-	};
-
-	phy_write_attrs[0].mib_val = (u8)addr;
-	phy_write_attrs[1].mib_val = (u8)(addr >> 8);
-	phy_write_attrs[2].mib_val = (u8)val;
-	phy_write_attrs[3].mib_val = (u8)(val >> 8);
-
-	return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs, ARRAY_SIZE(phy_write_attrs));
-}
-
-static int ufs_versal2_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val)
-{
-	u32 mib_val;
-	int ret;
-	static struct ufshcd_dme_attr_val phy_read_attrs[] = {
-		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
-		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
-		{ UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL },
-		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
-	};
-
-	phy_read_attrs[0].mib_val = (u8)addr;
-	phy_read_attrs[1].mib_val = (u8)(addr >> 8);
-
-	ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs, ARRAY_SIZE(phy_read_attrs));
-	if (ret)
-		return ret;
-
-	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val);
-	if (ret)
-		return ret;
-
-	*val = mib_val;
-	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val);
-	if (ret)
-		return ret;
-
-	*val |= (mib_val << 8);
-
-	return 0;
-}
-
 static int ufs_versal2_enable_phy(struct ufs_hba *hba)
 {
 	u32 offset, reg;
@@ -162,64 +111,64 @@ static int ufs_versal2_setup_phy(struct ufs_hba *hba)
 	u32 reg;
 
 	/* Bypass RX-AFE offset calibrations (ATT/CTLE) */
-	ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(0), &reg);
+	ret = ufshcd_dwc_phy_reg_read(hba, FAST_FLAGS(0), &reg);
 	if (ret)
 		return ret;
 
 	reg |= MPHY_FAST_RX_AFE_CAL;
-	ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(0), reg);
+	ret = ufshcd_dwc_phy_reg_write(hba, FAST_FLAGS(0), reg);
 	if (ret)
 		return ret;
 
-	ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(1), &reg);
+	ret = ufshcd_dwc_phy_reg_read(hba, FAST_FLAGS(1), &reg);
 	if (ret)
 		return ret;
 
 	reg |= MPHY_FAST_RX_AFE_CAL;
-	ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(1), reg);
+	ret = ufshcd_dwc_phy_reg_write(hba, FAST_FLAGS(1), reg);
 	if (ret)
 		return ret;
 
 	/* Program ATT and CTLE compensation values */
 	if (host->attcompval0) {
-		ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), host->attcompval0);
+		ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), host->attcompval0);
 		if (ret)
 			return ret;
 	}
 
 	if (host->attcompval1) {
-		ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), host->attcompval1);
+		ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), host->attcompval1);
 		if (ret)
 			return ret;
 	}
 
 	if (host->ctlecompval0) {
-		ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), host->ctlecompval0);
+		ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), host->ctlecompval0);
 		if (ret)
 			return ret;
 	}
 
 	if (host->ctlecompval1) {
-		ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), host->ctlecompval1);
+		ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), host->ctlecompval1);
 		if (ret)
 			return ret;
 	}
 
-	ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(0), &reg);
+	ret = ufshcd_dwc_phy_reg_read(hba, FW_CALIB_CCFG(0), &reg);
 	if (ret)
 		return ret;
 
 	reg |= MPHY_FW_CALIB_CFG_VAL;
-	ret = ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(0), reg);
+	ret = ufshcd_dwc_phy_reg_write(hba, FW_CALIB_CCFG(0), reg);
 	if (ret)
 		return ret;
 
-	ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(1), &reg);
+	ret = ufshcd_dwc_phy_reg_read(hba, FW_CALIB_CCFG(1), &reg);
 	if (ret)
 		return ret;
 
 	reg |= MPHY_FW_CALIB_CFG_VAL;
-	return ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(1), reg);
+	return ufshcd_dwc_phy_reg_write(hba, FW_CALIB_CCFG(1), reg);
 }
 
 static int ufs_versal2_phy_init(struct ufs_hba *hba)
@@ -406,7 +355,7 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_
 
 	for (lane = 0; lane < activelanes; lane++) {
 		time_left = TIMEOUT_MICROSEC;
-		ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
+		ret = ufshcd_dwc_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
 		if (ret)
 			return ret;
 
@@ -416,12 +365,12 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_
 		else
 			reg &= ~MPHY_RX_OVRD_VAL;
 
-		ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
+		ret = ufshcd_dwc_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
 		if (ret)
 			return ret;
 
 		do {
-			ret = ufs_versal2_phy_reg_read(hba, RX_PCS_OUT(lane), &reg);
+			ret = ufshcd_dwc_phy_reg_read(hba, RX_PCS_OUT(lane), &reg);
 			if (ret)
 				return ret;
 
@@ -483,12 +432,12 @@ static int ufs_versal2_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_ch
 
 		/* Remove rx_req override */
 		for (lane = 0; lane < dev_req_params->lane_tx; lane++) {
-			ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
+			ret = ufshcd_dwc_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
 			if (ret)
 				return ret;
 
 			reg &= ~MPHY_RX_OVRD_EN;
-			ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
+			ret = ufshcd_dwc_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
 			if (ret)
 				return ret;
 		}
diff --git a/drivers/ufs/host/ufshcd-dwc.c b/drivers/ufs/host/ufshcd-dwc.c
index 21b1cf912dcc..45920793d1a8 100644
--- a/drivers/ufs/host/ufshcd-dwc.c
+++ b/drivers/ufs/host/ufshcd-dwc.c
@@ -141,6 +141,83 @@ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
 }
 EXPORT_SYMBOL(ufshcd_dwc_link_startup_notify);
 
+/**
+ * ufshcd_dwc_phy_reg_write - Write a DWC M-PHY CREG register
+ * @hba: private structure pointer
+ * @addr: M-PHY CREG register address
+ * @val: value to write
+ *
+ * Write a 16-bit M-PHY CREG register through the Synopsys DesignWare
+ * UniPro indirect register access interface.
+ *
+ * Return: 0 on success, non-zero value on failure.
+ */
+int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
+{
+	struct ufshcd_dme_attr_val phy_write_attrs[] = {
+		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
+		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
+		{ UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
+		{ UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
+		{ UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
+		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
+	};
+
+	phy_write_attrs[0].mib_val = (u8)addr;
+	phy_write_attrs[1].mib_val = (u8)(addr >> 8);
+	phy_write_attrs[2].mib_val = (u8)val;
+	phy_write_attrs[3].mib_val = (u8)(val >> 8);
+
+	return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs,
+					ARRAY_SIZE(phy_write_attrs));
+}
+EXPORT_SYMBOL(ufshcd_dwc_phy_reg_write);
+
+/**
+ * ufshcd_dwc_phy_reg_read - Read a DWC M-PHY CREG register
+ * @hba: private structure pointer
+ * @addr: M-PHY CREG register address
+ * @val: pointer where the read value is stored
+ *
+ * Read a 16-bit M-PHY CREG register through the Synopsys DesignWare
+ * UniPro indirect register access interface.
+ *
+ * Return: 0 on success, non-zero value on failure.
+ */
+int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val)
+{
+	struct ufshcd_dme_attr_val phy_read_attrs[] = {
+		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
+		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
+		{ UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL },
+		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
+	};
+	u32 mib_val;
+	int ret;
+
+	phy_read_attrs[0].mib_val = (u8)addr;
+	phy_read_attrs[1].mib_val = (u8)(addr >> 8);
+
+	ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs,
+				       ARRAY_SIZE(phy_read_attrs));
+	if (ret)
+		return ret;
+
+	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val);
+	if (ret)
+		return ret;
+
+	*val = mib_val;
+	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val);
+	if (ret)
+		return ret;
+
+	*val |= (mib_val << 8);
+
+	return 0;
+}
+EXPORT_SYMBOL(ufshcd_dwc_phy_reg_read);
+
 MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
 MODULE_DESCRIPTION("UFS Host driver for Synopsys Designware Core");
 MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
index 0406f2bb576b..d8673cd54d4c 100644
--- a/drivers/ufs/host/ufshcd-dwc.h
+++ b/drivers/ufs/host/ufshcd-dwc.h
@@ -45,4 +45,6 @@ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
 					enum ufs_notify_change_status status);
 int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba,
 				const struct ufshcd_dme_attr_val *v, int n);
+int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val);
+int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val);
 #endif /* End of Header */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 05/10] scsi: ufs: dwc: Export common clock divider and link status helpers
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
                   ` (3 preceding siblings ...)
  2026-08-26 13:40 ` [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 15:22   ` Frank Li
  2026-08-26 13:40 ` [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver Larisa Grigore
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Make ufshcd_dwc_program_clk_div() and ufshcd_dwc_link_is_up() available to
DWC-based platform glue drivers. These helpers are needed by platform
drivers that must program the controller HCLK divider and verify link state
during their vendor-specific initialization flows.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 drivers/ufs/host/ufshcd-dwc.c | 6 ++++--
 drivers/ufs/host/ufshcd-dwc.h | 2 ++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/ufs/host/ufshcd-dwc.c b/drivers/ufs/host/ufshcd-dwc.c
index 45920793d1a8..4a689d211e81 100644
--- a/drivers/ufs/host/ufshcd-dwc.c
+++ b/drivers/ufs/host/ufshcd-dwc.c
@@ -39,10 +39,11 @@ EXPORT_SYMBOL(ufshcd_dwc_dme_set_attrs);
  * @divider_val: clock divider value to be programmed
  *
  */
-static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val)
+void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val)
 {
 	ufshcd_writel(hba, divider_val, DWC_UFS_REG_HCLKDIV);
 }
+EXPORT_SYMBOL(ufshcd_dwc_program_clk_div);
 
 /**
  * ufshcd_dwc_link_is_up() - check if link is up.
@@ -50,7 +51,7 @@ static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val)
  *
  * Return: 0 on success, non-zero value on failure.
  */
-static int ufshcd_dwc_link_is_up(struct ufs_hba *hba)
+int ufshcd_dwc_link_is_up(struct ufs_hba *hba)
 {
 	int dme_result = 0;
 
@@ -63,6 +64,7 @@ static int ufshcd_dwc_link_is_up(struct ufs_hba *hba)
 
 	return 1;
 }
+EXPORT_SYMBOL(ufshcd_dwc_link_is_up);
 
 /**
  * ufshcd_dwc_connection_setup() - configure unipro attributes.
diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
index d8673cd54d4c..53b523ef59df 100644
--- a/drivers/ufs/host/ufshcd-dwc.h
+++ b/drivers/ufs/host/ufshcd-dwc.h
@@ -47,4 +47,6 @@ int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba,
 				const struct ufshcd_dme_attr_val *v, int n);
 int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val);
 int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val);
+int ufshcd_dwc_link_is_up(struct ufs_hba *hba);
+void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val);
 #endif /* End of Header */
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
                   ` (4 preceding siblings ...)
  2026-08-26 13:40 ` [PATCH 05/10] scsi: ufs: dwc: Export common clock divider and link status helpers Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 14:35   ` Frank Li
  2026-08-26 13:40 ` [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver Larisa Grigore
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Document the device tree binding for the NXP S32N79 DWC-based UFS host
controller which is compliant with JESD-223D "Universal Flash Storage Host
Controller Interface" v3.0.

The controller requires access to the SCM register block and the core
clock rate when M-PHY initialization is performed. Add the optional
nxp,mphy-boot-mode property to select the M-PHY firmware execution
environment.
Currently, the M-PHY firmware can only run from ROM. If the property is
omitted, M-PHY initialization is assumed to be handled externally.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 .../bindings/ufs/nxp,s32n7-ufshc.yaml         | 99 +++++++++++++++++++
 1 file changed, 99 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml

diff --git a/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
new file mode 100644
index 000000000000..d9d5669be313
--- /dev/null
+++ b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
@@ -0,0 +1,99 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/ufs/nxp,s32n7-ufshc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: NXP S32N79 Universal Flash Storage (UFS) Controller
+
+maintainers:
+  - Larisa Grigore <larisa.grigore@oss.nxp.com>
+
+# Select only our matches, not all jedec,ufs
+select:
+  properties:
+    compatible:
+      contains:
+        const: nxp,s32n79-ufshc
+  required:
+    - compatible
+
+properties:
+  compatible:
+    items:
+      - const: nxp,s32n79-ufshc
+      - const: jedec,ufs-3.0
+
+  reg:
+    minItems: 1
+    items:
+      - description: UFS Host Controller registers
+      - description: System Control Module registers
+
+  reg-names:
+    description: |
+      Names for the register regions. The first entry must be "ufshc".
+      The second entry "scm" is mandatory only when nxp,mphy-boot-mode
+      property is present.
+    minItems: 1
+    items:
+      - const: ufshc
+      - const: scm
+
+  clocks:
+    maxItems: 1
+    description: UFS core clock
+
+  clock-names:
+    items:
+      - const: core_clk
+
+  nxp,mphy-boot-mode:
+    description: |
+      MPHY initialization mode. Defines how the MPHY firmware should be
+      initialized and executed during UFS controller startup.
+      If this property is not specified, MPHY initialization will be skipped.
+    $ref: /schemas/types.yaml#/definitions/string
+    enum:
+      - rom
+
+required:
+  - compatible
+  - reg
+
+allOf:
+  - $ref: ufs-common.yaml
+  - if:
+      required:
+        - nxp,mphy-boot-mode
+    then:
+      properties:
+        reg:
+          minItems: 2
+        reg-names:
+          minItems: 2
+      required:
+        - reg-names
+        - clocks
+        - clock-names
+    else:
+      properties:
+        reg:
+          maxItems: 1
+        reg-names:
+          maxItems: 1
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    ufshc@4ac80000 {
+        compatible = "nxp,s32n79-ufshc", "jedec,ufs-3.0";
+        reg = <0x4ac80000 0x1000>, <0x4ac40000 0x1000>;
+        reg-names = "ufshc", "scm";
+        interrupt-parent = <&irqsteer_coss>;
+        interrupts = <211>;
+        clocks = <&sys_clk>;
+        clock-names = "core_clk";
+        nxp,mphy-boot-mode = "rom";
+    };
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
                   ` (5 preceding siblings ...)
  2026-08-26 13:40 ` [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 14:01   ` sashiko-bot
  2026-08-26 15:49   ` Frank Li
  2026-08-26 13:40 ` [PATCH 08/10] arm64: dts: freescale: s32n79: Add UFS host controller Larisa Grigore
                   ` (2 subsequent siblings)
  9 siblings, 2 replies; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Add support for the UFS host controller on the NXP S32N79 SoC, built on
the Synopsys DesignWare (DWC) UFS architecture, using the UFSHCD DWC and
UFSHCD platform glue.

This controller requires S32N79-specific initialization prior to
UIC_LINKSTARTUP, including:
  - programming SCM.ONE_US_TICK and HC.HCLKDIV based on the core clock,
  - applying the vendor-defined M-PHY calibration sequence, and
  - performing post-link calibration steps needed for HS operation.

The M-PHY boot mode is selected via the 'nxp,mphy-boot-mode' device tree
property. If the property is omitted, M-PHY initialization is assumed to
be handled by external firmware or by a simulation environment.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 drivers/ufs/host/Kconfig         |  12 +
 drivers/ufs/host/Makefile        |   1 +
 drivers/ufs/host/ufs-nxp-s32n7.c | 464 +++++++++++++++++++++++++++++++
 drivers/ufs/host/ufshcd-dwc.h    |   9 +-
 4 files changed, 484 insertions(+), 2 deletions(-)
 create mode 100644 drivers/ufs/host/ufs-nxp-s32n7.c

diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig
index ff170c0b6da0..e8ee7dd05f1a 100644
--- a/drivers/ufs/host/Kconfig
+++ b/drivers/ufs/host/Kconfig
@@ -168,3 +168,15 @@ config SCSI_UFS_AMD_VERSAL2
 
 	  Select this if you have UFS controller on AMD Versal Gen 2 SoC.
 	  If unsure, say N.
+
+config SCSI_UFS_S32N7
+	tristate "NXP S32N7 platform driver"
+	depends on SCSI_UFSHCD_PLATFORM && (ARCH_S32 || COMPILE_TEST)
+	help
+	  This selects the S32N7 specific additions on top of the UFSHCD DWC
+	  and UFSHCD platform driver. UFS host on S32N79 needs some vendor
+	  specific configurations like PHY and vendor specific register accesses
+	  before accessing the hardware.
+
+	  Select this if you have UFS controller on an S32N7 based board.
+	  If unsure, say N.
diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile
index 65d8bb23ab7b..0381750a35d3 100644
--- a/drivers/ufs/host/Makefile
+++ b/drivers/ufs/host/Makefile
@@ -2,6 +2,7 @@
 
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
 obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
+obj-$(CONFIG_SCSI_UFS_S32N7) += ufs-nxp-s32n7.o ufshcd-dwc.o
 obj-$(CONFIG_SCSI_UFS_CDNS_PLATFORM) += cdns-pltfrm.o
 obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
 obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
diff --git a/drivers/ufs/host/ufs-nxp-s32n7.c b/drivers/ufs/host/ufs-nxp-s32n7.c
new file mode 100644
index 000000000000..8a125105536b
--- /dev/null
+++ b/drivers/ufs/host/ufs-nxp-s32n7.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
+/*
+ * Copyright 2026 NXP
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include "ufshcd-pltfrm.h"
+#include "ufshcd-dwc.h"
+#include "ufshci-dwc.h"
+
+/* SCM Register Offsets. */
+#define SCM_ONE_US_TICK			0x2CU
+#define SCM_STATUS			0x30
+
+/* SCM_ONE_US_TICK Register. */
+#define SCM_ONE_US_TICK_MASK		GENMASK(8, 0)
+
+/* SCM_MPHY_RAM_CONFIG_STATUS Register Fields. */
+#define SCM_STATUS_PHY_RESET_MASK		BIT(0)
+#define SCM_STATUS_SRAM_BYPASS_MASK		BIT(1)
+#define SCM_STATUS_SRAM_INIT_DONE_MASK		BIT(16)
+
+#define SCM_STATUS_PHY_RESET(val)		\
+	FIELD_PREP(SCM_STATUS_PHY_RESET_MASK, val)
+
+#define SCM_STATUS_SRAM_BYPASS(val)		\
+	FIELD_PREP(SCM_STATUS_SRAM_BYPASS_MASK, val)
+
+/* Timeout values. */
+#define CFG_MPHY_INIT_TIMEOUT_VALUE_US	3000000
+
+/* Clock validation limits. */
+#define MAX_VALID_ONE_US_TICK		SCM_ONE_US_TICK_MASK
+
+/* CPort definitions */
+#define CPORT_0				0
+
+/* Hibern8 state poll timeout. */
+#define HBRN8_POLL_TOUT_MS      1000
+
+/**
+ * enum mphy_boot_mode - MPHY boot mode options
+ * @MPHY_BOOT_NONE: Skip MPHY initialization
+ * @MPHY_BOOT_ROM: Running MPHY from internal FW ROM
+ */
+enum mphy_boot_mode {
+	MPHY_BOOT_NONE = 0,
+	MPHY_BOOT_ROM = 1,
+};
+
+/**
+ * struct s32n_ufs - S32N7 UFS host controller data
+ * @hba: UFS host controller instance
+ * @reg_scm: SCM register base address
+ * @core_clk: core reference clock used to derive ONE_US_TICK/HCLKDIV
+ * @mphy_boot_mode: MPHY boot mode configuration
+ */
+struct s32n_ufs {
+	struct ufs_hba *hba;
+	void __iomem *reg_scm;
+	struct clk *core_clk;
+	enum mphy_boot_mode mphy_boot_mode;
+};
+
+/**
+ * struct phy_reg_cfg - PHY register configuration entry
+ * @reg: Register offset/address
+ * @val: Value to write
+ */
+struct phy_reg_cfg {
+	u32 reg;
+	u32 val;
+};
+
+/**
+ * ufs_s32n_phy_write_sequence - Write a sequence of PHY registers
+ * @hba: UFS host controller instance
+ * @cfg: Array of register configurations
+ * @count: Number of entries in the array
+ *
+ * Return: 0 on success, error code on failure
+ */
+static int ufs_s32n_phy_write_sequence(struct ufs_hba *hba,
+				       const struct phy_reg_cfg *cfg,
+				       size_t count)
+{
+	int ret;
+	size_t i;
+
+	for (i = 0; i < count; i++) {
+		ret = ufshcd_dwc_phy_reg_write(hba, cfg[i].reg, cfg[i].val);
+		if (ret) {
+			dev_err(hba->dev,
+				"Failed to write PHY reg 0x%x = 0x%x (step %zu)\n",
+				cfg[i].reg, cfg[i].val, i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static unsigned long ufs_s32n_calculate_us_tick(unsigned long clk_rate)
+{
+	return clk_rate / USEC_PER_SEC;
+}
+
+static int ufs_s32n_configure_clocks(struct ufs_hba *hba)
+{
+	struct s32n_ufs *ufs = ufshcd_get_variant(hba);
+	struct device *dev = hba->dev;
+	unsigned long clk_rate = 0;
+	unsigned long one_us_tick;
+
+	if (!ufs->core_clk) {
+		dev_err(dev, "Invalid core_clk.\n");
+		return -EINVAL;
+	}
+
+	clk_rate = clk_get_rate(ufs->core_clk);
+	if (!clk_rate) {
+		dev_err(dev, "Failed to get valid clock rate.\n");
+		return -EINVAL;
+	}
+
+	one_us_tick = ufs_s32n_calculate_us_tick(clk_rate);
+	if (one_us_tick == 0 || one_us_tick > MAX_VALID_ONE_US_TICK) {
+		dev_err(dev, "Invalid one_us_tick value: %lu (clk_rate: %lu Hz).\n",
+			one_us_tick, clk_rate);
+		return -EINVAL;
+	}
+
+	dev_dbg(dev, "Core clock rate: %lu Hz, one_us_tick = %lu.\n",
+		 clk_rate, one_us_tick);
+
+	/*
+	 * Configure the micro-second tick rate generator based on core
+	 * clock rate.
+	 */
+	writel(one_us_tick, ufs->reg_scm + SCM_ONE_US_TICK);
+	ufshcd_dwc_program_clk_div(hba, one_us_tick);
+
+	return 0;
+}
+
+static int ufs_s32n_phy_initial_calib(struct ufs_hba *hba)
+{
+	static const struct phy_reg_cfg initial_calib[] = {
+		{ FAST_FLAGS(0), 0x6 },
+		{ FAST_FLAGS(1), 0x6 },
+		{ RX_DAC_CTRL_OVRD(0), 0x1 },
+		{ RX_DAC_CTRL_OVRD(1), 0x1 },
+		{ RX_DAC_CTRL(0), 0x8E },
+		{ RX_DAC_CTRL(1), 0x91 },
+		{ RX_DAC_CTRL_SEL(0), 0x1 },
+		{ RX_DAC_CTRL_SEL(1), 0x1 },
+		{ RX_DAC_CTRL_EN(0), 0x1 },
+		{ RX_DAC_CTRL_EN(1), 0x1 },
+		{ RX_DAC_CTRL_OVRD(0), 0x0 },
+		{ RX_DAC_CTRL_OVRD(1), 0x0 },
+		{ RX_DAC_CTRL_OVRD(0), 0x1 },
+		{ RX_DAC_CTRL_OVRD(1), 0x1 },
+		{ RX_DAC_CTRL(0), 0x71 },
+		{ RX_DAC_CTRL(1), 0x7F },
+		{ RX_DAC_CTRL_SEL(0), 0x2 },
+		{ RX_DAC_CTRL_SEL(1), 0x2 },
+		{ RX_DAC_CTRL_EN(0), 0x1 },
+		{ RX_DAC_CTRL_EN(1), 0x1 },
+		{ RX_DAC_CTRL_OVRD(0), 0x0 },
+		{ RX_DAC_CTRL_OVRD(1), 0x0 },
+		{ FW_CALIB_CCFG(0), 0x100 },
+		{ FW_CALIB_CCFG(1), 0x100 },
+	};
+	int ret;
+
+	/* Clock Control */
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBREFCLKCTRL2, 0), 0x80);
+	if (ret)
+		return ret;
+
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
+	if (ret)
+		return ret;
+
+	/* Initial calibration sequence */
+	return ufs_s32n_phy_write_sequence(hba, initial_calib,
+					   ARRAY_SIZE(initial_calib));
+}
+
+static int ufs_s32n_link_startup_pre_change(struct ufs_hba *hba)
+{
+	struct s32n_ufs *ufs = ufshcd_get_variant(hba);
+	struct device *dev = hba->dev;
+	int ret;
+	u32 reg;
+
+	/*
+	 * There are two supported MPHY boot options implemented:
+	 * - Running MPHY from internal FW ROM:
+	 *  MPHY_RAM_CONFIG_STATUS.SRAM_BYPASS = 1
+	 *  MPHY_RAM_CONFIG_STATUS.SRAM_EXT_LD_DONE = 0
+	 * - Skip M-PHY initialization when it is handled externally.
+	 */
+	if (ufs->mphy_boot_mode == MPHY_BOOT_NONE) {
+		dev_dbg(dev, "Skipping UFS MPHY init.\n");
+		return 0;
+	}
+
+	ret = ufs_s32n_configure_clocks(hba);
+	if (ret)
+		return ret;
+
+	/* Reset SCM.MPHY_RAM_CONFIG_STATUS to default value; keep MPHY in reset. */
+	writel(SCM_STATUS_PHY_RESET(1), ufs->reg_scm + SCM_STATUS);
+	if (ufs->mphy_boot_mode == MPHY_BOOT_ROM) {
+		/* ROM Mode. */
+		writel(SCM_STATUS_PHY_RESET(1) | SCM_STATUS_SRAM_BYPASS(1),
+		       ufs->reg_scm + SCM_STATUS);
+	}
+
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBCRCTRL, 0), 0x1);
+	if (ret)
+		return ret;
+
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
+	if (ret)
+		return ret;
+
+	/* Release PHY_RESET. */
+	writel(readl(ufs->reg_scm + SCM_STATUS) & ~SCM_STATUS_PHY_RESET_MASK,
+	       ufs->reg_scm + SCM_STATUS);
+
+	/* Wait until SRAM_INIT_DONE = 1. */
+	ret = readl_poll_timeout(ufs->reg_scm + SCM_STATUS, reg,
+			reg & SCM_STATUS_SRAM_INIT_DONE_MASK,
+			1000, CFG_MPHY_INIT_TIMEOUT_VALUE_US);
+	if (ret) {
+		dev_err(dev, "UFS MPHY init not done!\n");
+		return ret;
+	}
+
+	/* Start of initial calibration */
+	ret = ufs_s32n_phy_initial_calib(hba);
+	if (ret) {
+		dev_err(dev, "UFS MPHY initial calibration failed!\n");
+		return ret;
+	}
+
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYDISABLE, 0), 0x0);
+	if (ret)
+		return ret;
+
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
+	if (ret)
+		return ret;
+
+	/* Set Local DeviceID. */
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID, 0), 0x0);
+	if (ret)
+		return ret;
+
+	/* End of Gear1 settings */
+
+	return ufshcd_check_hibern8(hba, hba->lanes_per_direction,
+				    HBRN8_POLL_TOUT_MS);
+}
+
+static int ufs_s32n_link_startup_post_change(struct ufs_hba *hba)
+{
+	static const struct ufshcd_dme_attr_val cport_setup[] = {
+		{ UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0),
+				CPORT_IDLE, DME_LOCAL },
+		{ UIC_ARG_MIB_SEL(T_CPORTFLAGS, CPORT_0),
+				CPORT_DEF_FLAGS, DME_LOCAL },
+		{ UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0),
+				CPORT_CONNECTED, DME_LOCAL },
+	};
+	static const struct phy_reg_cfg post_calib[] = {
+		{ RX_OVRD_IN_1(0), 0xc },
+		{ RX_OVRD_IN_1(1), 0xc },
+		{ RX_OVRD_IN_1(0), 0x8 },
+		{ RX_OVRD_IN_1(1), 0x8 },
+		{ RX_OVRD_IN_1(0), 0x0 },
+		{ RX_OVRD_IN_1(1), 0x0 },
+	};
+	struct s32n_ufs *ufs = ufshcd_get_variant(hba);
+	unsigned int data = 0;
+	int ret;
+
+	if (ufs->mphy_boot_mode == MPHY_BOOT_NONE)
+		return 0;
+
+	/* Set Connection State to IDLE (it allows CPort Attributes to be set). */
+	ret = ufshcd_dwc_dme_set_attrs(hba, cport_setup, ARRAY_SIZE(cport_setup));
+	if (ret)
+		return ret;
+
+	ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0), &data);
+	if (ret)
+		return ret;
+
+	if (data != CPORT_CONNECTED)
+		return -EIO;
+
+	/* Post Link Startup Calibration sequence */
+	ret = ufs_s32n_phy_write_sequence(hba, post_calib,
+					   ARRAY_SIZE(post_calib));
+	if (ret)
+		return ret;
+
+	/*
+	 * Performing MPHY configuration for rate change:
+	 * CB rate selection: 0 - rate A, 1 - rate B;
+	 */
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBRATESEL, 0), 0x1);
+	if (ret)
+		return ret;
+
+	ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
+	if (ret)
+		return ret;
+
+	/* End of Gear4 calibration */
+	return 0;
+}
+
+static int ufs_s32n_link_startup_notify(struct ufs_hba *hba,
+			     enum ufs_notify_change_status status)
+{
+	int err;
+
+	if (status == PRE_CHANGE) {
+		err = ufs_s32n_link_startup_pre_change(hba);
+		if (err) {
+			dev_err(hba->dev, "MPHY setup failed (%d).\n", err);
+			return err;
+		}
+		return 0;
+	}
+
+	/* POST_CHANGE */
+	err = ufshcd_dwc_link_is_up(hba);
+	if (err) {
+		dev_err(hba->dev, "Link is not up.\n");
+		return err;
+	}
+
+	err = ufs_s32n_link_startup_post_change(hba);
+	if (err)
+		dev_err(hba->dev, "Connection setup failed (%d).\n", err);
+
+	return err;
+}
+
+static int ufs_s32n_init(struct ufs_hba *hba)
+{
+	struct device *dev = hba->dev;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct device_node *np = dev->of_node;
+	const char *mphy_boot_mode;
+	struct s32n_ufs *ufs;
+	int ret;
+
+	ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
+	if (!ufs)
+		return -ENOMEM;
+
+	ufs->hba = hba;
+	ufs->mphy_boot_mode = MPHY_BOOT_NONE;
+
+	hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
+	hba->quirks |= UFSHCD_QUIRK_PERFORM_LINK_STARTUP_ONCE;
+	hba->spm_lvl = UFS_PM_LVL_5;
+
+	ret = of_property_read_string(np, "nxp,mphy-boot-mode", &mphy_boot_mode);
+	if (ret || !mphy_boot_mode) {
+		dev_dbg(dev,
+			"nxp,mphy-boot-mode property not found. UFS MPHY init will be skipped.\n");
+		goto init_out;
+	}
+
+	if (!strcmp(mphy_boot_mode, "rom")) {
+		ufs->mphy_boot_mode = MPHY_BOOT_ROM;
+	} else {
+		return dev_err_probe(dev, -EINVAL,
+				     "Unrecognized nxp,mphy-boot-mode property. UFS MPHY init will be skipped.\n");
+	}
+
+	ufs->reg_scm = devm_platform_ioremap_resource_byname(pdev, "scm");
+	if (IS_ERR(ufs->reg_scm))
+		return dev_err_probe(dev, PTR_ERR(ufs->reg_scm),
+				     "ioremap failed for SCM registers.\n");
+
+	ufs->core_clk = devm_clk_get(dev, "core_clk");
+	if (IS_ERR(ufs->core_clk))
+		return dev_err_probe(dev, PTR_ERR(ufs->core_clk),
+				     "Failed to get core clock.\n");
+
+init_out:
+	ufshcd_set_variant(hba, ufs);
+
+	return 0;
+}
+
+static const struct ufs_hba_variant_ops ufs_hba_s32n79_vops = {
+	.name = "s32n79",
+	.init = ufs_s32n_init,
+	.link_startup_notify = ufs_s32n_link_startup_notify,
+};
+
+static const struct of_device_id ufs_s32n7_match[] = {
+	{
+		.compatible = "nxp,s32n79-ufshc",
+		.data = &ufs_hba_s32n79_vops,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ufs_s32n7_match);
+
+static int ufs_s32n7_probe(struct platform_device *pdev)
+{
+	const struct ufs_hba_variant_ops *vops;
+
+	vops = device_get_match_data(&pdev->dev);
+	if (!vops)
+		return -ENODEV;
+
+	return ufshcd_pltfrm_init(pdev, vops);
+}
+
+static void ufs_s32n7_remove(struct platform_device *pdev)
+{
+	ufshcd_pltfrm_remove(pdev);
+}
+
+static const struct dev_pm_ops ufs_s32n7_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume)
+	SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL)
+	.prepare	= ufshcd_suspend_prepare,
+	.complete	= ufshcd_resume_complete,
+};
+
+static struct platform_driver ufs_s32n7_driver = {
+	.probe = ufs_s32n7_probe,
+	.remove = ufs_s32n7_remove,
+	.driver	= {
+		.name	= "ufs-s32n7",
+		.pm	= &ufs_s32n7_pm_ops,
+		.of_match_table	= ufs_s32n7_match,
+	},
+};
+
+module_platform_driver(ufs_s32n7_driver);
+
+MODULE_AUTHOR("Larisa Grigore <larisa.grigore@oss.nxp.com>");
+MODULE_DESCRIPTION("NXP S32N7 UFS Host Controller platform driver");
+MODULE_LICENSE("Dual BSD/GPL");
diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
index 53b523ef59df..747383cfb839 100644
--- a/drivers/ufs/host/ufshcd-dwc.h
+++ b/drivers/ufs/host/ufshcd-dwc.h
@@ -13,8 +13,7 @@
 #include <ufs/ufshcd.h>
 
 /* RMMI Attributes */
-#define CBREFCLKCTRL2		0x8132
-#define CBCRCTRL		0x811F
+#define RXSQCONTROL		0x8009
 #define CBC10DIRECTCONF2	0x810E
 #define CBRATESEL		0x8114
 #define CBCREGADDRLSB		0x8116
@@ -24,10 +23,16 @@
 #define CBCREGRDLSB		0x811A
 #define CBCREGRDMSB		0x811B
 #define CBCREGRDWRSEL		0x811C
+#define CBCRCTRL		0x811F
+#define CBREFCLKCTRL2		0x8132
 
 #define CBREFREFCLK_GATE_OVR_EN		BIT(7)
 
 /* M-PHY registers */
+#define RX_DAC_CTRL(n)		(0x10AF + ((n) * 0x100))
+#define RX_DAC_CTRL_OVRD(n)	(0x10B0 + ((n) * 0x100))
+#define RX_DAC_CTRL_SEL(n)	(0x10B1 + ((n) * 0x100))
+#define RX_DAC_CTRL_EN(n)	(0x10B8 + ((n) * 0x100))
 #define RX_OVRD_IN_1(n)		(0x3006 + ((n) * 0x100))
 #define RX_PCS_OUT(n)		(0x300F + ((n) * 0x100))
 #define FAST_FLAGS(n)		(0x401C + ((n) * 0x100))
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 08/10] arm64: dts: freescale: s32n79: Add UFS host controller
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
                   ` (6 preceding siblings ...)
  2026-08-26 13:40 ` [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 13:40 ` [PATCH 09/10] arm64: dts: freescale: s32n79-rdb: Enable UFS Larisa Grigore
  2026-08-26 13:40 ` [PATCH 10/10] MAINTAINERS: Add NXP S32N7 UFS host controller entry Larisa Grigore
  9 siblings, 0 replies; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Add the S32N79 UFS host controller node to the SoC dtsi, including the
UFSHC register region and the SCM region required for M-PHY
initialization.

The controller remains disabled by default and is enabled by board dts
files as needed.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 arch/arm64/boot/dts/freescale/s32n79.dtsi | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/s32n79.dtsi b/arch/arm64/boot/dts/freescale/s32n79.dtsi
index 94ab58783fdc..5b92eaea05fb 100644
--- a/arch/arm64/boot/dts/freescale/s32n79.dtsi
+++ b/arch/arm64/boot/dts/freescale/s32n79.dtsi
@@ -100,6 +100,17 @@ uart7: serial@4aa40000 {
 			status = "disabled";
 		};
 
+		ufshc: ufs@4ac80000 {
+			compatible = "nxp,s32n79-ufshc", "jedec,ufs-3.0";
+			reg = <0x4ac80000 0x1000>, <0x4ac40000 0x1000>;
+			reg-names = "ufshc", "scm";
+			interrupt-parent = <&irqsteer_coss>;
+			interrupts = <211>;
+			clocks = <&clks 0x92>;
+			clock-names = "core_clk";
+			status = "disabled";
+		};
+
 		irqsteer_coss: interrupt-controller@4ed00000 {
 			compatible = "nxp,s32n79-irqsteer";
 			reg = <0x4ed00000 0x10000>;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 09/10] arm64: dts: freescale: s32n79-rdb: Enable UFS
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
                   ` (7 preceding siblings ...)
  2026-08-26 13:40 ` [PATCH 08/10] arm64: dts: freescale: s32n79: Add UFS host controller Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  2026-08-26 13:40 ` [PATCH 10/10] MAINTAINERS: Add NXP S32N7 UFS host controller entry Larisa Grigore
  9 siblings, 0 replies; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Enable the UFS host controller on the S32N79 RDB and select the M-PHY
boot mode. Configure the controller for two lanes per direction.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 arch/arm64/boot/dts/freescale/s32n79-rdb.dts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/s32n79-rdb.dts b/arch/arm64/boot/dts/freescale/s32n79-rdb.dts
index 1feccd61258e..ff0c2b638037 100644
--- a/arch/arm64/boot/dts/freescale/s32n79-rdb.dts
+++ b/arch/arm64/boot/dts/freescale/s32n79-rdb.dts
@@ -63,6 +63,12 @@ &uart7 {
 	status = "okay";
 };
 
+&ufshc {
+	nxp,mphy-boot-mode = "rom";
+	lanes-per-direction = <2>;
+	status = "okay";
+};
+
 &usdhc0 {
 	disable-wp;
 	no-sdio;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* [PATCH 10/10] MAINTAINERS: Add NXP S32N7 UFS host controller entry
  2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
                   ` (8 preceding siblings ...)
  2026-08-26 13:40 ` [PATCH 09/10] arm64: dts: freescale: s32n79-rdb: Enable UFS Larisa Grigore
@ 2026-08-26 13:40 ` Larisa Grigore
  9 siblings, 0 replies; 35+ messages in thread
From: Larisa Grigore @ 2026-08-26 13:40 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

Add maintainer for S32N7 UFS host controller driver.

Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index cfee5d47b214..cd9f1d0c5406 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27768,6 +27768,14 @@ L:	linux-scsi@vger.kernel.org
 S:	Maintained
 F:	drivers/ufs/host/ufs-renesas.c
 
+UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER S32N7 HOOKS
+M:	Larisa Grigore <larisa.grigore@oss.nxp.com>
+R:	NXP S32 Linux Team <s32@nxp.com>
+L:	linux-scsi@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
+F:	drivers/ufs/host/ufs-nxp-s32n7.c
+
 UNIWILL LAPTOP DRIVER
 M:	Armin Wolf <W_Armin@gmx.de>
 L:	platform-driver-x86@vger.kernel.org
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 35+ messages in thread

* Re: [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver
  2026-08-26 13:40 ` [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver Larisa Grigore
@ 2026-08-26 14:01   ` sashiko-bot
  2026-08-26 15:49   ` Frank Li
  1 sibling, 0 replies; 35+ messages in thread
From: sashiko-bot @ 2026-08-26 14:01 UTC (permalink / raw)
  To: Larisa Grigore; +Cc: robh, conor+dt, imx, linux-scsi, devicetree, Frank.Li

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The error handling for an unrecognized `nxp,mphy-boot-mode` property aborts the probe instead of skipping MPHY initialization, contradicting its own error message.
--

commit cc6f571e7075af648ae4e8cc7125fa859f589eaf
Author: Larisa Grigore <larisa.grigore@oss.nxp.com>

scsi: ufs: Add NXP S32N79 UFS host controller driver

This commit adds support for the UFS host controller on the NXP S32N79 SoC
using the DWC architecture. It handles specific initialization steps prior
to UIC_LINKSTARTUP and introduces the 'nxp,mphy-boot-mode' device tree
property.

> diff --git a/drivers/ufs/host/ufs-nxp-s32n7.c b/drivers/ufs/host/ufs-nxp-s32n7.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/ufs/host/ufs-nxp-s32n7.c

[ ... ]

> +static int ufs_s32n_init(struct ufs_hba *hba)
> +{

[ ... ]

> +	ret = of_property_read_string(np, "nxp,mphy-boot-mode", &mphy_boot_mode);
> +	if (ret || !mphy_boot_mode) {
> +		dev_dbg(dev,
> +			"nxp,mphy-boot-mode property not found. UFS MPHY init will be skipped.\n");
> +		goto init_out;
> +	}
> +
> +	if (!strcmp(mphy_boot_mode, "rom")) {
> +		ufs->mphy_boot_mode = MPHY_BOOT_ROM;
> +	} else {
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Unrecognized nxp,mphy-boot-mode property. UFS MPHY init will be skipped.\n");
> +	}

[Severity: Medium]
Does this code inadvertently abort the entire UFS host controller probe?

The error message explicitly states "UFS MPHY init will be skipped", but
returning the result of dev_err_probe() propagates the -EINVAL error up the
call stack and completely fails the probe.

If the intent is to truly skip the MPHY initialization as indicated by the log
message, should this perhaps log a warning and use `goto init_out` instead of
returning a fatal error code?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826134048.854748-1-larisa.grigore@oss.nxp.com?part=7

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver
  2026-08-26 13:40 ` [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver Larisa Grigore
@ 2026-08-26 14:35   ` Frank Li
  2026-08-26 15:57     ` Conor Dooley
  2026-08-27 14:36     ` Larisa Ileana Grigore
  0 siblings, 2 replies; 35+ messages in thread
From: Frank Li @ 2026-08-26 14:35 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:44PM +0200, Larisa Grigore wrote:
> [You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> Document the device tree binding for the NXP S32N79 DWC-based UFS host
> controller which is compliant with JESD-223D "Universal Flash Storage Host
> Controller Interface" v3.0.
>
> The controller requires access to the SCM register block and the core
> clock rate when M-PHY initialization is performed. Add the optional
> nxp,mphy-boot-mode property to select the M-PHY firmware execution
> environment.
> Currently, the M-PHY firmware can only run from ROM. If the property is
> omitted, M-PHY initialization is assumed to be handled externally.
>
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---
>  .../bindings/ufs/nxp,s32n7-ufshc.yaml         | 99 +++++++++++++++++++
>  1 file changed, 99 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>
> diff --git a/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
> new file mode 100644
> index 000000000000..d9d5669be313
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
> @@ -0,0 +1,99 @@
> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/ufs/nxp,s32n7-ufshc.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: NXP S32N79 Universal Flash Storage (UFS) Controller
> +
> +maintainers:
> +  - Larisa Grigore <larisa.grigore@oss.nxp.com>
> +
> +# Select only our matches, not all jedec,ufs
> +select:
> +  properties:
> +    compatible:
> +      contains:
> +        const: nxp,s32n79-ufshc
> +  required:
> +    - compatible
> +
> +properties:
> +  compatible:
> +    items:
> +      - const: nxp,s32n79-ufshc
> +      - const: jedec,ufs-3.0

Any place actually use this fallback?

> +
> +  reg:
> +    minItems: 1
> +    items:
> +      - description: UFS Host Controller registers
> +      - description: System Control Module registers
> +
> +  reg-names:
> +    description: |
> +      Names for the register regions. The first entry must be "ufshc".
> +      The second entry "scm" is mandatory only when nxp,mphy-boot-mode
> +      property is present.

you already us if - else branch to restrict this limition, needn't
description here.

> +    minItems: 1
> +    items:
> +      - const: ufshc
> +      - const: scm
> +
> +  clocks:
> +    maxItems: 1
> +    description: UFS core clock
> +
> +  clock-names:
> +    items:
> +      - const: core_clk

Needn't suffix _clk, just "core", generally needn't clock-names if only
one clocks.

> +
> +  nxp,mphy-boot-mode:
> +    description: |
> +      MPHY initialization mode. Defines how the MPHY firmware should be
> +      initialized and executed during UFS controller startup.
> +      If this property is not specified, MPHY initialization will be skipped.
> +    $ref: /schemas/types.yaml#/definitions/string
> +    enum:
> +      - rom

why not use standard "phy" property, if no "phy" means ROM init it.

Frank
> +
> +required:
> +  - compatible
> +  - reg
> +
> +allOf:
> +  - $ref: ufs-common.yaml
> +  - if:
> +      required:
> +        - nxp,mphy-boot-mode
> +    then:
> +      properties:
> +        reg:
> +          minItems: 2
> +        reg-names:
> +          minItems: 2
> +      required:
> +        - reg-names
> +        - clocks
> +        - clock-names
> +    else:
> +      properties:
> +        reg:
> +          maxItems: 1
> +        reg-names:
> +          maxItems: 1
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    ufshc@4ac80000 {
> +        compatible = "nxp,s32n79-ufshc", "jedec,ufs-3.0";
> +        reg = <0x4ac80000 0x1000>, <0x4ac40000 0x1000>;
> +        reg-names = "ufshc", "scm";
> +        interrupt-parent = <&irqsteer_coss>;
> +        interrupts = <211>;
> +        clocks = <&sys_clk>;
> +        clock-names = "core_clk";
> +        nxp,mphy-boot-mode = "rom";
> +    };
> --
> 2.43.0
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h
  2026-08-26 13:40 ` [PATCH 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h Larisa Grigore
@ 2026-08-26 15:05   ` Frank Li
  2026-08-27 13:59     ` Larisa Ileana Grigore
  0 siblings, 1 reply; 35+ messages in thread
From: Frank Li @ 2026-08-26 15:05 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:39PM +0200, Larisa Grigore wrote:
> [You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> Move the Tx/Rx FSM state definitions from ufshcd-dwc.h to
> include/ufs/unipro.h.
>
> These state values are UniPro/M-PHY related and are not specific to
> the DesignWare UFS host controller implementation, so unipro.h is a
> more appropriate location for sharing them.
>
> As part of this move, drop the driver-private TX_FSM_HIBERN8 define in
> ufs-hisi and ufs-qcom and repoint their Hibern8 checks at the shared
> TX_STATE_HIBERN8 enum constant. This is a no-op change: TX_FSM_HIBERN8
> was 0x1 and TX_STATE_HIBERN8 is 1, so the compared values are identical.
>
> This also prepares for using these definitions in a subsequent commit.
>
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---
...
> -enum rx_fsm_state {
> -       RX_STATE_DISABLED = 0,
> -       RX_STATE_HIBERN8 = 1,
> -       RX_STATE_SLEEP = 2,
> -       RX_STATE_STALL = 3,
> -       RX_STATE_LSBURST = 4,
> -       RX_STATE_HSBURST = 5,
> -};
> -
> -enum tx_fsm_state {
> -       TX_STATE_DISABLED = 0,
> -       TX_STATE_HIBERN8 = 1,
> -       TX_STATE_SLEEP = 2,
> -       TX_STATE_STALL = 3,
> -       TX_STATE_LSBURST = 4,
> -       TX_STATE_HSBURST = 5,
> -};
> -
>  struct ufshcd_dme_attr_val {
>         u32 attr_sel;
>         u32 mib_val;
> diff --git a/include/ufs/unipro.h b/include/ufs/unipro.h
> index 9c168703b104..bf063edc284d 100644
> --- a/include/ufs/unipro.h
> +++ b/include/ufs/unipro.h
> @@ -466,4 +466,23 @@ enum {
>         CPORT_CONNECTED,
>  };
>
> +/* Tx/Rx FSM state */
> +enum ufs_rx_fsm_state {
> +       RX_STATE_DISABLED = 0,
> +       RX_STATE_HIBERN8 = 1,
> +       RX_STATE_SLEEP = 2,
> +       RX_STATE_STALL = 3,
> +       RX_STATE_LSBURST = 4,
> +       RX_STATE_HSBURST = 5,
> +};
> +
> +enum ufs_tx_fsm_state {
> +       TX_STATE_DISABLED = 0,
> +       TX_STATE_HIBERN8 = 1,
> +       TX_STATE_SLEEP = 2,
> +       TX_STATE_STALL = 3,
> +       TX_STATE_LSBURST = 4,
> +       TX_STATE_HSBURST = 5,
> +};

Suppose ref RX(TX)_FSM_State in mipi m-phy speci v6.0, table 50 M-RX status
attributes. you add ref to mipi M-phy to let reader know it is really
defined by spec, not dwc implementation.

Both value are the same, why need define two copy.

Frank

> +
>  #endif /* _UNIPRO_H_ */
> --
> 2.43.0
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 02/10] ufs: unipro: Add TX/RX FSM state attributes
  2026-08-26 13:40 ` [PATCH 02/10] ufs: unipro: Add TX/RX FSM state attributes Larisa Grigore
@ 2026-08-26 15:09   ` Frank Li
  0 siblings, 0 replies; 35+ messages in thread
From: Frank Li @ 2026-08-26 15:09 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:40PM +0200, Larisa Grigore wrote:
>
> Add TX_FSM_STATE and RX_FSM_STATE to include/ufs/unipro.h, as these are
> UniPro M-PHY attribute definitions rather than host controller specific
> ones.
>
> Remove the duplicated local definitions from the HiSilicon, Qualcomm, AMD
> Versal and DesignWare UFS headers.
>
> This also prepares for using these definitions in a subsequent commit.
>
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---

Nice cleanup,

Reviewed-by: Frank Li <Frank.Li@nxp.com>


>  drivers/ufs/host/ufs-amd-versal2.c | 4 ++--
>  drivers/ufs/host/ufs-hisi.c        | 8 ++++----
>  drivers/ufs/host/ufs-hisi.h        | 5 -----
>  drivers/ufs/host/ufs-qcom.c        | 4 ++--
>  drivers/ufs/host/ufs-qcom.h        | 1 -
>  drivers/ufs/host/ufshcd-dwc.h      | 4 ----
>  include/ufs/unipro.h               | 2 ++
>  7 files changed, 10 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c
> index 2154d6286817..0d4d5e98ad65 100644
> --- a/drivers/ufs/host/ufs-amd-versal2.c
> +++ b/drivers/ufs/host/ufs-amd-versal2.c
> @@ -112,7 +112,7 @@ static int ufs_versal2_enable_phy(struct ufs_hba *hba)
>                 u32 time_left, mibsel;
>
>                 time_left = TIMEOUT_MICROSEC;
> -               mibsel = UIC_ARG_MIB_SEL(MTX_FSM_STATE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(offset));
> +               mibsel = UIC_ARG_MIB_SEL(TX_FSM_STATE, UIC_ARG_MPHY_TX_GEN_SEL_INDEX(offset));
>                 do {
>                         ret = ufshcd_dme_get(hba, mibsel, &reg);
>                         if (ret)
> @@ -132,7 +132,7 @@ static int ufs_versal2_enable_phy(struct ufs_hba *hba)
>                 }
>
>                 time_left = TIMEOUT_MICROSEC;
> -               mibsel = UIC_ARG_MIB_SEL(MRX_FSM_STATE, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(offset));
> +               mibsel = UIC_ARG_MIB_SEL(RX_FSM_STATE, UIC_ARG_MPHY_RX_GEN_SEL_INDEX(offset));
>                 do {
>                         ret = ufshcd_dme_get(hba, mibsel, &reg);
>                         if (ret)
> diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
> index 2d214ce9620e..bd223bda1ce2 100644
> --- a/drivers/ufs/host/ufs-hisi.c
> +++ b/drivers/ufs/host/ufs-hisi.c
> @@ -30,10 +30,10 @@ static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
>         unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
>
>         do {
> -               err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 0),
> +               err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
>                                       &tx_fsm_val_0);
>                 err |= ufshcd_dme_get(hba,
> -                   UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), &tx_fsm_val_1);
> +                   UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
>                 if (err || (tx_fsm_val_0 == TX_STATE_HIBERN8 &&
>                         tx_fsm_val_1 == TX_STATE_HIBERN8))
>                         break;
> @@ -47,10 +47,10 @@ static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
>          * check the state again.
>          */
>         if (time_after(jiffies, timeout)) {
> -               err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 0),
> +               err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
>                                      &tx_fsm_val_0);
>                 err |= ufshcd_dme_get(hba,
> -                UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE, 1), &tx_fsm_val_1);
> +                UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
>         }
>
>         if (err) {
> diff --git a/drivers/ufs/host/ufs-hisi.h b/drivers/ufs/host/ufs-hisi.h
> index 6af6a268458d..08dccfbec404 100644
> --- a/drivers/ufs/host/ufs-hisi.h
> +++ b/drivers/ufs/host/ufs-hisi.h
> @@ -39,11 +39,6 @@
>  #define MASK_UFS_DEVICE_RESET          (0x1 << 16)
>  #define BIT_UFS_DEVICE_RESET           (0x1)
>
> -/*
> - * M-TX Configuration Attributes for Hixxxx
> - */
> -#define MPHY_TX_FSM_STATE      0x41
> -
>  /*
>   * Hixxxx UFS HC specific Registers
>   */
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index 75f569dd5cfe..b2081ed6197a 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -390,7 +390,7 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
>
>         do {
>                 err = ufshcd_dme_get(hba,
> -                               UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE,
> +                               UIC_ARG_MIB_SEL(TX_FSM_STATE,
>                                         UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
>                                 &tx_fsm_val);
>                 if (err || tx_fsm_val == TX_STATE_HIBERN8)
> @@ -406,7 +406,7 @@ static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
>          */
>         if (time_after(jiffies, timeout))
>                 err = ufshcd_dme_get(hba,
> -                               UIC_ARG_MIB_SEL(MPHY_TX_FSM_STATE,
> +                               UIC_ARG_MIB_SEL(TX_FSM_STATE,
>                                         UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
>                                 &tx_fsm_val);
>
> diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
> index b7b68333fc32..fc24ed995ff3 100644
> --- a/drivers/ufs/host/ufs-qcom.h
> +++ b/drivers/ufs/host/ufs-qcom.h
> @@ -10,7 +10,6 @@
>  #include <soc/qcom/ice.h>
>  #include <ufs/ufshcd.h>
>
> -#define MPHY_TX_FSM_STATE       0x41
>  #define HBRN8_POLL_TOUT_MS      100
>  #define DEFAULT_CLK_RATE_HZ     1000000
>  #define MAX_SUPP_MAC           64
> diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
> index a5e40e7604a1..0406f2bb576b 100644
> --- a/drivers/ufs/host/ufshcd-dwc.h
> +++ b/drivers/ufs/host/ufshcd-dwc.h
> @@ -27,10 +27,6 @@
>
>  #define CBREFREFCLK_GATE_OVR_EN                BIT(7)
>
> -/* M-PHY Attributes */
> -#define MTX_FSM_STATE          0x41
> -#define MRX_FSM_STATE          0xC1
> -
>  /* M-PHY registers */
>  #define RX_OVRD_IN_1(n)                (0x3006 + ((n) * 0x100))
>  #define RX_PCS_OUT(n)          (0x300F + ((n) * 0x100))
> diff --git a/include/ufs/unipro.h b/include/ufs/unipro.h
> index bf063edc284d..d9fcdf922482 100644
> --- a/include/ufs/unipro.h
> +++ b/include/ufs/unipro.h
> @@ -34,6 +34,7 @@
>  #define TX_PWM_G6_G7_SYNC_LENGTH               0x0034
>  #define TX_HS_DEEMPHASIS_SETTING               0x0037
>  #define TX_HS_PRESHOOT_SETTING                 0x003B
> +#define TX_FSM_STATE                           0x0041
>  #define TX_REFCLKFREQ                          0x00EB
>  #define TX_CFGCLKFREQVAL                       0x00EC
>  #define        CFGEXTRATTR                             0x00F0
> @@ -72,6 +73,7 @@
>  #define CFGRXCDR8                              0x00BA
>  #define CFGRXOVR8                              0x00BD
>  #define CFGRXOVR6                              0x00BF
> +#define RX_FSM_STATE                           0x00C1
>  #define RX_FOM                                 0x00C2
>  #define RXDIRECTCTRL2                          0x00C7
>  #define CFGRXOVR4                              0x00E9
> --
> 2.43.0
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
  2026-08-26 13:40 ` [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper Larisa Grigore
@ 2026-08-26 15:17   ` Frank Li
  2026-08-26 15:33   ` Bart Van Assche
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 35+ messages in thread
From: Frank Li @ 2026-08-26 15:17 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:41PM +0200, Larisa Grigore wrote:
>
> Factor out the common logic used to poll TX_FSM_STATE until the TX lanes
> enter Hibern8 into ufshcd_check_hibern8().
>
> The HiSilicon and Qualcomm drivers currently implement similar checks
> using the UniPro TX_FSM_STATE attribute after link/PHY bring-up. Move
> this logic to a common helper in ufshcd-pltfrm so it can be shared
> across host controller drivers.
>
> Switch the HiSilicon and Qualcomm drivers to use the new helper and drop
> their local implementations.
>
> This also prepares for reusing the same UniPro-specific Hibern8 check in
> a subsequent commit.
>
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---


Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/ufs/host/ufs-hisi.c      | 48 +------------------
>  drivers/ufs/host/ufs-qcom.c      | 42 +----------------
>  drivers/ufs/host/ufshcd-pltfrm.c | 79 ++++++++++++++++++++++++++++++++
>  drivers/ufs/host/ufshcd-pltfrm.h |  2 +
>  4 files changed, 84 insertions(+), 87 deletions(-)
>
> diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
> index bd223bda1ce2..76d77bc30322 100644
> --- a/drivers/ufs/host/ufs-hisi.c
> +++ b/drivers/ufs/host/ufs-hisi.c
> @@ -22,50 +22,6 @@
>  #include <ufs/ufshci.h>
>  #include <ufs/ufs_quirks.h>
>
> -static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
> -{
> -       int err = 0;
> -       u32 tx_fsm_val_0 = 0;
> -       u32 tx_fsm_val_1 = 0;
> -       unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
> -
> -       do {
> -               err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
> -                                     &tx_fsm_val_0);
> -               err |= ufshcd_dme_get(hba,
> -                   UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
> -               if (err || (tx_fsm_val_0 == TX_STATE_HIBERN8 &&
> -                       tx_fsm_val_1 == TX_STATE_HIBERN8))
> -                       break;
> -
> -               /* sleep for max. 200us */
> -               usleep_range(100, 200);
> -       } while (time_before(jiffies, timeout));
> -
> -       /*
> -        * we might have scheduled out for long during polling so
> -        * check the state again.
> -        */
> -       if (time_after(jiffies, timeout)) {
> -               err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
> -                                    &tx_fsm_val_0);
> -               err |= ufshcd_dme_get(hba,
> -                UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
> -       }
> -
> -       if (err) {
> -               dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
> -                       __func__, err);
> -       } else if (tx_fsm_val_0 != TX_STATE_HIBERN8 ||
> -                        tx_fsm_val_1 != TX_STATE_HIBERN8) {
> -               err = -1;
> -               dev_err(hba->dev, "%s: invalid TX_FSM_STATE, lane0 = %d, lane1 = %d\n",
> -                       __func__, tx_fsm_val_0, tx_fsm_val_1);
> -       }
> -
> -       return err;
> -}
> -
>  static void ufs_hisi_clk_init(struct ufs_hba *hba)
>  {
>         struct ufs_hisi_host *host = ufshcd_get_variant(hba);
> @@ -224,9 +180,9 @@ static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
>
>         /* Unipro VS_mphy_disable */
>         ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), 0x0);
> -       err = ufs_hisi_check_hibern8(hba);
> +       err = ufshcd_check_hibern8(hba, 2, HBRN8_POLL_TOUT_MS);
>         if (err)
> -               dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n");
> +               dev_err(hba->dev, "hibern8 TX FSM check failed\n");
>
>         if (!(host->caps & UFS_HISI_CAP_PHY10nm))
>                 ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV);
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index b2081ed6197a..c94488661bcf 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -382,46 +382,6 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host)
>         return 0;
>  }
>
> -static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
> -{
> -       int err;
> -       u32 tx_fsm_val;
> -       unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
> -
> -       do {
> -               err = ufshcd_dme_get(hba,
> -                               UIC_ARG_MIB_SEL(TX_FSM_STATE,
> -                                       UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
> -                               &tx_fsm_val);
> -               if (err || tx_fsm_val == TX_STATE_HIBERN8)
> -                       break;
> -
> -               /* sleep for max. 200us */
> -               usleep_range(100, 200);
> -       } while (time_before(jiffies, timeout));
> -
> -       /*
> -        * we might have scheduled out for long during polling so
> -        * check the state again.
> -        */
> -       if (time_after(jiffies, timeout))
> -               err = ufshcd_dme_get(hba,
> -                               UIC_ARG_MIB_SEL(TX_FSM_STATE,
> -                                       UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
> -                               &tx_fsm_val);
> -
> -       if (err) {
> -               dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
> -                               __func__, err);
> -       } else if (tx_fsm_val != TX_STATE_HIBERN8) {
> -               err = tx_fsm_val;
> -               dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n",
> -                               __func__, err);
> -       }
> -
> -       return err;
> -}
> -
>  static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host)
>  {
>         ufshcd_rmwl(host->hba, QUNIPRO_SEL, QUNIPRO_SEL, REG_UFS_CFG1);
> @@ -607,7 +567,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
>                 break;
>         case POST_CHANGE:
>                 /* check if UFS PHY moved from DISABLED to HIBERN8 */
> -               err = ufs_qcom_check_hibern8(hba);
> +               err = ufshcd_check_hibern8(hba, 1, HBRN8_POLL_TOUT_MS);
>                 ufs_qcom_enable_hw_clk_gating(hba);
>                 ufs_qcom_ice_enable(host);
>                 ufs_qcom_config_ice_allocator(host);
> diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
> index c2dafb583cf5..034ee988924a 100644
> --- a/drivers/ufs/host/ufshcd-pltfrm.c
> +++ b/drivers/ufs/host/ufshcd-pltfrm.c
> @@ -9,6 +9,7 @@
>   */
>
>  #include <linux/clk.h>
> +#include <linux/delay.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_opp.h>
> @@ -563,6 +564,84 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
>  }
>  EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
>
> +/**
> + * ufshcd_check_hibern8 - Check if all TX lanes entered Hibern8 state
> + * @hba: host controller instance
> + * @num_lanes: number of TX lanes to check
> + * @timeout_ms: timeout in milliseconds for all lanes
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
> +                        unsigned int timeout_ms)
> +{
> +       unsigned long timeout;
> +       u32 tx_fsm_val = 0;
> +       unsigned int i;
> +       bool success;
> +       int err;
> +
> +       if (!num_lanes)
> +               return -EINVAL;
> +
> +       timeout = jiffies + msecs_to_jiffies(timeout_ms);
> +
> +       do {
> +               success = true;
> +
> +               for (i = 0; i < num_lanes; i++) {
> +                       err = ufshcd_dme_get(hba,
> +                                       UIC_ARG_MIB_SEL(TX_FSM_STATE,
> +                                               UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
> +                                       &tx_fsm_val);
> +                       if (err) {
> +                               dev_err(hba->dev,
> +                                               "%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
> +                                               __func__, i, err);
> +                               return err;
> +                       }
> +
> +                       if (tx_fsm_val != TX_STATE_HIBERN8) {
> +                               success = false;
> +                               break;
> +                       }
> +               }
> +
> +               if (success)
> +                       return 0;
> +
> +               /* sleep for max. 200us */
> +               usleep_range(100, 200);
> +       } while (time_before(jiffies, timeout));
> +
> +       /*
> +        * We might have been scheduled out for long during polling, so do
> +        * one final check before reporting timeout.
> +        */
> +       for (i = 0; i < num_lanes; i++) {
> +               err = ufshcd_dme_get(hba,
> +                               UIC_ARG_MIB_SEL(TX_FSM_STATE,
> +                                       UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
> +                               &tx_fsm_val);
> +               if (err) {
> +                       dev_err(hba->dev,
> +                                       "%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
> +                                       __func__, i, err);
> +                       return err;
> +               }
> +
> +               if (tx_fsm_val != TX_STATE_HIBERN8) {
> +                       dev_err(hba->dev,
> +                                       "%s: timeout waiting for lane %u to enter HIBERN8, TX_FSM_STATE=%u\n",
> +                                       __func__, i, tx_fsm_val);
> +                       return -ETIMEDOUT;
> +               }
> +       }
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL_GPL(ufshcd_check_hibern8);
> +
>  MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
>  MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
>  MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
> diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h
> index 0a18a8aed94d..ec2a8013e975 100644
> --- a/drivers/ufs/host/ufshcd-pltfrm.h
> +++ b/drivers/ufs/host/ufshcd-pltfrm.h
> @@ -35,5 +35,7 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
>  void ufshcd_pltfrm_remove(struct platform_device *pdev);
>  int ufshcd_populate_vreg(struct device *dev, const char *name,
>                          struct ufs_vreg **out_vreg, bool skip_current);
> +int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
> +                        unsigned int timeout_ms);
>
>  #endif /* UFSHCD_PLTFRM_H_ */
> --
> 2.43.0
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc
  2026-08-26 13:40 ` [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc Larisa Grigore
@ 2026-08-26 15:20   ` Frank Li
  2026-08-27 14:11     ` Larisa Ileana Grigore
  2026-08-26 15:36   ` Bart Van Assche
  2026-08-27 10:47   ` Bough Chen
  2 siblings, 1 reply; 35+ messages in thread
From: Frank Li @ 2026-08-26 15:20 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:42PM +0200, Larisa Grigore wrote:
>
> The AMD Versal Gen 2 UFS platform driver carries local helpers to access
> M-PHY control registers through the UniPro CREG interface.
>
> Move these helpers into the common Synopsys DWC layer as
> ufshcd_dwc_phy_reg_read() and ufshcd_dwc_phy_reg_write(). This avoids code
> duplication in DWC-based glue drivers and provides a single implementation
> for future reuse.

Nit: add empty line between paragraph

> Dropped `static` from `phy_write_attrs` and `phy_read_attrs` since there
> is no benefit in keeping them persistent.
>
> Update the AMD Versal2 glue driver to use the new ufshcd-dwc helpers.
>
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/ufs/host/ufs-amd-versal2.c | 85 ++++++------------------------
>  drivers/ufs/host/ufshcd-dwc.c      | 77 +++++++++++++++++++++++++++
>  drivers/ufs/host/ufshcd-dwc.h      |  2 +
>  3 files changed, 96 insertions(+), 68 deletions(-)
>
> diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c
> index 0d4d5e98ad65..c7da95999c85 100644
> --- a/drivers/ufs/host/ufs-amd-versal2.c
> +++ b/drivers/ufs/host/ufs-amd-versal2.c
> @@ -43,57 +43,6 @@ struct ufs_versal2_host {
>         u8 ctlecompval1;
>  };
>
> -static int ufs_versal2_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
> -{
> -       static struct ufshcd_dme_attr_val phy_write_attrs[] = {
> -               { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> -               { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> -               { UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
> -               { UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
> -               { UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
> -               { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> -       };
> -
> -       phy_write_attrs[0].mib_val = (u8)addr;
> -       phy_write_attrs[1].mib_val = (u8)(addr >> 8);
> -       phy_write_attrs[2].mib_val = (u8)val;
> -       phy_write_attrs[3].mib_val = (u8)(val >> 8);
> -
> -       return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs, ARRAY_SIZE(phy_write_attrs));
> -}
> -
> -static int ufs_versal2_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val)
> -{
> -       u32 mib_val;
> -       int ret;
> -       static struct ufshcd_dme_attr_val phy_read_attrs[] = {
> -               { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> -               { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> -               { UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL },
> -               { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> -       };
> -
> -       phy_read_attrs[0].mib_val = (u8)addr;
> -       phy_read_attrs[1].mib_val = (u8)(addr >> 8);
> -
> -       ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs, ARRAY_SIZE(phy_read_attrs));
> -       if (ret)
> -               return ret;
> -
> -       ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val);
> -       if (ret)
> -               return ret;
> -
> -       *val = mib_val;
> -       ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val);
> -       if (ret)
> -               return ret;
> -
> -       *val |= (mib_val << 8);
> -
> -       return 0;
> -}
> -
>  static int ufs_versal2_enable_phy(struct ufs_hba *hba)
>  {
>         u32 offset, reg;
> @@ -162,64 +111,64 @@ static int ufs_versal2_setup_phy(struct ufs_hba *hba)
>         u32 reg;
>
>         /* Bypass RX-AFE offset calibrations (ATT/CTLE) */
> -       ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(0), &reg);
> +       ret = ufshcd_dwc_phy_reg_read(hba, FAST_FLAGS(0), &reg);
>         if (ret)
>                 return ret;
>
>         reg |= MPHY_FAST_RX_AFE_CAL;
> -       ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(0), reg);
> +       ret = ufshcd_dwc_phy_reg_write(hba, FAST_FLAGS(0), reg);
>         if (ret)
>                 return ret;
>
> -       ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(1), &reg);
> +       ret = ufshcd_dwc_phy_reg_read(hba, FAST_FLAGS(1), &reg);
>         if (ret)
>                 return ret;
>
>         reg |= MPHY_FAST_RX_AFE_CAL;
> -       ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(1), reg);
> +       ret = ufshcd_dwc_phy_reg_write(hba, FAST_FLAGS(1), reg);
>         if (ret)
>                 return ret;
>
>         /* Program ATT and CTLE compensation values */
>         if (host->attcompval0) {
> -               ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), host->attcompval0);
> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), host->attcompval0);
>                 if (ret)
>                         return ret;
>         }
>
>         if (host->attcompval1) {
> -               ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), host->attcompval1);
> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), host->attcompval1);
>                 if (ret)
>                         return ret;
>         }
>
>         if (host->ctlecompval0) {
> -               ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), host->ctlecompval0);
> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), host->ctlecompval0);
>                 if (ret)
>                         return ret;
>         }
>
>         if (host->ctlecompval1) {
> -               ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), host->ctlecompval1);
> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), host->ctlecompval1);
>                 if (ret)
>                         return ret;
>         }
>
> -       ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(0), &reg);
> +       ret = ufshcd_dwc_phy_reg_read(hba, FW_CALIB_CCFG(0), &reg);
>         if (ret)
>                 return ret;
>
>         reg |= MPHY_FW_CALIB_CFG_VAL;
> -       ret = ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(0), reg);
> +       ret = ufshcd_dwc_phy_reg_write(hba, FW_CALIB_CCFG(0), reg);
>         if (ret)
>                 return ret;
>
> -       ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(1), &reg);
> +       ret = ufshcd_dwc_phy_reg_read(hba, FW_CALIB_CCFG(1), &reg);
>         if (ret)
>                 return ret;
>
>         reg |= MPHY_FW_CALIB_CFG_VAL;
> -       return ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(1), reg);
> +       return ufshcd_dwc_phy_reg_write(hba, FW_CALIB_CCFG(1), reg);
>  }
>
>  static int ufs_versal2_phy_init(struct ufs_hba *hba)
> @@ -406,7 +355,7 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_
>
>         for (lane = 0; lane < activelanes; lane++) {
>                 time_left = TIMEOUT_MICROSEC;
> -               ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
> +               ret = ufshcd_dwc_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
>                 if (ret)
>                         return ret;
>
> @@ -416,12 +365,12 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_
>                 else
>                         reg &= ~MPHY_RX_OVRD_VAL;
>
> -               ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
>                 if (ret)
>                         return ret;
>
>                 do {
> -                       ret = ufs_versal2_phy_reg_read(hba, RX_PCS_OUT(lane), &reg);
> +                       ret = ufshcd_dwc_phy_reg_read(hba, RX_PCS_OUT(lane), &reg);
>                         if (ret)
>                                 return ret;
>
> @@ -483,12 +432,12 @@ static int ufs_versal2_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_ch
>
>                 /* Remove rx_req override */
>                 for (lane = 0; lane < dev_req_params->lane_tx; lane++) {
> -                       ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
> +                       ret = ufshcd_dwc_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
>                         if (ret)
>                                 return ret;
>
>                         reg &= ~MPHY_RX_OVRD_EN;
> -                       ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
> +                       ret = ufshcd_dwc_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
>                         if (ret)
>                                 return ret;
>                 }
> diff --git a/drivers/ufs/host/ufshcd-dwc.c b/drivers/ufs/host/ufshcd-dwc.c
> index 21b1cf912dcc..45920793d1a8 100644
> --- a/drivers/ufs/host/ufshcd-dwc.c
> +++ b/drivers/ufs/host/ufshcd-dwc.c
> @@ -141,6 +141,83 @@ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
>  }
>  EXPORT_SYMBOL(ufshcd_dwc_link_startup_notify);
>
> +/**
> + * ufshcd_dwc_phy_reg_write - Write a DWC M-PHY CREG register
> + * @hba: private structure pointer
> + * @addr: M-PHY CREG register address
> + * @val: value to write
> + *
> + * Write a 16-bit M-PHY CREG register through the Synopsys DesignWare
> + * UniPro indirect register access interface.
> + *
> + * Return: 0 on success, non-zero value on failure.
> + */
> +int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
> +{
> +       struct ufshcd_dme_attr_val phy_write_attrs[] = {
> +               { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> +               { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> +               { UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
> +               { UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
> +               { UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
> +               { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> +       };
> +
> +       phy_write_attrs[0].mib_val = (u8)addr;
> +       phy_write_attrs[1].mib_val = (u8)(addr >> 8);
> +       phy_write_attrs[2].mib_val = (u8)val;
> +       phy_write_attrs[3].mib_val = (u8)(val >> 8);
> +
> +       return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs,
> +                                       ARRAY_SIZE(phy_write_attrs));
> +}
> +EXPORT_SYMBOL(ufshcd_dwc_phy_reg_write);
> +
> +/**
> + * ufshcd_dwc_phy_reg_read - Read a DWC M-PHY CREG register
> + * @hba: private structure pointer
> + * @addr: M-PHY CREG register address
> + * @val: pointer where the read value is stored
> + *
> + * Read a 16-bit M-PHY CREG register through the Synopsys DesignWare
> + * UniPro indirect register access interface.
> + *
> + * Return: 0 on success, non-zero value on failure.
> + */
> +int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val)
> +{
> +       struct ufshcd_dme_attr_val phy_read_attrs[] = {
> +               { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> +               { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> +               { UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL },
> +               { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> +       };
> +       u32 mib_val;
> +       int ret;
> +
> +       phy_read_attrs[0].mib_val = (u8)addr;
> +       phy_read_attrs[1].mib_val = (u8)(addr >> 8);
> +
> +       ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs,
> +                                      ARRAY_SIZE(phy_read_attrs));
> +       if (ret)
> +               return ret;
> +
> +       ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val);
> +       if (ret)
> +               return ret;
> +
> +       *val = mib_val;
> +       ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val);
> +       if (ret)
> +               return ret;
> +
> +       *val |= (mib_val << 8);
> +
> +       return 0;
> +}
> +EXPORT_SYMBOL(ufshcd_dwc_phy_reg_read);
> +
>  MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
>  MODULE_DESCRIPTION("UFS Host driver for Synopsys Designware Core");
>  MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
> index 0406f2bb576b..d8673cd54d4c 100644
> --- a/drivers/ufs/host/ufshcd-dwc.h
> +++ b/drivers/ufs/host/ufshcd-dwc.h
> @@ -45,4 +45,6 @@ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
>                                         enum ufs_notify_change_status status);
>  int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba,
>                                 const struct ufshcd_dme_attr_val *v, int n);
> +int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val);
> +int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val);
>  #endif /* End of Header */
> --
> 2.43.0
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 05/10] scsi: ufs: dwc: Export common clock divider and link status helpers
  2026-08-26 13:40 ` [PATCH 05/10] scsi: ufs: dwc: Export common clock divider and link status helpers Larisa Grigore
@ 2026-08-26 15:22   ` Frank Li
  0 siblings, 0 replies; 35+ messages in thread
From: Frank Li @ 2026-08-26 15:22 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:43PM +0200, Larisa Grigore wrote:
>
> Make ufshcd_dwc_program_clk_div() and ufshcd_dwc_link_is_up() available to
> DWC-based platform glue drivers. These helpers are needed by platform
> drivers that must program the controller HCLK divider and verify link state
> during their vendor-specific initialization flows.
>
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

>  drivers/ufs/host/ufshcd-dwc.c | 6 ++++--
>  drivers/ufs/host/ufshcd-dwc.h | 2 ++
>  2 files changed, 6 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/ufs/host/ufshcd-dwc.c b/drivers/ufs/host/ufshcd-dwc.c
> index 45920793d1a8..4a689d211e81 100644
> --- a/drivers/ufs/host/ufshcd-dwc.c
> +++ b/drivers/ufs/host/ufshcd-dwc.c
> @@ -39,10 +39,11 @@ EXPORT_SYMBOL(ufshcd_dwc_dme_set_attrs);
>   * @divider_val: clock divider value to be programmed
>   *
>   */
> -static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val)
> +void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val)
>  {
>         ufshcd_writel(hba, divider_val, DWC_UFS_REG_HCLKDIV);
>  }
> +EXPORT_SYMBOL(ufshcd_dwc_program_clk_div);
>
>  /**
>   * ufshcd_dwc_link_is_up() - check if link is up.
> @@ -50,7 +51,7 @@ static void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val)
>   *
>   * Return: 0 on success, non-zero value on failure.
>   */
> -static int ufshcd_dwc_link_is_up(struct ufs_hba *hba)
> +int ufshcd_dwc_link_is_up(struct ufs_hba *hba)
>  {
>         int dme_result = 0;
>
> @@ -63,6 +64,7 @@ static int ufshcd_dwc_link_is_up(struct ufs_hba *hba)
>
>         return 1;
>  }
> +EXPORT_SYMBOL(ufshcd_dwc_link_is_up);
>
>  /**
>   * ufshcd_dwc_connection_setup() - configure unipro attributes.
> diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
> index d8673cd54d4c..53b523ef59df 100644
> --- a/drivers/ufs/host/ufshcd-dwc.h
> +++ b/drivers/ufs/host/ufshcd-dwc.h
> @@ -47,4 +47,6 @@ int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba,
>                                 const struct ufshcd_dme_attr_val *v, int n);
>  int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val);
>  int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val);
> +int ufshcd_dwc_link_is_up(struct ufs_hba *hba);
> +void ufshcd_dwc_program_clk_div(struct ufs_hba *hba, u32 divider_val);
>  #endif /* End of Header */
> --
> 2.43.0
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
  2026-08-26 13:40 ` [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper Larisa Grigore
  2026-08-26 15:17   ` Frank Li
@ 2026-08-26 15:33   ` Bart Van Assche
  2026-08-27 14:03     ` Larisa Ileana Grigore
  2026-08-27  9:14   ` Bough Chen
  2026-08-27 15:00   ` Stanley Jhu
  3 siblings, 1 reply; 35+ messages in thread
From: Bart Van Assche @ 2026-08-26 15:33 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

On 8/26/26 6:40 AM, Larisa Grigore wrote:
> +	for (i = 0; i < num_lanes; i++) {
> +		err = ufshcd_dme_get(hba,
> +				UIC_ARG_MIB_SEL(TX_FSM_STATE,
> +					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
> +				&tx_fsm_val);
> +		if (err) {
> +			dev_err(hba->dev,
> +					"%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
> +					__func__, i, err);
> +			return err;
> +		}
> +
> +		if (tx_fsm_val != TX_STATE_HIBERN8) {
> +			dev_err(hba->dev,
> +					"%s: timeout waiting for lane %u to enter HIBERN8, TX_FSM_STATE=%u\n",
> +					__func__, i, tx_fsm_val);
> +			return -ETIMEDOUT;
> +		}
> +	}
Instead of duplicating this code block, please move it into a new
function.

Thanks,

Bart.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc
  2026-08-26 13:40 ` [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc Larisa Grigore
  2026-08-26 15:20   ` Frank Li
@ 2026-08-26 15:36   ` Bart Van Assche
  2026-08-27 14:13     ` Larisa Ileana Grigore
  2026-08-27 10:47   ` Bough Chen
  2 siblings, 1 reply; 35+ messages in thread
From: Bart Van Assche @ 2026-08-26 15:36 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

On 8/26/26 6:40 AM, Larisa Grigore wrote:
> +int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
> +{
> +	struct ufshcd_dme_attr_val phy_write_attrs[] = {
> +		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
> +		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> +	};

This array and similar arrays should be declared 'static const'.

Thanks,

Bart.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver
  2026-08-26 13:40 ` [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver Larisa Grigore
  2026-08-26 14:01   ` sashiko-bot
@ 2026-08-26 15:49   ` Frank Li
  2026-08-27 14:44     ` Larisa Ileana Grigore
  1 sibling, 1 reply; 35+ messages in thread
From: Frank Li @ 2026-08-26 15:49 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:45PM +0200, Larisa Grigore wrote:
>
> Add support for the UFS host controller on the NXP S32N79 SoC, built on
> the Synopsys DesignWare (DWC) UFS architecture, using the UFSHCD DWC and
> UFSHCD platform glue.
>
> This controller requires S32N79-specific initialization prior to
> UIC_LINKSTARTUP, including:
>   - programming SCM.ONE_US_TICK and HC.HCLKDIV based on the core clock,
>   - applying the vendor-defined M-PHY calibration sequence, and
>   - performing post-link calibration steps needed for HS operation.
>
> The M-PHY boot mode is selected via the 'nxp,mphy-boot-mode' device tree
> property. If the property is omitted, M-PHY initialization is assumed to
> be handled by external firmware or by a simulation environment.

Is "simulation environment" productionable?

>
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---
>  drivers/ufs/host/Kconfig         |  12 +
>  drivers/ufs/host/Makefile        |   1 +
>  drivers/ufs/host/ufs-nxp-s32n7.c | 464 +++++++++++++++++++++++++++++++
>  drivers/ufs/host/ufshcd-dwc.h    |   9 +-
>  4 files changed, 484 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/ufs/host/ufs-nxp-s32n7.c
>
> diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig
> index ff170c0b6da0..e8ee7dd05f1a 100644
> --- a/drivers/ufs/host/Kconfig
> +++ b/drivers/ufs/host/Kconfig
> @@ -168,3 +168,15 @@ config SCSI_UFS_AMD_VERSAL2
>
>           Select this if you have UFS controller on AMD Versal Gen 2 SoC.
>           If unsure, say N.
> +
> +config SCSI_UFS_S32N7
> +       tristate "NXP S32N7 platform driver"
> +       depends on SCSI_UFSHCD_PLATFORM && (ARCH_S32 || COMPILE_TEST)
> +       help
> +         This selects the S32N7 specific additions on top of the UFSHCD DWC
> +         and UFSHCD platform driver. UFS host on S32N79 needs some vendor
> +         specific configurations like PHY and vendor specific register accesses
> +         before accessing the hardware.
> +
> +         Select this if you have UFS controller on an S32N7 based board.
> +         If unsure, say N.
> diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile
> index 65d8bb23ab7b..0381750a35d3 100644
> --- a/drivers/ufs/host/Makefile
> +++ b/drivers/ufs/host/Makefile
> @@ -2,6 +2,7 @@
>
>  obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
>  obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
> +obj-$(CONFIG_SCSI_UFS_S32N7) += ufs-nxp-s32n7.o ufshcd-dwc.o

It is not related with your patch, but multi ufshcd-dwc.o may link many
times if other ufs host driver also build, modern link can remove duplicate
symbal, but not sure what happen if one build as module, one buildin.

>  obj-$(CONFIG_SCSI_UFS_CDNS_PLATFORM) += cdns-pltfrm.o
>  obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
>  obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
> diff --git a/drivers/ufs/host/ufs-nxp-s32n7.c b/drivers/ufs/host/ufs-nxp-s32n7.c
> new file mode 100644
> index 000000000000..8a125105536b
> --- /dev/null
> +++ b/drivers/ufs/host/ufs-nxp-s32n7.c
> @@ -0,0 +1,464 @@
> +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
> +/*
> + * Copyright 2026 NXP
> + *
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +
> +#include "ufshcd-pltfrm.h"
> +#include "ufshcd-dwc.h"
> +#include "ufshci-dwc.h"
> +
> +/* SCM Register Offsets. */
> +#define SCM_ONE_US_TICK                        0x2CU
> +#define SCM_STATUS                     0x30
> +
> +/* SCM_ONE_US_TICK Register. */
> +#define SCM_ONE_US_TICK_MASK           GENMASK(8, 0)
> +
> +/* SCM_MPHY_RAM_CONFIG_STATUS Register Fields. */
> +#define SCM_STATUS_PHY_RESET_MASK              BIT(0)
> +#define SCM_STATUS_SRAM_BYPASS_MASK            BIT(1)
> +#define SCM_STATUS_SRAM_INIT_DONE_MASK         BIT(16)
> +
> +#define SCM_STATUS_PHY_RESET(val)              \
> +       FIELD_PREP(SCM_STATUS_PHY_RESET_MASK, val)
> +
> +#define SCM_STATUS_SRAM_BYPASS(val)            \
> +       FIELD_PREP(SCM_STATUS_SRAM_BYPASS_MASK, val)
> +
> +/* Timeout values. */
> +#define CFG_MPHY_INIT_TIMEOUT_VALUE_US 3000000
> +
> +/* Clock validation limits. */
> +#define MAX_VALID_ONE_US_TICK          SCM_ONE_US_TICK_MASK
> +
> +/* CPort definitions */
> +#define CPORT_0                                0
> +
> +/* Hibern8 state poll timeout. */
> +#define HBRN8_POLL_TOUT_MS      1000

Does this value is spec defined or just engineering choose?

> +
> +/**
> + * enum mphy_boot_mode - MPHY boot mode options
> + * @MPHY_BOOT_NONE: Skip MPHY initialization
> + * @MPHY_BOOT_ROM: Running MPHY from internal FW ROM
> + */
> +enum mphy_boot_mode {
> +       MPHY_BOOT_NONE = 0,
> +       MPHY_BOOT_ROM = 1,
> +};
> +
> +/**
> + * struct s32n_ufs - S32N7 UFS host controller data
> + * @hba: UFS host controller instance
> + * @reg_scm: SCM register base address
> + * @core_clk: core reference clock used to derive ONE_US_TICK/HCLKDIV
> + * @mphy_boot_mode: MPHY boot mode configuration
> + */
> +struct s32n_ufs {
> +       struct ufs_hba *hba;
> +       void __iomem *reg_scm;
> +       struct clk *core_clk;
> +       enum mphy_boot_mode mphy_boot_mode;
> +};
> +
> +/**
> + * struct phy_reg_cfg - PHY register configuration entry
> + * @reg: Register offset/address
> + * @val: Value to write
> + */
> +struct phy_reg_cfg {
> +       u32 reg;
> +       u32 val;
> +};
> +
> +/**
> + * ufs_s32n_phy_write_sequence - Write a sequence of PHY registers
> + * @hba: UFS host controller instance
> + * @cfg: Array of register configurations
> + * @count: Number of entries in the array
> + *
> + * Return: 0 on success, error code on failure
> + */
> +static int ufs_s32n_phy_write_sequence(struct ufs_hba *hba,
> +                                      const struct phy_reg_cfg *cfg,
> +                                      size_t count)
> +{
> +       int ret;
> +       size_t i;
> +
> +       for (i = 0; i < count; i++) {
> +               ret = ufshcd_dwc_phy_reg_write(hba, cfg[i].reg, cfg[i].val);
> +               if (ret) {
> +                       dev_err(hba->dev,
> +                               "Failed to write PHY reg 0x%x = 0x%x (step %zu)\n",
> +                               cfg[i].reg, cfg[i].val, i);
> +                       return ret;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static unsigned long ufs_s32n_calculate_us_tick(unsigned long clk_rate)
> +{
> +       return clk_rate / USEC_PER_SEC;
> +}
> +
> +static int ufs_s32n_configure_clocks(struct ufs_hba *hba)
> +{
> +       struct s32n_ufs *ufs = ufshcd_get_variant(hba);
> +       struct device *dev = hba->dev;
> +       unsigned long clk_rate = 0;
> +       unsigned long one_us_tick;
> +
> +       if (!ufs->core_clk) {

suppose return failure at probe if no core_clk, needn't check here

> +               dev_err(dev, "Invalid core_clk.\n");
> +               return -EINVAL;
> +       }
> +
> +       clk_rate = clk_get_rate(ufs->core_clk);
> +       if (!clk_rate) {
> +               dev_err(dev, "Failed to get valid clock rate.\n");
> +               return -EINVAL;
> +       }
> +
> +       one_us_tick = ufs_s32n_calculate_us_tick(clk_rate);
> +       if (one_us_tick == 0 || one_us_tick > MAX_VALID_ONE_US_TICK) {
> +               dev_err(dev, "Invalid one_us_tick value: %lu (clk_rate: %lu Hz).\n",
> +                       one_us_tick, clk_rate);
> +               return -EINVAL;
> +       }
> +
> +       dev_dbg(dev, "Core clock rate: %lu Hz, one_us_tick = %lu.\n",
> +                clk_rate, one_us_tick);
> +
> +       /*
> +        * Configure the micro-second tick rate generator based on core
> +        * clock rate.
> +        */
> +       writel(one_us_tick, ufs->reg_scm + SCM_ONE_US_TICK);
> +       ufshcd_dwc_program_clk_div(hba, one_us_tick);
> +
> +       return 0;
> +}
> +
> +static int ufs_s32n_phy_initial_calib(struct ufs_hba *hba)
> +{
> +       static const struct phy_reg_cfg initial_calib[] = {
> +               { FAST_FLAGS(0), 0x6 },
> +               { FAST_FLAGS(1), 0x6 },
> +               { RX_DAC_CTRL_OVRD(0), 0x1 },
> +               { RX_DAC_CTRL_OVRD(1), 0x1 },
> +               { RX_DAC_CTRL(0), 0x8E },
> +               { RX_DAC_CTRL(1), 0x91 },
> +               { RX_DAC_CTRL_SEL(0), 0x1 },
> +               { RX_DAC_CTRL_SEL(1), 0x1 },
> +               { RX_DAC_CTRL_EN(0), 0x1 },
> +               { RX_DAC_CTRL_EN(1), 0x1 },
> +               { RX_DAC_CTRL_OVRD(0), 0x0 },
> +               { RX_DAC_CTRL_OVRD(1), 0x0 },
> +               { RX_DAC_CTRL_OVRD(0), 0x1 },
> +               { RX_DAC_CTRL_OVRD(1), 0x1 },
> +               { RX_DAC_CTRL(0), 0x71 },
> +               { RX_DAC_CTRL(1), 0x7F },
> +               { RX_DAC_CTRL_SEL(0), 0x2 },
> +               { RX_DAC_CTRL_SEL(1), 0x2 },
> +               { RX_DAC_CTRL_EN(0), 0x1 },
> +               { RX_DAC_CTRL_EN(1), 0x1 },
> +               { RX_DAC_CTRL_OVRD(0), 0x0 },
> +               { RX_DAC_CTRL_OVRD(1), 0x0 },
> +               { FW_CALIB_CCFG(0), 0x100 },
> +               { FW_CALIB_CCFG(1), 0x100 },
> +       };

Can you put this data to platform data in case difference data for differe
soc in future?

> +       int ret;
> +
> +       /* Clock Control */
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBREFCLKCTRL2, 0), 0x80);
> +       if (ret)
> +               return ret;
> +
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
> +       if (ret)
> +               return ret;
> +
> +       /* Initial calibration sequence */
> +       return ufs_s32n_phy_write_sequence(hba, initial_calib,
> +                                          ARRAY_SIZE(initial_calib));
> +}
> +
> +static int ufs_s32n_link_startup_pre_change(struct ufs_hba *hba)
> +{
> +       struct s32n_ufs *ufs = ufshcd_get_variant(hba);
> +       struct device *dev = hba->dev;
> +       int ret;
> +       u32 reg;
> +
> +       /*
> +        * There are two supported MPHY boot options implemented:
> +        * - Running MPHY from internal FW ROM:
> +        *  MPHY_RAM_CONFIG_STATUS.SRAM_BYPASS = 1
> +        *  MPHY_RAM_CONFIG_STATUS.SRAM_EXT_LD_DONE = 0
> +        * - Skip M-PHY initialization when it is handled externally.
> +        */
> +       if (ufs->mphy_boot_mode == MPHY_BOOT_NONE) {
> +               dev_dbg(dev, "Skipping UFS MPHY init.\n");
> +               return 0;
> +       }
> +
> +       ret = ufs_s32n_configure_clocks(hba);
> +       if (ret)
> +               return ret;
> +
> +       /* Reset SCM.MPHY_RAM_CONFIG_STATUS to default value; keep MPHY in reset. */
> +       writel(SCM_STATUS_PHY_RESET(1), ufs->reg_scm + SCM_STATUS);
> +       if (ufs->mphy_boot_mode == MPHY_BOOT_ROM) {
> +               /* ROM Mode. */
> +               writel(SCM_STATUS_PHY_RESET(1) | SCM_STATUS_SRAM_BYPASS(1),
> +                      ufs->reg_scm + SCM_STATUS);
> +       }
> +
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBCRCTRL, 0), 0x1);
> +       if (ret)
> +               return ret;
> +
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
> +       if (ret)
> +               return ret;
> +
> +       /* Release PHY_RESET. */
> +       writel(readl(ufs->reg_scm + SCM_STATUS) & ~SCM_STATUS_PHY_RESET_MASK,
> +              ufs->reg_scm + SCM_STATUS);
> +
> +       /* Wait until SRAM_INIT_DONE = 1. */
> +       ret = readl_poll_timeout(ufs->reg_scm + SCM_STATUS, reg,
> +                       reg & SCM_STATUS_SRAM_INIT_DONE_MASK,
> +                       1000, CFG_MPHY_INIT_TIMEOUT_VALUE_US);
> +       if (ret) {
> +               dev_err(dev, "UFS MPHY init not done!\n");
> +               return ret;
> +       }
> +
> +       /* Start of initial calibration */
> +       ret = ufs_s32n_phy_initial_calib(hba);
> +       if (ret) {
> +               dev_err(dev, "UFS MPHY initial calibration failed!\n");
> +               return ret;
> +       }
> +
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYDISABLE, 0), 0x0);
> +       if (ret)
> +               return ret;
> +
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
> +       if (ret)
> +               return ret;
> +
> +       /* Set Local DeviceID. */
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID, 0), 0x0);
> +       if (ret)
> +               return ret;
> +
> +       /* End of Gear1 settings */
> +
> +       return ufshcd_check_hibern8(hba, hba->lanes_per_direction,
> +                                   HBRN8_POLL_TOUT_MS);
> +}
> +
> +static int ufs_s32n_link_startup_post_change(struct ufs_hba *hba)
> +{
> +       static const struct ufshcd_dme_attr_val cport_setup[] = {
> +               { UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0),
> +                               CPORT_IDLE, DME_LOCAL },
> +               { UIC_ARG_MIB_SEL(T_CPORTFLAGS, CPORT_0),
> +                               CPORT_DEF_FLAGS, DME_LOCAL },
> +               { UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0),
> +                               CPORT_CONNECTED, DME_LOCAL },
> +       };
> +       static const struct phy_reg_cfg post_calib[] = {
> +               { RX_OVRD_IN_1(0), 0xc },
> +               { RX_OVRD_IN_1(1), 0xc },
> +               { RX_OVRD_IN_1(0), 0x8 },
> +               { RX_OVRD_IN_1(1), 0x8 },
> +               { RX_OVRD_IN_1(0), 0x0 },
> +               { RX_OVRD_IN_1(1), 0x0 },
> +       };
> +       struct s32n_ufs *ufs = ufshcd_get_variant(hba);
> +       unsigned int data = 0;
> +       int ret;
> +
> +       if (ufs->mphy_boot_mode == MPHY_BOOT_NONE)
> +               return 0;
> +
> +       /* Set Connection State to IDLE (it allows CPort Attributes to be set). */
> +       ret = ufshcd_dwc_dme_set_attrs(hba, cport_setup, ARRAY_SIZE(cport_setup));
> +       if (ret)
> +               return ret;
> +
> +       ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0), &data);
> +       if (ret)
> +               return ret;
> +
> +       if (data != CPORT_CONNECTED)
> +               return -EIO;
> +
> +       /* Post Link Startup Calibration sequence */
> +       ret = ufs_s32n_phy_write_sequence(hba, post_calib,
> +                                          ARRAY_SIZE(post_calib));
> +       if (ret)
> +               return ret;
> +
> +       /*
> +        * Performing MPHY configuration for rate change:
> +        * CB rate selection: 0 - rate A, 1 - rate B;
> +        */
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBRATESEL, 0), 0x1);
> +       if (ret)
> +               return ret;
> +
> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
> +       if (ret)
> +               return ret;
> +
> +       /* End of Gear4 calibration */
> +       return 0;
> +}
> +
> +static int ufs_s32n_link_startup_notify(struct ufs_hba *hba,
> +                            enum ufs_notify_change_status status)
> +{
> +       int err;
> +
> +       if (status == PRE_CHANGE) {
> +               err = ufs_s32n_link_startup_pre_change(hba);
> +               if (err) {
> +                       dev_err(hba->dev, "MPHY setup failed (%d).\n", err);
> +                       return err;
> +               }
> +               return 0;
> +       }
> +
> +       /* POST_CHANGE */
> +       err = ufshcd_dwc_link_is_up(hba);
> +       if (err) {
> +               dev_err(hba->dev, "Link is not up.\n");
> +               return err;
> +       }
> +
> +       err = ufs_s32n_link_startup_post_change(hba);
> +       if (err)
> +               dev_err(hba->dev, "Connection setup failed (%d).\n", err);
> +
> +       return err;
> +}
> +
> +static int ufs_s32n_init(struct ufs_hba *hba)
> +{
> +       struct device *dev = hba->dev;
> +       struct platform_device *pdev = to_platform_device(dev);
> +       struct device_node *np = dev->of_node;
> +       const char *mphy_boot_mode;
> +       struct s32n_ufs *ufs;
> +       int ret;
> +
> +       ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
> +       if (!ufs)
> +               return -ENOMEM;
> +
> +       ufs->hba = hba;
> +       ufs->mphy_boot_mode = MPHY_BOOT_NONE;
> +
> +       hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
> +       hba->quirks |= UFSHCD_QUIRK_PERFORM_LINK_STARTUP_ONCE;
> +       hba->spm_lvl = UFS_PM_LVL_5;
> +
> +       ret = of_property_read_string(np, "nxp,mphy-boot-mode", &mphy_boot_mode);
> +       if (ret || !mphy_boot_mode) {
> +               dev_dbg(dev,
> +                       "nxp,mphy-boot-mode property not found. UFS MPHY init will be skipped.\n");
> +               goto init_out;
> +       }
> +
> +       if (!strcmp(mphy_boot_mode, "rom")) {
> +               ufs->mphy_boot_mode = MPHY_BOOT_ROM;
> +       } else {
> +               return dev_err_probe(dev, -EINVAL,
> +                                    "Unrecognized nxp,mphy-boot-mode property. UFS MPHY init will be skipped.\n");
> +       }
> +
> +       ufs->reg_scm = devm_platform_ioremap_resource_byname(pdev, "scm");
> +       if (IS_ERR(ufs->reg_scm))
> +               return dev_err_probe(dev, PTR_ERR(ufs->reg_scm),
> +                                    "ioremap failed for SCM registers.\n");
> +
> +       ufs->core_clk = devm_clk_get(dev, "core_clk");
> +       if (IS_ERR(ufs->core_clk))
> +               return dev_err_probe(dev, PTR_ERR(ufs->core_clk),
> +                                    "Failed to get core clock.\n");
> +
> +init_out:
> +       ufshcd_set_variant(hba, ufs);
> +
> +       return 0;
> +}
> +
> +static const struct ufs_hba_variant_ops ufs_hba_s32n79_vops = {
> +       .name = "s32n79",
> +       .init = ufs_s32n_init,
> +       .link_startup_notify = ufs_s32n_link_startup_notify,
> +};
> +
> +static const struct of_device_id ufs_s32n7_match[] = {
> +       {
> +               .compatible = "nxp,s32n79-ufshc",
> +               .data = &ufs_hba_s32n79_vops,
> +       },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, ufs_s32n7_match);
> +
> +static int ufs_s32n7_probe(struct platform_device *pdev)
> +{
> +       const struct ufs_hba_variant_ops *vops;
> +
> +       vops = device_get_match_data(&pdev->dev);
> +       if (!vops)
> +               return -ENODEV;
> +
> +       return ufshcd_pltfrm_init(pdev, vops);
> +}
> +
> +static void ufs_s32n7_remove(struct platform_device *pdev)
> +{
> +       ufshcd_pltfrm_remove(pdev);
> +}
> +
> +static const struct dev_pm_ops ufs_s32n7_pm_ops = {
> +       SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume)
> +       SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL)

Use  SYSTEM_SLEEP_PM_OPS()

> +       .prepare        = ufshcd_suspend_prepare,
> +       .complete       = ufshcd_resume_complete,
> +};
> +
> +static struct platform_driver ufs_s32n7_driver = {
> +       .probe = ufs_s32n7_probe,
> +       .remove = ufs_s32n7_remove,
> +       .driver = {
> +               .name   = "ufs-s32n7",
> +               .pm     = &ufs_s32n7_pm_ops,

pm_ptr(&ufs_s32n7_pm_ops)

Frank

> +               .of_match_table = ufs_s32n7_match,
> +       },
> +};
> +
> +module_platform_driver(ufs_s32n7_driver);
> +
> +MODULE_AUTHOR("Larisa Grigore <larisa.grigore@oss.nxp.com>");
> +MODULE_DESCRIPTION("NXP S32N7 UFS Host Controller platform driver");
> +MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
> index 53b523ef59df..747383cfb839 100644
> --- a/drivers/ufs/host/ufshcd-dwc.h
> +++ b/drivers/ufs/host/ufshcd-dwc.h
> @@ -13,8 +13,7 @@
>  #include <ufs/ufshcd.h>
>
>  /* RMMI Attributes */
> -#define CBREFCLKCTRL2          0x8132
> -#define CBCRCTRL               0x811F
> +#define RXSQCONTROL            0x8009
>  #define CBC10DIRECTCONF2       0x810E
>  #define CBRATESEL              0x8114
>  #define CBCREGADDRLSB          0x8116
> @@ -24,10 +23,16 @@
>  #define CBCREGRDLSB            0x811A
>  #define CBCREGRDMSB            0x811B
>  #define CBCREGRDWRSEL          0x811C
> +#define CBCRCTRL               0x811F
> +#define CBREFCLKCTRL2          0x8132
>
>  #define CBREFREFCLK_GATE_OVR_EN                BIT(7)
>
>  /* M-PHY registers */
> +#define RX_DAC_CTRL(n)         (0x10AF + ((n) * 0x100))
> +#define RX_DAC_CTRL_OVRD(n)    (0x10B0 + ((n) * 0x100))
> +#define RX_DAC_CTRL_SEL(n)     (0x10B1 + ((n) * 0x100))
> +#define RX_DAC_CTRL_EN(n)      (0x10B8 + ((n) * 0x100))
>  #define RX_OVRD_IN_1(n)                (0x3006 + ((n) * 0x100))
>  #define RX_PCS_OUT(n)          (0x300F + ((n) * 0x100))
>  #define FAST_FLAGS(n)          (0x401C + ((n) * 0x100))
> --
> 2.43.0
>
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver
  2026-08-26 14:35   ` Frank Li
@ 2026-08-26 15:57     ` Conor Dooley
  2026-08-27 14:37       ` Larisa Ileana Grigore
  2026-08-27 14:36     ` Larisa Ileana Grigore
  1 sibling, 1 reply; 35+ messages in thread
From: Conor Dooley @ 2026-08-26 15:57 UTC (permalink / raw)
  To: Frank Li
  Cc: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

[-- Attachment #1: Type: text/plain, Size: 2684 bytes --]

On Wed, Aug 26, 2026 at 09:35:01AM -0500, Frank Li wrote:
> On Wed, Aug 26, 2026 at 03:40:44PM +0200, Larisa Grigore wrote:
> > [You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
> >
> > Document the device tree binding for the NXP S32N79 DWC-based UFS host
> > controller which is compliant with JESD-223D "Universal Flash Storage Host
> > Controller Interface" v3.0.
> >
> > The controller requires access to the SCM register block and the core
> > clock rate when M-PHY initialization is performed. Add the optional
> > nxp,mphy-boot-mode property to select the M-PHY firmware execution
> > environment.
> > Currently, the M-PHY firmware can only run from ROM. If the property is
> > omitted, M-PHY initialization is assumed to be handled externally.
> >
> > Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> > ---
> >  .../bindings/ufs/nxp,s32n7-ufshc.yaml         | 99 +++++++++++++++++++
> >  1 file changed, 99 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
> >
> > diff --git a/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
> > new file mode 100644
> > index 000000000000..d9d5669be313
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
> > @@ -0,0 +1,99 @@
> > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > +%YAML 1.2
> > +---
> > +$id: http://devicetree.org/schemas/ufs/nxp,s32n7-ufshc.yaml#
> > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > +
> > +title: NXP S32N79 Universal Flash Storage (UFS) Controller
> > +
> > +maintainers:
> > +  - Larisa Grigore <larisa.grigore@oss.nxp.com>
> > +
> > +# Select only our matches, not all jedec,ufs
> > +select:
> > +  properties:
> > +    compatible:
> > +      contains:
> > +        const: nxp,s32n79-ufshc
> > +  required:
> > +    - compatible
> > +
> > +properties:
> > +  compatible:
> > +    items:
> > +      - const: nxp,s32n79-ufshc
> > +      - const: jedec,ufs-3.0
> 
> Any place actually use this fallback?

I don't think that this exists anywhere already:
https://lore.kernel.org/all/?q=%22jedec%2Cufs-3.0%22
$ git checkout korg-next/master 
Previous HEAD position was 2697ef8943c9 Add linux-next specific files for 20260813
HEAD is now at f82a5da2f049 Add linux-next specific files for 20260826
$ rg jedec,ufs-3.0
$ 

So not entirely sure what the point of this actually is, if the driver
doesn't use the fallback.

pw-bot: changes-requested

Thanks,
Conor.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
  2026-08-26 13:40 ` [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper Larisa Grigore
  2026-08-26 15:17   ` Frank Li
  2026-08-26 15:33   ` Bart Van Assche
@ 2026-08-27  9:14   ` Bough Chen
  2026-08-27 14:10     ` Larisa Ileana Grigore
  2026-08-27 15:00   ` Stanley Jhu
  3 siblings, 1 reply; 35+ messages in thread
From: Bough Chen @ 2026-08-27  9:14 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:41PM +0200, Larisa Grigore wrote:
> Factor out the common logic used to poll TX_FSM_STATE until the TX lanes
> enter Hibern8 into ufshcd_check_hibern8().
> 
> The HiSilicon and Qualcomm drivers currently implement similar checks
> using the UniPro TX_FSM_STATE attribute after link/PHY bring-up. Move
> this logic to a common helper in ufshcd-pltfrm so it can be shared
> across host controller drivers.
> 
> Switch the HiSilicon and Qualcomm drivers to use the new helper and drop
> their local implementations.

The code change the error return value for qcom and hisi, better to point this
out like the following, so that QCOM/HISI guys may pay attention on it:

"While at it, normalize the "TX lane failed to reach Hibern8" error path to
return -ETIMEDOUT instead of the raw TX_FSM_STATE value (qcom) or -1 (hisi),
so all callers get a proper errno. This is an intentional, harmless change of
the returned error value; the pass/fail behaviour at the call sites is
unchanged."

Regards
Haibo Chen
> 
> This also prepares for reusing the same UniPro-specific Hibern8 check in
> a subsequent commit.
> 
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---
>  drivers/ufs/host/ufs-hisi.c      | 48 +------------------
>  drivers/ufs/host/ufs-qcom.c      | 42 +----------------
>  drivers/ufs/host/ufshcd-pltfrm.c | 79 ++++++++++++++++++++++++++++++++
>  drivers/ufs/host/ufshcd-pltfrm.h |  2 +
>  4 files changed, 84 insertions(+), 87 deletions(-)
> 
> diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
> index bd223bda1ce2..76d77bc30322 100644
> --- a/drivers/ufs/host/ufs-hisi.c
> +++ b/drivers/ufs/host/ufs-hisi.c
> @@ -22,50 +22,6 @@
>  #include <ufs/ufshci.h>
>  #include <ufs/ufs_quirks.h>
>  
> -static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
> -{
> -	int err = 0;
> -	u32 tx_fsm_val_0 = 0;
> -	u32 tx_fsm_val_1 = 0;
> -	unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
> -
> -	do {
> -		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
> -				      &tx_fsm_val_0);
> -		err |= ufshcd_dme_get(hba,
> -		    UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
> -		if (err || (tx_fsm_val_0 == TX_STATE_HIBERN8 &&
> -			tx_fsm_val_1 == TX_STATE_HIBERN8))
> -			break;
> -
> -		/* sleep for max. 200us */
> -		usleep_range(100, 200);
> -	} while (time_before(jiffies, timeout));
> -
> -	/*
> -	 * we might have scheduled out for long during polling so
> -	 * check the state again.
> -	 */
> -	if (time_after(jiffies, timeout)) {
> -		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
> -				     &tx_fsm_val_0);
> -		err |= ufshcd_dme_get(hba,
> -		 UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
> -	}
> -
> -	if (err) {
> -		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
> -			__func__, err);
> -	} else if (tx_fsm_val_0 != TX_STATE_HIBERN8 ||
> -			 tx_fsm_val_1 != TX_STATE_HIBERN8) {
> -		err = -1;
> -		dev_err(hba->dev, "%s: invalid TX_FSM_STATE, lane0 = %d, lane1 = %d\n",
> -			__func__, tx_fsm_val_0, tx_fsm_val_1);
> -	}
> -
> -	return err;
> -}
> -
>  static void ufs_hisi_clk_init(struct ufs_hba *hba)
>  {
>  	struct ufs_hisi_host *host = ufshcd_get_variant(hba);
> @@ -224,9 +180,9 @@ static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
>  
>  	/* Unipro VS_mphy_disable */
>  	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), 0x0);
> -	err = ufs_hisi_check_hibern8(hba);
> +	err = ufshcd_check_hibern8(hba, 2, HBRN8_POLL_TOUT_MS);
>  	if (err)
> -		dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n");
> +		dev_err(hba->dev, "hibern8 TX FSM check failed\n");
>  
>  	if (!(host->caps & UFS_HISI_CAP_PHY10nm))
>  		ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV);
> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
> index b2081ed6197a..c94488661bcf 100644
> --- a/drivers/ufs/host/ufs-qcom.c
> +++ b/drivers/ufs/host/ufs-qcom.c
> @@ -382,46 +382,6 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host)
>  	return 0;
>  }
>  
> -static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
> -{
> -	int err;
> -	u32 tx_fsm_val;
> -	unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
> -
> -	do {
> -		err = ufshcd_dme_get(hba,
> -				UIC_ARG_MIB_SEL(TX_FSM_STATE,
> -					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
> -				&tx_fsm_val);
> -		if (err || tx_fsm_val == TX_STATE_HIBERN8)
> -			break;
> -
> -		/* sleep for max. 200us */
> -		usleep_range(100, 200);
> -	} while (time_before(jiffies, timeout));
> -
> -	/*
> -	 * we might have scheduled out for long during polling so
> -	 * check the state again.
> -	 */
> -	if (time_after(jiffies, timeout))
> -		err = ufshcd_dme_get(hba,
> -				UIC_ARG_MIB_SEL(TX_FSM_STATE,
> -					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
> -				&tx_fsm_val);
> -
> -	if (err) {
> -		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
> -				__func__, err);
> -	} else if (tx_fsm_val != TX_STATE_HIBERN8) {
> -		err = tx_fsm_val;
> -		dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n",
> -				__func__, err);
> -	}
> -
> -	return err;
> -}
> -
>  static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host)
>  {
>  	ufshcd_rmwl(host->hba, QUNIPRO_SEL, QUNIPRO_SEL, REG_UFS_CFG1);
> @@ -607,7 +567,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
>  		break;
>  	case POST_CHANGE:
>  		/* check if UFS PHY moved from DISABLED to HIBERN8 */
> -		err = ufs_qcom_check_hibern8(hba);
> +		err = ufshcd_check_hibern8(hba, 1, HBRN8_POLL_TOUT_MS);
>  		ufs_qcom_enable_hw_clk_gating(hba);
>  		ufs_qcom_ice_enable(host);
>  		ufs_qcom_config_ice_allocator(host);
> diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
> index c2dafb583cf5..034ee988924a 100644
> --- a/drivers/ufs/host/ufshcd-pltfrm.c
> +++ b/drivers/ufs/host/ufshcd-pltfrm.c
> @@ -9,6 +9,7 @@
>   */
>  
>  #include <linux/clk.h>
> +#include <linux/delay.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_opp.h>
> @@ -563,6 +564,84 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
>  }
>  EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
>  
> +/**
> + * ufshcd_check_hibern8 - Check if all TX lanes entered Hibern8 state
> + * @hba: host controller instance
> + * @num_lanes: number of TX lanes to check
> + * @timeout_ms: timeout in milliseconds for all lanes
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
> +			 unsigned int timeout_ms)
> +{
> +	unsigned long timeout;
> +	u32 tx_fsm_val = 0;
> +	unsigned int i;
> +	bool success;
> +	int err;
> +
> +	if (!num_lanes)
> +		return -EINVAL;
> +
> +	timeout = jiffies + msecs_to_jiffies(timeout_ms);
> +
> +	do {
> +		success = true;
> +
> +		for (i = 0; i < num_lanes; i++) {
> +			err = ufshcd_dme_get(hba,
> +					UIC_ARG_MIB_SEL(TX_FSM_STATE,
> +						UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
> +					&tx_fsm_val);
> +			if (err) {
> +				dev_err(hba->dev,
> +						"%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
> +						__func__, i, err);
> +				return err;
> +			}
> +
> +			if (tx_fsm_val != TX_STATE_HIBERN8) {
> +				success = false;
> +				break;
> +			}
> +		}
> +
> +		if (success)
> +			return 0;
> +
> +		/* sleep for max. 200us */
> +		usleep_range(100, 200);
> +	} while (time_before(jiffies, timeout));
> +
> +	/*
> +	 * We might have been scheduled out for long during polling, so do
> +	 * one final check before reporting timeout.
> +	 */
> +	for (i = 0; i < num_lanes; i++) {
> +		err = ufshcd_dme_get(hba,
> +				UIC_ARG_MIB_SEL(TX_FSM_STATE,
> +					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
> +				&tx_fsm_val);
> +		if (err) {
> +			dev_err(hba->dev,
> +					"%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
> +					__func__, i, err);
> +			return err;
> +		}
> +
> +		if (tx_fsm_val != TX_STATE_HIBERN8) {
> +			dev_err(hba->dev,
> +					"%s: timeout waiting for lane %u to enter HIBERN8, TX_FSM_STATE=%u\n",
> +					__func__, i, tx_fsm_val);
> +			return -ETIMEDOUT;
> +		}
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(ufshcd_check_hibern8);
> +
>  MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
>  MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
>  MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
> diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h
> index 0a18a8aed94d..ec2a8013e975 100644
> --- a/drivers/ufs/host/ufshcd-pltfrm.h
> +++ b/drivers/ufs/host/ufshcd-pltfrm.h
> @@ -35,5 +35,7 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
>  void ufshcd_pltfrm_remove(struct platform_device *pdev);
>  int ufshcd_populate_vreg(struct device *dev, const char *name,
>  			 struct ufs_vreg **out_vreg, bool skip_current);
> +int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
> +			 unsigned int timeout_ms);
>  
>  #endif /* UFSHCD_PLTFRM_H_ */
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc
  2026-08-26 13:40 ` [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc Larisa Grigore
  2026-08-26 15:20   ` Frank Li
  2026-08-26 15:36   ` Bart Van Assche
@ 2026-08-27 10:47   ` Bough Chen
  2 siblings, 0 replies; 35+ messages in thread
From: Bough Chen @ 2026-08-27 10:47 UTC (permalink / raw)
  To: Larisa Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:42PM +0200, Larisa Grigore wrote:
> The AMD Versal Gen 2 UFS platform driver carries local helpers to access
> M-PHY control registers through the UniPro CREG interface.
> 
> Move these helpers into the common Synopsys DWC layer as
> ufshcd_dwc_phy_reg_read() and ufshcd_dwc_phy_reg_write(). This avoids code
> duplication in DWC-based glue drivers and provides a single implementation
> for future reuse.
> Dropped `static` from `phy_write_attrs` and `phy_read_attrs` since there
> is no benefit in keeping them persistent.
> 
> Update the AMD Versal2 glue driver to use the new ufshcd-dwc helpers.

ufs-sprd.c also contain similar code, seems can also re-use this helper, but it
do not include ufshcd-dwc.h, Unisoc(sprd) guys can do the further check.

Regards
Haibo Chen
> 
> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> ---
>  drivers/ufs/host/ufs-amd-versal2.c | 85 ++++++------------------------
>  drivers/ufs/host/ufshcd-dwc.c      | 77 +++++++++++++++++++++++++++
>  drivers/ufs/host/ufshcd-dwc.h      |  2 +
>  3 files changed, 96 insertions(+), 68 deletions(-)
> 
> diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c
> index 0d4d5e98ad65..c7da95999c85 100644
> --- a/drivers/ufs/host/ufs-amd-versal2.c
> +++ b/drivers/ufs/host/ufs-amd-versal2.c
> @@ -43,57 +43,6 @@ struct ufs_versal2_host {
>  	u8 ctlecompval1;
>  };
>  
> -static int ufs_versal2_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
> -{
> -	static struct ufshcd_dme_attr_val phy_write_attrs[] = {
> -		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> -		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> -		{ UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
> -		{ UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
> -		{ UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
> -		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> -	};
> -
> -	phy_write_attrs[0].mib_val = (u8)addr;
> -	phy_write_attrs[1].mib_val = (u8)(addr >> 8);
> -	phy_write_attrs[2].mib_val = (u8)val;
> -	phy_write_attrs[3].mib_val = (u8)(val >> 8);
> -
> -	return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs, ARRAY_SIZE(phy_write_attrs));
> -}
> -
> -static int ufs_versal2_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val)
> -{
> -	u32 mib_val;
> -	int ret;
> -	static struct ufshcd_dme_attr_val phy_read_attrs[] = {
> -		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> -		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> -		{ UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL },
> -		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> -	};
> -
> -	phy_read_attrs[0].mib_val = (u8)addr;
> -	phy_read_attrs[1].mib_val = (u8)(addr >> 8);
> -
> -	ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs, ARRAY_SIZE(phy_read_attrs));
> -	if (ret)
> -		return ret;
> -
> -	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val);
> -	if (ret)
> -		return ret;
> -
> -	*val = mib_val;
> -	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val);
> -	if (ret)
> -		return ret;
> -
> -	*val |= (mib_val << 8);
> -
> -	return 0;
> -}
> -
>  static int ufs_versal2_enable_phy(struct ufs_hba *hba)
>  {
>  	u32 offset, reg;
> @@ -162,64 +111,64 @@ static int ufs_versal2_setup_phy(struct ufs_hba *hba)
>  	u32 reg;
>  
>  	/* Bypass RX-AFE offset calibrations (ATT/CTLE) */
> -	ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(0), &reg);
> +	ret = ufshcd_dwc_phy_reg_read(hba, FAST_FLAGS(0), &reg);
>  	if (ret)
>  		return ret;
>  
>  	reg |= MPHY_FAST_RX_AFE_CAL;
> -	ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(0), reg);
> +	ret = ufshcd_dwc_phy_reg_write(hba, FAST_FLAGS(0), reg);
>  	if (ret)
>  		return ret;
>  
> -	ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(1), &reg);
> +	ret = ufshcd_dwc_phy_reg_read(hba, FAST_FLAGS(1), &reg);
>  	if (ret)
>  		return ret;
>  
>  	reg |= MPHY_FAST_RX_AFE_CAL;
> -	ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(1), reg);
> +	ret = ufshcd_dwc_phy_reg_write(hba, FAST_FLAGS(1), reg);
>  	if (ret)
>  		return ret;
>  
>  	/* Program ATT and CTLE compensation values */
>  	if (host->attcompval0) {
> -		ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), host->attcompval0);
> +		ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), host->attcompval0);
>  		if (ret)
>  			return ret;
>  	}
>  
>  	if (host->attcompval1) {
> -		ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), host->attcompval1);
> +		ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), host->attcompval1);
>  		if (ret)
>  			return ret;
>  	}
>  
>  	if (host->ctlecompval0) {
> -		ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), host->ctlecompval0);
> +		ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), host->ctlecompval0);
>  		if (ret)
>  			return ret;
>  	}
>  
>  	if (host->ctlecompval1) {
> -		ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), host->ctlecompval1);
> +		ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), host->ctlecompval1);
>  		if (ret)
>  			return ret;
>  	}
>  
> -	ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(0), &reg);
> +	ret = ufshcd_dwc_phy_reg_read(hba, FW_CALIB_CCFG(0), &reg);
>  	if (ret)
>  		return ret;
>  
>  	reg |= MPHY_FW_CALIB_CFG_VAL;
> -	ret = ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(0), reg);
> +	ret = ufshcd_dwc_phy_reg_write(hba, FW_CALIB_CCFG(0), reg);
>  	if (ret)
>  		return ret;
>  
> -	ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(1), &reg);
> +	ret = ufshcd_dwc_phy_reg_read(hba, FW_CALIB_CCFG(1), &reg);
>  	if (ret)
>  		return ret;
>  
>  	reg |= MPHY_FW_CALIB_CFG_VAL;
> -	return ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(1), reg);
> +	return ufshcd_dwc_phy_reg_write(hba, FW_CALIB_CCFG(1), reg);
>  }
>  
>  static int ufs_versal2_phy_init(struct ufs_hba *hba)
> @@ -406,7 +355,7 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_
>  
>  	for (lane = 0; lane < activelanes; lane++) {
>  		time_left = TIMEOUT_MICROSEC;
> -		ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
> +		ret = ufshcd_dwc_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
>  		if (ret)
>  			return ret;
>  
> @@ -416,12 +365,12 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_
>  		else
>  			reg &= ~MPHY_RX_OVRD_VAL;
>  
> -		ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
> +		ret = ufshcd_dwc_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
>  		if (ret)
>  			return ret;
>  
>  		do {
> -			ret = ufs_versal2_phy_reg_read(hba, RX_PCS_OUT(lane), &reg);
> +			ret = ufshcd_dwc_phy_reg_read(hba, RX_PCS_OUT(lane), &reg);
>  			if (ret)
>  				return ret;
>  
> @@ -483,12 +432,12 @@ static int ufs_versal2_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_ch
>  
>  		/* Remove rx_req override */
>  		for (lane = 0; lane < dev_req_params->lane_tx; lane++) {
> -			ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
> +			ret = ufshcd_dwc_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
>  			if (ret)
>  				return ret;
>  
>  			reg &= ~MPHY_RX_OVRD_EN;
> -			ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
> +			ret = ufshcd_dwc_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
>  			if (ret)
>  				return ret;
>  		}
> diff --git a/drivers/ufs/host/ufshcd-dwc.c b/drivers/ufs/host/ufshcd-dwc.c
> index 21b1cf912dcc..45920793d1a8 100644
> --- a/drivers/ufs/host/ufshcd-dwc.c
> +++ b/drivers/ufs/host/ufshcd-dwc.c
> @@ -141,6 +141,83 @@ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
>  }
>  EXPORT_SYMBOL(ufshcd_dwc_link_startup_notify);
>  
> +/**
> + * ufshcd_dwc_phy_reg_write - Write a DWC M-PHY CREG register
> + * @hba: private structure pointer
> + * @addr: M-PHY CREG register address
> + * @val: value to write
> + *
> + * Write a 16-bit M-PHY CREG register through the Synopsys DesignWare
> + * UniPro indirect register access interface.
> + *
> + * Return: 0 on success, non-zero value on failure.
> + */
> +int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
> +{
> +	struct ufshcd_dme_attr_val phy_write_attrs[] = {
> +		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
> +		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> +	};
> +
> +	phy_write_attrs[0].mib_val = (u8)addr;
> +	phy_write_attrs[1].mib_val = (u8)(addr >> 8);
> +	phy_write_attrs[2].mib_val = (u8)val;
> +	phy_write_attrs[3].mib_val = (u8)(val >> 8);
> +
> +	return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs,
> +					ARRAY_SIZE(phy_write_attrs));
> +}
> +EXPORT_SYMBOL(ufshcd_dwc_phy_reg_write);
> +
> +/**
> + * ufshcd_dwc_phy_reg_read - Read a DWC M-PHY CREG register
> + * @hba: private structure pointer
> + * @addr: M-PHY CREG register address
> + * @val: pointer where the read value is stored
> + *
> + * Read a 16-bit M-PHY CREG register through the Synopsys DesignWare
> + * UniPro indirect register access interface.
> + *
> + * Return: 0 on success, non-zero value on failure.
> + */
> +int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val)
> +{
> +	struct ufshcd_dme_attr_val phy_read_attrs[] = {
> +		{ UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL },
> +		{ UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
> +	};
> +	u32 mib_val;
> +	int ret;
> +
> +	phy_read_attrs[0].mib_val = (u8)addr;
> +	phy_read_attrs[1].mib_val = (u8)(addr >> 8);
> +
> +	ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs,
> +				       ARRAY_SIZE(phy_read_attrs));
> +	if (ret)
> +		return ret;
> +
> +	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val);
> +	if (ret)
> +		return ret;
> +
> +	*val = mib_val;
> +	ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val);
> +	if (ret)
> +		return ret;
> +
> +	*val |= (mib_val << 8);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(ufshcd_dwc_phy_reg_read);
> +
>  MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
>  MODULE_DESCRIPTION("UFS Host driver for Synopsys Designware Core");
>  MODULE_LICENSE("Dual BSD/GPL");
> diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
> index 0406f2bb576b..d8673cd54d4c 100644
> --- a/drivers/ufs/host/ufshcd-dwc.h
> +++ b/drivers/ufs/host/ufshcd-dwc.h
> @@ -45,4 +45,6 @@ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
>  					enum ufs_notify_change_status status);
>  int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba,
>  				const struct ufshcd_dme_attr_val *v, int n);
> +int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val);
> +int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val);
>  #endif /* End of Header */
> -- 
> 2.43.0
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h
  2026-08-26 15:05   ` Frank Li
@ 2026-08-27 13:59     ` Larisa Ileana Grigore
  0 siblings, 0 replies; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 13:59 UTC (permalink / raw)
  To: Frank Li
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On 8/26/2026 6:05 PM, Frank Li wrote:
> On Wed, Aug 26, 2026 at 03:40:39PM +0200, Larisa Grigore wrote:
>> [You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>>
>> Move the Tx/Rx FSM state definitions from ufshcd-dwc.h to
>> include/ufs/unipro.h.
>>
>> These state values are UniPro/M-PHY related and are not specific to
>> the DesignWare UFS host controller implementation, so unipro.h is a
>> more appropriate location for sharing them.
>>
>> As part of this move, drop the driver-private TX_FSM_HIBERN8 define in
>> ufs-hisi and ufs-qcom and repoint their Hibern8 checks at the shared
>> TX_STATE_HIBERN8 enum constant. This is a no-op change: TX_FSM_HIBERN8
>> was 0x1 and TX_STATE_HIBERN8 is 1, so the compared values are identical.
>>
>> This also prepares for using these definitions in a subsequent commit.
>>
>> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
>> ---
> ...
>> -enum rx_fsm_state {
>> -       RX_STATE_DISABLED = 0,
>> -       RX_STATE_HIBERN8 = 1,
>> -       RX_STATE_SLEEP = 2,
>> -       RX_STATE_STALL = 3,
>> -       RX_STATE_LSBURST = 4,
>> -       RX_STATE_HSBURST = 5,
>> -};
>> -
>> -enum tx_fsm_state {
>> -       TX_STATE_DISABLED = 0,
>> -       TX_STATE_HIBERN8 = 1,
>> -       TX_STATE_SLEEP = 2,
>> -       TX_STATE_STALL = 3,
>> -       TX_STATE_LSBURST = 4,
>> -       TX_STATE_HSBURST = 5,
>> -};
>> -
>>   struct ufshcd_dme_attr_val {
>>          u32 attr_sel;
>>          u32 mib_val;
>> diff --git a/include/ufs/unipro.h b/include/ufs/unipro.h
>> index 9c168703b104..bf063edc284d 100644
>> --- a/include/ufs/unipro.h
>> +++ b/include/ufs/unipro.h
>> @@ -466,4 +466,23 @@ enum {
>>          CPORT_CONNECTED,
>>   };
>>
>> +/* Tx/Rx FSM state */
>> +enum ufs_rx_fsm_state {
>> +       RX_STATE_DISABLED = 0,
>> +       RX_STATE_HIBERN8 = 1,
>> +       RX_STATE_SLEEP = 2,
>> +       RX_STATE_STALL = 3,
>> +       RX_STATE_LSBURST = 4,
>> +       RX_STATE_HSBURST = 5,
>> +};
>> +
>> +enum ufs_tx_fsm_state {
>> +       TX_STATE_DISABLED = 0,
>> +       TX_STATE_HIBERN8 = 1,
>> +       TX_STATE_SLEEP = 2,
>> +       TX_STATE_STALL = 3,
>> +       TX_STATE_LSBURST = 4,
>> +       TX_STATE_HSBURST = 5,
>> +};
> 
> Suppose ref RX(TX)_FSM_State in mipi m-phy speci v6.0, table 50 M-RX status
> attributes. you add ref to mipi M-phy to let reader know it is really
> defined by spec, not dwc implementation.
> 
> Both value are the same, why need define two copy.
> 
> Frank

Hi Frank,

Thank you for taking the time to review this patch series and for the 
feedback!

The reason the two enums currently look duplicated is that they 
represent the encodings of two distinct MIPI M-PHY attributes, 
RX_FSM_STATE and TX_FSM_STATE. The specification defines identical state 
encodings for both RX and TX FSMs, which is why the values are the same.

My intention was to preserve the original approach and minimize the 
scope of the changes in this patch. Since the existing code already 
treats the RX and TX FSM states separately, I kept that structure when 
moving the definitions to the common header.

I will clarify in the commit message that these encodings come from the 
MIPI M-PHY specification and are not DesignWare-specific.

Regarding the separate enums, I can consolidate them into a single FSM 
state enum if you think that would be a better representation, given 
that the state encodings are identical.

Thanks,
  Larisa

>> +
>>   #endif /* _UNIPRO_H_ */
>> --
>> 2.43.0
>>
>>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
  2026-08-26 15:33   ` Bart Van Assche
@ 2026-08-27 14:03     ` Larisa Ileana Grigore
  0 siblings, 0 replies; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 14:03 UTC (permalink / raw)
  To: Bart Van Assche, NXP S32 Linux Team
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

On 8/26/2026 6:33 PM, Bart Van Assche wrote:
> On 8/26/26 6:40 AM, Larisa Grigore wrote:
>> +    for (i = 0; i < num_lanes; i++) {
>> +        err = ufshcd_dme_get(hba,
>> +                UIC_ARG_MIB_SEL(TX_FSM_STATE,
>> +                    UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
>> +                &tx_fsm_val);
>> +        if (err) {
>> +            dev_err(hba->dev,
>> +                    "%s: unable to get TX_FSM_STATE for lane %u, err 
>> %d\n",
>> +                    __func__, i, err);
>> +            return err;
>> +        }
>> +
>> +        if (tx_fsm_val != TX_STATE_HIBERN8) {
>> +            dev_err(hba->dev,
>> +                    "%s: timeout waiting for lane %u to enter 
>> HIBERN8, TX_FSM_STATE=%u\n",
>> +                    __func__, i, tx_fsm_val);
>> +            return -ETIMEDOUT;
>> +        }
>> +    }
> Instead of duplicating this code block, please move it into a new
> function.
> 
> Thanks,
> 
> Bart.

Thanks for taking the time to review this patch series! Will do it.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
  2026-08-27  9:14   ` Bough Chen
@ 2026-08-27 14:10     ` Larisa Ileana Grigore
  0 siblings, 0 replies; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 14:10 UTC (permalink / raw)
  To: Bough Chen
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On 8/27/2026 12:14 PM, Bough Chen wrote:
> On Wed, Aug 26, 2026 at 03:40:41PM +0200, Larisa Grigore wrote:
>> Factor out the common logic used to poll TX_FSM_STATE until the TX lanes
>> enter Hibern8 into ufshcd_check_hibern8().
>>
>> The HiSilicon and Qualcomm drivers currently implement similar checks
>> using the UniPro TX_FSM_STATE attribute after link/PHY bring-up. Move
>> this logic to a common helper in ufshcd-pltfrm so it can be shared
>> across host controller drivers.
>>
>> Switch the HiSilicon and Qualcomm drivers to use the new helper and drop
>> their local implementations.
> 
> The code change the error return value for qcom and hisi, better to point this
> out like the following, so that QCOM/HISI guys may pay attention on it:
> 
> "While at it, normalize the "TX lane failed to reach Hibern8" error path to
> return -ETIMEDOUT instead of the raw TX_FSM_STATE value (qcom) or -1 (hisi),
> so all callers get a proper errno. This is an intentional, harmless change of
> the returned error value; the pass/fail behaviour at the call sites is
> unchanged."
> 
> Regards
> Haibo Chen

Thanks for taking the time to review this patch series! I’ll address it 
in the next revision.

>>
>> This also prepares for reusing the same UniPro-specific Hibern8 check in
>> a subsequent commit.
>>
>> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
>> ---
>>   drivers/ufs/host/ufs-hisi.c      | 48 +------------------
>>   drivers/ufs/host/ufs-qcom.c      | 42 +----------------
>>   drivers/ufs/host/ufshcd-pltfrm.c | 79 ++++++++++++++++++++++++++++++++
>>   drivers/ufs/host/ufshcd-pltfrm.h |  2 +
>>   4 files changed, 84 insertions(+), 87 deletions(-)
>>
>> diff --git a/drivers/ufs/host/ufs-hisi.c b/drivers/ufs/host/ufs-hisi.c
>> index bd223bda1ce2..76d77bc30322 100644
>> --- a/drivers/ufs/host/ufs-hisi.c
>> +++ b/drivers/ufs/host/ufs-hisi.c
>> @@ -22,50 +22,6 @@
>>   #include <ufs/ufshci.h>
>>   #include <ufs/ufs_quirks.h>
>>   
>> -static int ufs_hisi_check_hibern8(struct ufs_hba *hba)
>> -{
>> -	int err = 0;
>> -	u32 tx_fsm_val_0 = 0;
>> -	u32 tx_fsm_val_1 = 0;
>> -	unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
>> -
>> -	do {
>> -		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
>> -				      &tx_fsm_val_0);
>> -		err |= ufshcd_dme_get(hba,
>> -		    UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
>> -		if (err || (tx_fsm_val_0 == TX_STATE_HIBERN8 &&
>> -			tx_fsm_val_1 == TX_STATE_HIBERN8))
>> -			break;
>> -
>> -		/* sleep for max. 200us */
>> -		usleep_range(100, 200);
>> -	} while (time_before(jiffies, timeout));
>> -
>> -	/*
>> -	 * we might have scheduled out for long during polling so
>> -	 * check the state again.
>> -	 */
>> -	if (time_after(jiffies, timeout)) {
>> -		err = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(TX_FSM_STATE, 0),
>> -				     &tx_fsm_val_0);
>> -		err |= ufshcd_dme_get(hba,
>> -		 UIC_ARG_MIB_SEL(TX_FSM_STATE, 1), &tx_fsm_val_1);
>> -	}
>> -
>> -	if (err) {
>> -		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
>> -			__func__, err);
>> -	} else if (tx_fsm_val_0 != TX_STATE_HIBERN8 ||
>> -			 tx_fsm_val_1 != TX_STATE_HIBERN8) {
>> -		err = -1;
>> -		dev_err(hba->dev, "%s: invalid TX_FSM_STATE, lane0 = %d, lane1 = %d\n",
>> -			__func__, tx_fsm_val_0, tx_fsm_val_1);
>> -	}
>> -
>> -	return err;
>> -}
>> -
>>   static void ufs_hisi_clk_init(struct ufs_hba *hba)
>>   {
>>   	struct ufs_hisi_host *host = ufshcd_get_variant(hba);
>> @@ -224,9 +180,9 @@ static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
>>   
>>   	/* Unipro VS_mphy_disable */
>>   	ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xD0C1, 0x0), 0x0);
>> -	err = ufs_hisi_check_hibern8(hba);
>> +	err = ufshcd_check_hibern8(hba, 2, HBRN8_POLL_TOUT_MS);
>>   	if (err)
>> -		dev_err(hba->dev, "ufs_hisi_check_hibern8 error\n");
>> +		dev_err(hba->dev, "hibern8 TX FSM check failed\n");
>>   
>>   	if (!(host->caps & UFS_HISI_CAP_PHY10nm))
>>   		ufshcd_writel(hba, UFS_HCLKDIV_NORMAL_VALUE, UFS_REG_HCLKDIV);
>> diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
>> index b2081ed6197a..c94488661bcf 100644
>> --- a/drivers/ufs/host/ufs-qcom.c
>> +++ b/drivers/ufs/host/ufs-qcom.c
>> @@ -382,46 +382,6 @@ static int ufs_qcom_init_lane_clks(struct ufs_qcom_host *host)
>>   	return 0;
>>   }
>>   
>> -static int ufs_qcom_check_hibern8(struct ufs_hba *hba)
>> -{
>> -	int err;
>> -	u32 tx_fsm_val;
>> -	unsigned long timeout = jiffies + msecs_to_jiffies(HBRN8_POLL_TOUT_MS);
>> -
>> -	do {
>> -		err = ufshcd_dme_get(hba,
>> -				UIC_ARG_MIB_SEL(TX_FSM_STATE,
>> -					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
>> -				&tx_fsm_val);
>> -		if (err || tx_fsm_val == TX_STATE_HIBERN8)
>> -			break;
>> -
>> -		/* sleep for max. 200us */
>> -		usleep_range(100, 200);
>> -	} while (time_before(jiffies, timeout));
>> -
>> -	/*
>> -	 * we might have scheduled out for long during polling so
>> -	 * check the state again.
>> -	 */
>> -	if (time_after(jiffies, timeout))
>> -		err = ufshcd_dme_get(hba,
>> -				UIC_ARG_MIB_SEL(TX_FSM_STATE,
>> -					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(0)),
>> -				&tx_fsm_val);
>> -
>> -	if (err) {
>> -		dev_err(hba->dev, "%s: unable to get TX_FSM_STATE, err %d\n",
>> -				__func__, err);
>> -	} else if (tx_fsm_val != TX_STATE_HIBERN8) {
>> -		err = tx_fsm_val;
>> -		dev_err(hba->dev, "%s: invalid TX_FSM_STATE = %d\n",
>> -				__func__, err);
>> -	}
>> -
>> -	return err;
>> -}
>> -
>>   static void ufs_qcom_select_unipro_mode(struct ufs_qcom_host *host)
>>   {
>>   	ufshcd_rmwl(host->hba, QUNIPRO_SEL, QUNIPRO_SEL, REG_UFS_CFG1);
>> @@ -607,7 +567,7 @@ static int ufs_qcom_hce_enable_notify(struct ufs_hba *hba,
>>   		break;
>>   	case POST_CHANGE:
>>   		/* check if UFS PHY moved from DISABLED to HIBERN8 */
>> -		err = ufs_qcom_check_hibern8(hba);
>> +		err = ufshcd_check_hibern8(hba, 1, HBRN8_POLL_TOUT_MS);
>>   		ufs_qcom_enable_hw_clk_gating(hba);
>>   		ufs_qcom_ice_enable(host);
>>   		ufs_qcom_config_ice_allocator(host);
>> diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
>> index c2dafb583cf5..034ee988924a 100644
>> --- a/drivers/ufs/host/ufshcd-pltfrm.c
>> +++ b/drivers/ufs/host/ufshcd-pltfrm.c
>> @@ -9,6 +9,7 @@
>>    */
>>   
>>   #include <linux/clk.h>
>> +#include <linux/delay.h>
>>   #include <linux/module.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/pm_opp.h>
>> @@ -563,6 +564,84 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
>>   }
>>   EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
>>   
>> +/**
>> + * ufshcd_check_hibern8 - Check if all TX lanes entered Hibern8 state
>> + * @hba: host controller instance
>> + * @num_lanes: number of TX lanes to check
>> + * @timeout_ms: timeout in milliseconds for all lanes
>> + *
>> + * Return: 0 on success, negative errno on failure.
>> + */
>> +int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
>> +			 unsigned int timeout_ms)
>> +{
>> +	unsigned long timeout;
>> +	u32 tx_fsm_val = 0;
>> +	unsigned int i;
>> +	bool success;
>> +	int err;
>> +
>> +	if (!num_lanes)
>> +		return -EINVAL;
>> +
>> +	timeout = jiffies + msecs_to_jiffies(timeout_ms);
>> +
>> +	do {
>> +		success = true;
>> +
>> +		for (i = 0; i < num_lanes; i++) {
>> +			err = ufshcd_dme_get(hba,
>> +					UIC_ARG_MIB_SEL(TX_FSM_STATE,
>> +						UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
>> +					&tx_fsm_val);
>> +			if (err) {
>> +				dev_err(hba->dev,
>> +						"%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
>> +						__func__, i, err);
>> +				return err;
>> +			}
>> +
>> +			if (tx_fsm_val != TX_STATE_HIBERN8) {
>> +				success = false;
>> +				break;
>> +			}
>> +		}
>> +
>> +		if (success)
>> +			return 0;
>> +
>> +		/* sleep for max. 200us */
>> +		usleep_range(100, 200);
>> +	} while (time_before(jiffies, timeout));
>> +
>> +	/*
>> +	 * We might have been scheduled out for long during polling, so do
>> +	 * one final check before reporting timeout.
>> +	 */
>> +	for (i = 0; i < num_lanes; i++) {
>> +		err = ufshcd_dme_get(hba,
>> +				UIC_ARG_MIB_SEL(TX_FSM_STATE,
>> +					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
>> +				&tx_fsm_val);
>> +		if (err) {
>> +			dev_err(hba->dev,
>> +					"%s: unable to get TX_FSM_STATE for lane %u, err %d\n",
>> +					__func__, i, err);
>> +			return err;
>> +		}
>> +
>> +		if (tx_fsm_val != TX_STATE_HIBERN8) {
>> +			dev_err(hba->dev,
>> +					"%s: timeout waiting for lane %u to enter HIBERN8, TX_FSM_STATE=%u\n",
>> +					__func__, i, tx_fsm_val);
>> +			return -ETIMEDOUT;
>> +		}
>> +	}
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(ufshcd_check_hibern8);
>> +
>>   MODULE_AUTHOR("Santosh Yaragnavi <santosh.sy@samsung.com>");
>>   MODULE_AUTHOR("Vinayak Holikatti <h.vinayak@samsung.com>");
>>   MODULE_DESCRIPTION("UFS host controller Platform bus based glue driver");
>> diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h
>> index 0a18a8aed94d..ec2a8013e975 100644
>> --- a/drivers/ufs/host/ufshcd-pltfrm.h
>> +++ b/drivers/ufs/host/ufshcd-pltfrm.h
>> @@ -35,5 +35,7 @@ int ufshcd_pltfrm_init(struct platform_device *pdev,
>>   void ufshcd_pltfrm_remove(struct platform_device *pdev);
>>   int ufshcd_populate_vreg(struct device *dev, const char *name,
>>   			 struct ufs_vreg **out_vreg, bool skip_current);
>> +int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
>> +			 unsigned int timeout_ms);
>>   
>>   #endif /* UFSHCD_PLTFRM_H_ */
>> -- 
>> 2.43.0
>>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc
  2026-08-26 15:20   ` Frank Li
@ 2026-08-27 14:11     ` Larisa Ileana Grigore
  0 siblings, 0 replies; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 14:11 UTC (permalink / raw)
  To: Frank Li
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On 8/26/2026 6:20 PM, Frank Li wrote:
> On Wed, Aug 26, 2026 at 03:40:42PM +0200, Larisa Grigore wrote:
>>
>> The AMD Versal Gen 2 UFS platform driver carries local helpers to access
>> M-PHY control registers through the UniPro CREG interface.
>>
>> Move these helpers into the common Synopsys DWC layer as
>> ufshcd_dwc_phy_reg_read() and ufshcd_dwc_phy_reg_write(). This avoids code
>> duplication in DWC-based glue drivers and provides a single implementation
>> for future reuse.
> 
> Nit: add empty line between paragraph
> 
Thank you! I’ll address it in the next revision.

>> Dropped `static` from `phy_write_attrs` and `phy_read_attrs` since there
>> is no benefit in keeping them persistent.
>>
>> Update the AMD Versal2 glue driver to use the new ufshcd-dwc helpers.
>>
>> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
>> ---
> 
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> 
>>   drivers/ufs/host/ufs-amd-versal2.c | 85 ++++++------------------------
>>   drivers/ufs/host/ufshcd-dwc.c      | 77 +++++++++++++++++++++++++++
>>   drivers/ufs/host/ufshcd-dwc.h      |  2 +
>>   3 files changed, 96 insertions(+), 68 deletions(-)
>>
>> diff --git a/drivers/ufs/host/ufs-amd-versal2.c b/drivers/ufs/host/ufs-amd-versal2.c
>> index 0d4d5e98ad65..c7da95999c85 100644
>> --- a/drivers/ufs/host/ufs-amd-versal2.c
>> +++ b/drivers/ufs/host/ufs-amd-versal2.c
>> @@ -43,57 +43,6 @@ struct ufs_versal2_host {
>>          u8 ctlecompval1;
>>   };
>>
>> -static int ufs_versal2_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
>> -{
>> -       static struct ufshcd_dme_attr_val phy_write_attrs[] = {
>> -               { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
>> -               { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
>> -               { UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
>> -               { UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
>> -               { UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
>> -               { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
>> -       };
>> -
>> -       phy_write_attrs[0].mib_val = (u8)addr;
>> -       phy_write_attrs[1].mib_val = (u8)(addr >> 8);
>> -       phy_write_attrs[2].mib_val = (u8)val;
>> -       phy_write_attrs[3].mib_val = (u8)(val >> 8);
>> -
>> -       return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs, ARRAY_SIZE(phy_write_attrs));
>> -}
>> -
>> -static int ufs_versal2_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val)
>> -{
>> -       u32 mib_val;
>> -       int ret;
>> -       static struct ufshcd_dme_attr_val phy_read_attrs[] = {
>> -               { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
>> -               { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
>> -               { UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL },
>> -               { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
>> -       };
>> -
>> -       phy_read_attrs[0].mib_val = (u8)addr;
>> -       phy_read_attrs[1].mib_val = (u8)(addr >> 8);
>> -
>> -       ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs, ARRAY_SIZE(phy_read_attrs));
>> -       if (ret)
>> -               return ret;
>> -
>> -       ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val);
>> -       if (ret)
>> -               return ret;
>> -
>> -       *val = mib_val;
>> -       ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val);
>> -       if (ret)
>> -               return ret;
>> -
>> -       *val |= (mib_val << 8);
>> -
>> -       return 0;
>> -}
>> -
>>   static int ufs_versal2_enable_phy(struct ufs_hba *hba)
>>   {
>>          u32 offset, reg;
>> @@ -162,64 +111,64 @@ static int ufs_versal2_setup_phy(struct ufs_hba *hba)
>>          u32 reg;
>>
>>          /* Bypass RX-AFE offset calibrations (ATT/CTLE) */
>> -       ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(0), &reg);
>> +       ret = ufshcd_dwc_phy_reg_read(hba, FAST_FLAGS(0), &reg);
>>          if (ret)
>>                  return ret;
>>
>>          reg |= MPHY_FAST_RX_AFE_CAL;
>> -       ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(0), reg);
>> +       ret = ufshcd_dwc_phy_reg_write(hba, FAST_FLAGS(0), reg);
>>          if (ret)
>>                  return ret;
>>
>> -       ret = ufs_versal2_phy_reg_read(hba, FAST_FLAGS(1), &reg);
>> +       ret = ufshcd_dwc_phy_reg_read(hba, FAST_FLAGS(1), &reg);
>>          if (ret)
>>                  return ret;
>>
>>          reg |= MPHY_FAST_RX_AFE_CAL;
>> -       ret = ufs_versal2_phy_reg_write(hba, FAST_FLAGS(1), reg);
>> +       ret = ufshcd_dwc_phy_reg_write(hba, FAST_FLAGS(1), reg);
>>          if (ret)
>>                  return ret;
>>
>>          /* Program ATT and CTLE compensation values */
>>          if (host->attcompval0) {
>> -               ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), host->attcompval0);
>> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_ATT_IDAC(0), host->attcompval0);
>>                  if (ret)
>>                          return ret;
>>          }
>>
>>          if (host->attcompval1) {
>> -               ret = ufs_versal2_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), host->attcompval1);
>> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_ATT_IDAC(1), host->attcompval1);
>>                  if (ret)
>>                          return ret;
>>          }
>>
>>          if (host->ctlecompval0) {
>> -               ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), host->ctlecompval0);
>> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_CTLE_IDAC(0), host->ctlecompval0);
>>                  if (ret)
>>                          return ret;
>>          }
>>
>>          if (host->ctlecompval1) {
>> -               ret = ufs_versal2_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), host->ctlecompval1);
>> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_AFE_CTLE_IDAC(1), host->ctlecompval1);
>>                  if (ret)
>>                          return ret;
>>          }
>>
>> -       ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(0), &reg);
>> +       ret = ufshcd_dwc_phy_reg_read(hba, FW_CALIB_CCFG(0), &reg);
>>          if (ret)
>>                  return ret;
>>
>>          reg |= MPHY_FW_CALIB_CFG_VAL;
>> -       ret = ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(0), reg);
>> +       ret = ufshcd_dwc_phy_reg_write(hba, FW_CALIB_CCFG(0), reg);
>>          if (ret)
>>                  return ret;
>>
>> -       ret = ufs_versal2_phy_reg_read(hba, FW_CALIB_CCFG(1), &reg);
>> +       ret = ufshcd_dwc_phy_reg_read(hba, FW_CALIB_CCFG(1), &reg);
>>          if (ret)
>>                  return ret;
>>
>>          reg |= MPHY_FW_CALIB_CFG_VAL;
>> -       return ufs_versal2_phy_reg_write(hba, FW_CALIB_CCFG(1), reg);
>> +       return ufshcd_dwc_phy_reg_write(hba, FW_CALIB_CCFG(1), reg);
>>   }
>>
>>   static int ufs_versal2_phy_init(struct ufs_hba *hba)
>> @@ -406,7 +355,7 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_
>>
>>          for (lane = 0; lane < activelanes; lane++) {
>>                  time_left = TIMEOUT_MICROSEC;
>> -               ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
>> +               ret = ufshcd_dwc_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
>>                  if (ret)
>>                          return ret;
>>
>> @@ -416,12 +365,12 @@ static int ufs_versal2_phy_ratesel(struct ufs_hba *hba, u32 activelanes, u32 rx_
>>                  else
>>                          reg &= ~MPHY_RX_OVRD_VAL;
>>
>> -               ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
>> +               ret = ufshcd_dwc_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
>>                  if (ret)
>>                          return ret;
>>
>>                  do {
>> -                       ret = ufs_versal2_phy_reg_read(hba, RX_PCS_OUT(lane), &reg);
>> +                       ret = ufshcd_dwc_phy_reg_read(hba, RX_PCS_OUT(lane), &reg);
>>                          if (ret)
>>                                  return ret;
>>
>> @@ -483,12 +432,12 @@ static int ufs_versal2_pwr_change_notify(struct ufs_hba *hba, enum ufs_notify_ch
>>
>>                  /* Remove rx_req override */
>>                  for (lane = 0; lane < dev_req_params->lane_tx; lane++) {
>> -                       ret = ufs_versal2_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
>> +                       ret = ufshcd_dwc_phy_reg_read(hba, RX_OVRD_IN_1(lane), &reg);
>>                          if (ret)
>>                                  return ret;
>>
>>                          reg &= ~MPHY_RX_OVRD_EN;
>> -                       ret = ufs_versal2_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
>> +                       ret = ufshcd_dwc_phy_reg_write(hba, RX_OVRD_IN_1(lane), reg);
>>                          if (ret)
>>                                  return ret;
>>                  }
>> diff --git a/drivers/ufs/host/ufshcd-dwc.c b/drivers/ufs/host/ufshcd-dwc.c
>> index 21b1cf912dcc..45920793d1a8 100644
>> --- a/drivers/ufs/host/ufshcd-dwc.c
>> +++ b/drivers/ufs/host/ufshcd-dwc.c
>> @@ -141,6 +141,83 @@ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
>>   }
>>   EXPORT_SYMBOL(ufshcd_dwc_link_startup_notify);
>>
>> +/**
>> + * ufshcd_dwc_phy_reg_write - Write a DWC M-PHY CREG register
>> + * @hba: private structure pointer
>> + * @addr: M-PHY CREG register address
>> + * @val: value to write
>> + *
>> + * Write a 16-bit M-PHY CREG register through the Synopsys DesignWare
>> + * UniPro indirect register access interface.
>> + *
>> + * Return: 0 on success, non-zero value on failure.
>> + */
>> +int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
>> +{
>> +       struct ufshcd_dme_attr_val phy_write_attrs[] = {
>> +               { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
>> +               { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
>> +               { UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
>> +               { UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
>> +               { UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
>> +               { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
>> +       };
>> +
>> +       phy_write_attrs[0].mib_val = (u8)addr;
>> +       phy_write_attrs[1].mib_val = (u8)(addr >> 8);
>> +       phy_write_attrs[2].mib_val = (u8)val;
>> +       phy_write_attrs[3].mib_val = (u8)(val >> 8);
>> +
>> +       return ufshcd_dwc_dme_set_attrs(hba, phy_write_attrs,
>> +                                       ARRAY_SIZE(phy_write_attrs));
>> +}
>> +EXPORT_SYMBOL(ufshcd_dwc_phy_reg_write);
>> +
>> +/**
>> + * ufshcd_dwc_phy_reg_read - Read a DWC M-PHY CREG register
>> + * @hba: private structure pointer
>> + * @addr: M-PHY CREG register address
>> + * @val: pointer where the read value is stored
>> + *
>> + * Read a 16-bit M-PHY CREG register through the Synopsys DesignWare
>> + * UniPro indirect register access interface.
>> + *
>> + * Return: 0 on success, non-zero value on failure.
>> + */
>> +int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val)
>> +{
>> +       struct ufshcd_dme_attr_val phy_read_attrs[] = {
>> +               { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
>> +               { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
>> +               { UIC_ARG_MIB(CBCREGRDWRSEL), 0, DME_LOCAL },
>> +               { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
>> +       };
>> +       u32 mib_val;
>> +       int ret;
>> +
>> +       phy_read_attrs[0].mib_val = (u8)addr;
>> +       phy_read_attrs[1].mib_val = (u8)(addr >> 8);
>> +
>> +       ret = ufshcd_dwc_dme_set_attrs(hba, phy_read_attrs,
>> +                                      ARRAY_SIZE(phy_read_attrs));
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDLSB), &mib_val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       *val = mib_val;
>> +       ret = ufshcd_dme_get(hba, UIC_ARG_MIB(CBCREGRDMSB), &mib_val);
>> +       if (ret)
>> +               return ret;
>> +
>> +       *val |= (mib_val << 8);
>> +
>> +       return 0;
>> +}
>> +EXPORT_SYMBOL(ufshcd_dwc_phy_reg_read);
>> +
>>   MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
>>   MODULE_DESCRIPTION("UFS Host driver for Synopsys Designware Core");
>>   MODULE_LICENSE("Dual BSD/GPL");
>> diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
>> index 0406f2bb576b..d8673cd54d4c 100644
>> --- a/drivers/ufs/host/ufshcd-dwc.h
>> +++ b/drivers/ufs/host/ufshcd-dwc.h
>> @@ -45,4 +45,6 @@ int ufshcd_dwc_link_startup_notify(struct ufs_hba *hba,
>>                                          enum ufs_notify_change_status status);
>>   int ufshcd_dwc_dme_set_attrs(struct ufs_hba *hba,
>>                                  const struct ufshcd_dme_attr_val *v, int n);
>> +int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val);
>> +int ufshcd_dwc_phy_reg_read(struct ufs_hba *hba, u32 addr, u32 *val);
>>   #endif /* End of Header */
>> --
>> 2.43.0
>>
>>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc
  2026-08-26 15:36   ` Bart Van Assche
@ 2026-08-27 14:13     ` Larisa Ileana Grigore
  0 siblings, 0 replies; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 14:13 UTC (permalink / raw)
  To: Bart Van Assche, NXP S32 Linux Team
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

On 8/26/2026 6:36 PM, Bart Van Assche wrote:
> On 8/26/26 6:40 AM, Larisa Grigore wrote:
>> +int ufshcd_dwc_phy_reg_write(struct ufs_hba *hba, u32 addr, u32 val)
>> +{
>> +    struct ufshcd_dme_attr_val phy_write_attrs[] = {
>> +        { UIC_ARG_MIB(CBCREGADDRLSB), 0, DME_LOCAL },
>> +        { UIC_ARG_MIB(CBCREGADDRMSB), 0, DME_LOCAL },
>> +        { UIC_ARG_MIB(CBCREGWRLSB), 0, DME_LOCAL },
>> +        { UIC_ARG_MIB(CBCREGWRMSB), 0, DME_LOCAL },
>> +        { UIC_ARG_MIB(CBCREGRDWRSEL), 1, DME_LOCAL },
>> +        { UIC_ARG_MIB(VS_MPHYCFGUPDT), 1, DME_LOCAL }
>> +    };
> 
> This array and similar arrays should be declared 'static const'.
> 
> Thanks,
> 
> Bart.

Thanks for the review Bart! I did not make the arrays static because 
their entries depend on the runtime addr and val arguments. A shared 
static buffer would be racy if two callers ran concurrently. However, I 
can make the arrays const in v2 by moving the addr/val-derived bytes 
directly into the initializer.

Regards,
Larisa

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver
  2026-08-26 14:35   ` Frank Li
  2026-08-26 15:57     ` Conor Dooley
@ 2026-08-27 14:36     ` Larisa Ileana Grigore
  2026-08-27 14:45       ` Frank Li
  1 sibling, 1 reply; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 14:36 UTC (permalink / raw)
  To: Frank Li
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On 8/26/2026 5:35 PM, Frank Li wrote:
> On Wed, Aug 26, 2026 at 03:40:44PM +0200, Larisa Grigore wrote:
>> [You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>>
>> Document the device tree binding for the NXP S32N79 DWC-based UFS host
>> controller which is compliant with JESD-223D "Universal Flash Storage Host
>> Controller Interface" v3.0.
>>
>> The controller requires access to the SCM register block and the core
>> clock rate when M-PHY initialization is performed. Add the optional
>> nxp,mphy-boot-mode property to select the M-PHY firmware execution
>> environment.
>> Currently, the M-PHY firmware can only run from ROM. If the property is
>> omitted, M-PHY initialization is assumed to be handled externally.
>>
>> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
>> ---
>>   .../bindings/ufs/nxp,s32n7-ufshc.yaml         | 99 +++++++++++++++++++
>>   1 file changed, 99 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>>
>> diff --git a/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>> new file mode 100644
>> index 000000000000..d9d5669be313
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>> @@ -0,0 +1,99 @@
>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/ufs/nxp,s32n7-ufshc.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: NXP S32N79 Universal Flash Storage (UFS) Controller
>> +
>> +maintainers:
>> +  - Larisa Grigore <larisa.grigore@oss.nxp.com>
>> +
>> +# Select only our matches, not all jedec,ufs
>> +select:
>> +  properties:
>> +    compatible:
>> +      contains:
>> +        const: nxp,s32n79-ufshc
>> +  required:
>> +    - compatible
>> +
>> +properties:
>> +  compatible:
>> +    items:
>> +      - const: nxp,s32n79-ufshc
>> +      - const: jedec,ufs-3.0
> 
> Any place actually use this fallback?

You're right Frank! I will switch to `jedec,ufs-2.0` in v2.

> 
>> +
>> +  reg:
>> +    minItems: 1
>> +    items:
>> +      - description: UFS Host Controller registers
>> +      - description: System Control Module registers
>> +
>> +  reg-names:
>> +    description: |
>> +      Names for the register regions. The first entry must be "ufshc".
>> +      The second entry "scm" is mandatory only when nxp,mphy-boot-mode
>> +      property is present.
> 
> you already us if - else branch to restrict this limition, needn't
> description here.

Thank you! Will drop reg-names description in v2.

>> +    minItems: 1
>> +    items:
>> +      - const: ufshc
>> +      - const: scm
>> +
>> +  clocks:
>> +    maxItems: 1
>> +    description: UFS core clock
>> +
>> +  clock-names:
>> +    items:
>> +      - const: core_clk
> 
> Needn't suffix _clk, just "core", generally needn't clock-names if only
> one clocks.

I will rename it to "core" in v2, if that works for you.

>> +
>> +  nxp,mphy-boot-mode:
>> +    description: |
>> +      MPHY initialization mode. Defines how the MPHY firmware should be
>> +      initialized and executed during UFS controller startup.
>> +      If this property is not specified, MPHY initialization will be skipped.
>> +    $ref: /schemas/types.yaml#/definitions/string
>> +    enum:
>> +      - rom
> 
> why not use standard "phy" property, if no "phy" means ROM init it.

First, apologies for not including enough background in the commit message.

On the S32N79, the M-PHY firmware can be brought up in one of three 
ways, selected through the SoC's SCM MPHY_RAM_CONFIG_STATUS register:

1. running MPHY from internal FW ROM,
2. running MPHY from a (modified) FW ROM copy in external SRAM, or
3. running MPHY from a FW image in external SRAM.

`nxp,mphy-boot-mode` was introduced to select which of these boot 
sequences the controller driver programs. Today only option 1, "rom", is 
implemented and validated, but the property was meant to leave room for 
the two SRAM-based modes.

Given this setup, what would you suggest as the appropriate DT 
representation? I would greatly appreciate any guidance you can provide.

> Frank
>> +
>> +required:
>> +  - compatible
>> +  - reg
>> +
>> +allOf:
>> +  - $ref: ufs-common.yaml
>> +  - if:
>> +      required:
>> +        - nxp,mphy-boot-mode
>> +    then:
>> +      properties:
>> +        reg:
>> +          minItems: 2
>> +        reg-names:
>> +          minItems: 2
>> +      required:
>> +        - reg-names
>> +        - clocks
>> +        - clock-names
>> +    else:
>> +      properties:
>> +        reg:
>> +          maxItems: 1
>> +        reg-names:
>> +          maxItems: 1
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> +  - |
>> +    ufshc@4ac80000 {
>> +        compatible = "nxp,s32n79-ufshc", "jedec,ufs-3.0";
>> +        reg = <0x4ac80000 0x1000>, <0x4ac40000 0x1000>;
>> +        reg-names = "ufshc", "scm";
>> +        interrupt-parent = <&irqsteer_coss>;
>> +        interrupts = <211>;
>> +        clocks = <&sys_clk>;
>> +        clock-names = "core_clk";
>> +        nxp,mphy-boot-mode = "rom";
>> +    };
>> --
>> 2.43.0
>>
>>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver
  2026-08-26 15:57     ` Conor Dooley
@ 2026-08-27 14:37       ` Larisa Ileana Grigore
  0 siblings, 0 replies; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 14:37 UTC (permalink / raw)
  To: Conor Dooley, Frank Li
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On 8/26/2026 6:57 PM, Conor Dooley wrote:
> On Wed, Aug 26, 2026 at 09:35:01AM -0500, Frank Li wrote:
>> On Wed, Aug 26, 2026 at 03:40:44PM +0200, Larisa Grigore wrote:
>>> [You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>>>
>>> Document the device tree binding for the NXP S32N79 DWC-based UFS host
>>> controller which is compliant with JESD-223D "Universal Flash Storage Host
>>> Controller Interface" v3.0.
>>>
>>> The controller requires access to the SCM register block and the core
>>> clock rate when M-PHY initialization is performed. Add the optional
>>> nxp,mphy-boot-mode property to select the M-PHY firmware execution
>>> environment.
>>> Currently, the M-PHY firmware can only run from ROM. If the property is
>>> omitted, M-PHY initialization is assumed to be handled externally.
>>>
>>> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
>>> ---
>>>   .../bindings/ufs/nxp,s32n7-ufshc.yaml         | 99 +++++++++++++++++++
>>>   1 file changed, 99 insertions(+)
>>>   create mode 100644 Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>>>
>>> diff --git a/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>>> new file mode 100644
>>> index 000000000000..d9d5669be313
>>> --- /dev/null
>>> +++ b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>>> @@ -0,0 +1,99 @@
>>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>>> +%YAML 1.2
>>> +---
>>> +$id: http://devicetree.org/schemas/ufs/nxp,s32n7-ufshc.yaml#
>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>> +
>>> +title: NXP S32N79 Universal Flash Storage (UFS) Controller
>>> +
>>> +maintainers:
>>> +  - Larisa Grigore <larisa.grigore@oss.nxp.com>
>>> +
>>> +# Select only our matches, not all jedec,ufs
>>> +select:
>>> +  properties:
>>> +    compatible:
>>> +      contains:
>>> +        const: nxp,s32n79-ufshc
>>> +  required:
>>> +    - compatible
>>> +
>>> +properties:
>>> +  compatible:
>>> +    items:
>>> +      - const: nxp,s32n79-ufshc
>>> +      - const: jedec,ufs-3.0
>>
>> Any place actually use this fallback?
> 
> I don't think that this exists anywhere already:
> https://lore.kernel.org/all/?q=%22jedec%2Cufs-3.0%22
> $ git checkout korg-next/master
> Previous HEAD position was 2697ef8943c9 Add linux-next specific files for 20260813
> HEAD is now at f82a5da2f049 Add linux-next specific files for 20260826
> $ rg jedec,ufs-3.0
> $
> 
> So not entirely sure what the point of this actually is, if the driver
> doesn't use the fallback.
> 
> pw-bot: changes-requested
>
Thank you! Will address it in v2.

Regard,
Larisa

> Thanks,
> Conor.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver
  2026-08-26 15:49   ` Frank Li
@ 2026-08-27 14:44     ` Larisa Ileana Grigore
  0 siblings, 0 replies; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 14:44 UTC (permalink / raw)
  To: Frank Li
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On 8/26/2026 6:49 PM, Frank Li wrote:
> On Wed, Aug 26, 2026 at 03:40:45PM +0200, Larisa Grigore wrote:
>>
>> Add support for the UFS host controller on the NXP S32N79 SoC, built on
>> the Synopsys DesignWare (DWC) UFS architecture, using the UFSHCD DWC and
>> UFSHCD platform glue.
>>
>> This controller requires S32N79-specific initialization prior to
>> UIC_LINKSTARTUP, including:
>>    - programming SCM.ONE_US_TICK and HC.HCLKDIV based on the core clock,
>>    - applying the vendor-defined M-PHY calibration sequence, and
>>    - performing post-link calibration steps needed for HS operation.
>>
>> The M-PHY boot mode is selected via the 'nxp,mphy-boot-mode' device tree
>> property. If the property is omitted, M-PHY initialization is assumed to
>> be handled by external firmware or by a simulation environment.
> 
> Is "simulation environment" productionable?

Thank you! I will remove any references to the simulation environment in v2.

> 
>>
>> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
>> ---
>>   drivers/ufs/host/Kconfig         |  12 +
>>   drivers/ufs/host/Makefile        |   1 +
>>   drivers/ufs/host/ufs-nxp-s32n7.c | 464 +++++++++++++++++++++++++++++++
>>   drivers/ufs/host/ufshcd-dwc.h    |   9 +-
>>   4 files changed, 484 insertions(+), 2 deletions(-)
>>   create mode 100644 drivers/ufs/host/ufs-nxp-s32n7.c
>>
>> diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig
>> index ff170c0b6da0..e8ee7dd05f1a 100644
>> --- a/drivers/ufs/host/Kconfig
>> +++ b/drivers/ufs/host/Kconfig
>> @@ -168,3 +168,15 @@ config SCSI_UFS_AMD_VERSAL2
>>
>>            Select this if you have UFS controller on AMD Versal Gen 2 SoC.
>>            If unsure, say N.
>> +
>> +config SCSI_UFS_S32N7
>> +       tristate "NXP S32N7 platform driver"
>> +       depends on SCSI_UFSHCD_PLATFORM && (ARCH_S32 || COMPILE_TEST)
>> +       help
>> +         This selects the S32N7 specific additions on top of the UFSHCD DWC
>> +         and UFSHCD platform driver. UFS host on S32N79 needs some vendor
>> +         specific configurations like PHY and vendor specific register accesses
>> +         before accessing the hardware.
>> +
>> +         Select this if you have UFS controller on an S32N7 based board.
>> +         If unsure, say N.
>> diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile
>> index 65d8bb23ab7b..0381750a35d3 100644
>> --- a/drivers/ufs/host/Makefile
>> +++ b/drivers/ufs/host/Makefile
>> @@ -2,6 +2,7 @@
>>
>>   obj-$(CONFIG_SCSI_UFS_DWC_TC_PCI) += tc-dwc-g210-pci.o ufshcd-dwc.o tc-dwc-g210.o
>>   obj-$(CONFIG_SCSI_UFS_DWC_TC_PLATFORM) += tc-dwc-g210-pltfrm.o ufshcd-dwc.o tc-dwc-g210.o
>> +obj-$(CONFIG_SCSI_UFS_S32N7) += ufs-nxp-s32n7.o ufshcd-dwc.o
> 
> It is not related with your patch, but multi ufshcd-dwc.o may link many
> times if other ufs host driver also build, modern link can remove duplicate
> symbal, but not sure what happen if one build as module, one buildin.
> 
>>   obj-$(CONFIG_SCSI_UFS_CDNS_PLATFORM) += cdns-pltfrm.o
>>   obj-$(CONFIG_SCSI_UFS_QCOM) += ufs-qcom.o
>>   obj-$(CONFIG_SCSI_UFS_EXYNOS) += ufs-exynos.o
>> diff --git a/drivers/ufs/host/ufs-nxp-s32n7.c b/drivers/ufs/host/ufs-nxp-s32n7.c
>> new file mode 100644
>> index 000000000000..8a125105536b
>> --- /dev/null
>> +++ b/drivers/ufs/host/ufs-nxp-s32n7.c
>> @@ -0,0 +1,464 @@
>> +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-3-Clause)
>> +/*
>> + * Copyright 2026 NXP
>> + *
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/of.h>
>> +#include <linux/platform_device.h>
>> +
>> +#include "ufshcd-pltfrm.h"
>> +#include "ufshcd-dwc.h"
>> +#include "ufshci-dwc.h"
>> +
>> +/* SCM Register Offsets. */
>> +#define SCM_ONE_US_TICK                        0x2CU
>> +#define SCM_STATUS                     0x30
>> +
>> +/* SCM_ONE_US_TICK Register. */
>> +#define SCM_ONE_US_TICK_MASK           GENMASK(8, 0)
>> +
>> +/* SCM_MPHY_RAM_CONFIG_STATUS Register Fields. */
>> +#define SCM_STATUS_PHY_RESET_MASK              BIT(0)
>> +#define SCM_STATUS_SRAM_BYPASS_MASK            BIT(1)
>> +#define SCM_STATUS_SRAM_INIT_DONE_MASK         BIT(16)
>> +
>> +#define SCM_STATUS_PHY_RESET(val)              \
>> +       FIELD_PREP(SCM_STATUS_PHY_RESET_MASK, val)
>> +
>> +#define SCM_STATUS_SRAM_BYPASS(val)            \
>> +       FIELD_PREP(SCM_STATUS_SRAM_BYPASS_MASK, val)
>> +
>> +/* Timeout values. */
>> +#define CFG_MPHY_INIT_TIMEOUT_VALUE_US 3000000
>> +
>> +/* Clock validation limits. */
>> +#define MAX_VALID_ONE_US_TICK          SCM_ONE_US_TICK_MASK
>> +
>> +/* CPort definitions */
>> +#define CPORT_0                                0
>> +
>> +/* Hibern8 state poll timeout. */
>> +#define HBRN8_POLL_TOUT_MS      1000
> 
> Does this value is spec defined or just engineering choose?

This is engineering choose. I will update the commit message. Thanks!

>> +
>> +/**
>> + * enum mphy_boot_mode - MPHY boot mode options
>> + * @MPHY_BOOT_NONE: Skip MPHY initialization
>> + * @MPHY_BOOT_ROM: Running MPHY from internal FW ROM
>> + */
>> +enum mphy_boot_mode {
>> +       MPHY_BOOT_NONE = 0,
>> +       MPHY_BOOT_ROM = 1,
>> +};
>> +
>> +/**
>> + * struct s32n_ufs - S32N7 UFS host controller data
>> + * @hba: UFS host controller instance
>> + * @reg_scm: SCM register base address
>> + * @core_clk: core reference clock used to derive ONE_US_TICK/HCLKDIV
>> + * @mphy_boot_mode: MPHY boot mode configuration
>> + */
>> +struct s32n_ufs {
>> +       struct ufs_hba *hba;
>> +       void __iomem *reg_scm;
>> +       struct clk *core_clk;
>> +       enum mphy_boot_mode mphy_boot_mode;
>> +};
>> +
>> +/**
>> + * struct phy_reg_cfg - PHY register configuration entry
>> + * @reg: Register offset/address
>> + * @val: Value to write
>> + */
>> +struct phy_reg_cfg {
>> +       u32 reg;
>> +       u32 val;
>> +};
>> +
>> +/**
>> + * ufs_s32n_phy_write_sequence - Write a sequence of PHY registers
>> + * @hba: UFS host controller instance
>> + * @cfg: Array of register configurations
>> + * @count: Number of entries in the array
>> + *
>> + * Return: 0 on success, error code on failure
>> + */
>> +static int ufs_s32n_phy_write_sequence(struct ufs_hba *hba,
>> +                                      const struct phy_reg_cfg *cfg,
>> +                                      size_t count)
>> +{
>> +       int ret;
>> +       size_t i;
>> +
>> +       for (i = 0; i < count; i++) {
>> +               ret = ufshcd_dwc_phy_reg_write(hba, cfg[i].reg, cfg[i].val);
>> +               if (ret) {
>> +                       dev_err(hba->dev,
>> +                               "Failed to write PHY reg 0x%x = 0x%x (step %zu)\n",
>> +                               cfg[i].reg, cfg[i].val, i);
>> +                       return ret;
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static unsigned long ufs_s32n_calculate_us_tick(unsigned long clk_rate)
>> +{
>> +       return clk_rate / USEC_PER_SEC;
>> +}
>> +
>> +static int ufs_s32n_configure_clocks(struct ufs_hba *hba)
>> +{
>> +       struct s32n_ufs *ufs = ufshcd_get_variant(hba);
>> +       struct device *dev = hba->dev;
>> +       unsigned long clk_rate = 0;
>> +       unsigned long one_us_tick;
>> +
>> +       if (!ufs->core_clk) {
> 
> suppose return failure at probe if no core_clk, needn't check here

You are correct. Thank you!

> 
>> +               dev_err(dev, "Invalid core_clk.\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       clk_rate = clk_get_rate(ufs->core_clk);
>> +       if (!clk_rate) {
>> +               dev_err(dev, "Failed to get valid clock rate.\n");
>> +               return -EINVAL;
>> +       }
>> +
>> +       one_us_tick = ufs_s32n_calculate_us_tick(clk_rate);
>> +       if (one_us_tick == 0 || one_us_tick > MAX_VALID_ONE_US_TICK) {
>> +               dev_err(dev, "Invalid one_us_tick value: %lu (clk_rate: %lu Hz).\n",
>> +                       one_us_tick, clk_rate);
>> +               return -EINVAL;
>> +       }
>> +
>> +       dev_dbg(dev, "Core clock rate: %lu Hz, one_us_tick = %lu.\n",
>> +                clk_rate, one_us_tick);
>> +
>> +       /*
>> +        * Configure the micro-second tick rate generator based on core
>> +        * clock rate.
>> +        */
>> +       writel(one_us_tick, ufs->reg_scm + SCM_ONE_US_TICK);
>> +       ufshcd_dwc_program_clk_div(hba, one_us_tick);
>> +
>> +       return 0;
>> +}
>> +
>> +static int ufs_s32n_phy_initial_calib(struct ufs_hba *hba)
>> +{
>> +       static const struct phy_reg_cfg initial_calib[] = {
>> +               { FAST_FLAGS(0), 0x6 },
>> +               { FAST_FLAGS(1), 0x6 },
>> +               { RX_DAC_CTRL_OVRD(0), 0x1 },
>> +               { RX_DAC_CTRL_OVRD(1), 0x1 },
>> +               { RX_DAC_CTRL(0), 0x8E },
>> +               { RX_DAC_CTRL(1), 0x91 },
>> +               { RX_DAC_CTRL_SEL(0), 0x1 },
>> +               { RX_DAC_CTRL_SEL(1), 0x1 },
>> +               { RX_DAC_CTRL_EN(0), 0x1 },
>> +               { RX_DAC_CTRL_EN(1), 0x1 },
>> +               { RX_DAC_CTRL_OVRD(0), 0x0 },
>> +               { RX_DAC_CTRL_OVRD(1), 0x0 },
>> +               { RX_DAC_CTRL_OVRD(0), 0x1 },
>> +               { RX_DAC_CTRL_OVRD(1), 0x1 },
>> +               { RX_DAC_CTRL(0), 0x71 },
>> +               { RX_DAC_CTRL(1), 0x7F },
>> +               { RX_DAC_CTRL_SEL(0), 0x2 },
>> +               { RX_DAC_CTRL_SEL(1), 0x2 },
>> +               { RX_DAC_CTRL_EN(0), 0x1 },
>> +               { RX_DAC_CTRL_EN(1), 0x1 },
>> +               { RX_DAC_CTRL_OVRD(0), 0x0 },
>> +               { RX_DAC_CTRL_OVRD(1), 0x0 },
>> +               { FW_CALIB_CCFG(0), 0x100 },
>> +               { FW_CALIB_CCFG(1), 0x100 },
>> +       };
> 
> Can you put this data to platform data in case difference data for differe
> soc in future?

Thanks for the sugestion! I will add both initial_calib and post_calib.

> 
>> +       int ret;
>> +
>> +       /* Clock Control */
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBREFCLKCTRL2, 0), 0x80);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* Initial calibration sequence */
>> +       return ufs_s32n_phy_write_sequence(hba, initial_calib,
>> +                                          ARRAY_SIZE(initial_calib));
>> +}
>> +
>> +static int ufs_s32n_link_startup_pre_change(struct ufs_hba *hba)
>> +{
>> +       struct s32n_ufs *ufs = ufshcd_get_variant(hba);
>> +       struct device *dev = hba->dev;
>> +       int ret;
>> +       u32 reg;
>> +
>> +       /*
>> +        * There are two supported MPHY boot options implemented:
>> +        * - Running MPHY from internal FW ROM:
>> +        *  MPHY_RAM_CONFIG_STATUS.SRAM_BYPASS = 1
>> +        *  MPHY_RAM_CONFIG_STATUS.SRAM_EXT_LD_DONE = 0
>> +        * - Skip M-PHY initialization when it is handled externally.
>> +        */
>> +       if (ufs->mphy_boot_mode == MPHY_BOOT_NONE) {
>> +               dev_dbg(dev, "Skipping UFS MPHY init.\n");
>> +               return 0;
>> +       }
>> +
>> +       ret = ufs_s32n_configure_clocks(hba);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* Reset SCM.MPHY_RAM_CONFIG_STATUS to default value; keep MPHY in reset. */
>> +       writel(SCM_STATUS_PHY_RESET(1), ufs->reg_scm + SCM_STATUS);
>> +       if (ufs->mphy_boot_mode == MPHY_BOOT_ROM) {
>> +               /* ROM Mode. */
>> +               writel(SCM_STATUS_PHY_RESET(1) | SCM_STATUS_SRAM_BYPASS(1),
>> +                      ufs->reg_scm + SCM_STATUS);
>> +       }
>> +
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBCRCTRL, 0), 0x1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* Release PHY_RESET. */
>> +       writel(readl(ufs->reg_scm + SCM_STATUS) & ~SCM_STATUS_PHY_RESET_MASK,
>> +              ufs->reg_scm + SCM_STATUS);
>> +
>> +       /* Wait until SRAM_INIT_DONE = 1. */
>> +       ret = readl_poll_timeout(ufs->reg_scm + SCM_STATUS, reg,
>> +                       reg & SCM_STATUS_SRAM_INIT_DONE_MASK,
>> +                       1000, CFG_MPHY_INIT_TIMEOUT_VALUE_US);
>> +       if (ret) {
>> +               dev_err(dev, "UFS MPHY init not done!\n");
>> +               return ret;
>> +       }
>> +
>> +       /* Start of initial calibration */
>> +       ret = ufs_s32n_phy_initial_calib(hba);
>> +       if (ret) {
>> +               dev_err(dev, "UFS MPHY initial calibration failed!\n");
>> +               return ret;
>> +       }
>> +
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYDISABLE, 0), 0x0);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* Set Local DeviceID. */
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(N_DEVICEID, 0), 0x0);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* End of Gear1 settings */
>> +
>> +       return ufshcd_check_hibern8(hba, hba->lanes_per_direction,
>> +                                   HBRN8_POLL_TOUT_MS);
>> +}
>> +
>> +static int ufs_s32n_link_startup_post_change(struct ufs_hba *hba)
>> +{
>> +       static const struct ufshcd_dme_attr_val cport_setup[] = {
>> +               { UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0),
>> +                               CPORT_IDLE, DME_LOCAL },
>> +               { UIC_ARG_MIB_SEL(T_CPORTFLAGS, CPORT_0),
>> +                               CPORT_DEF_FLAGS, DME_LOCAL },
>> +               { UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0),
>> +                               CPORT_CONNECTED, DME_LOCAL },
>> +       };
>> +       static const struct phy_reg_cfg post_calib[] = {
>> +               { RX_OVRD_IN_1(0), 0xc },
>> +               { RX_OVRD_IN_1(1), 0xc },
>> +               { RX_OVRD_IN_1(0), 0x8 },
>> +               { RX_OVRD_IN_1(1), 0x8 },
>> +               { RX_OVRD_IN_1(0), 0x0 },
>> +               { RX_OVRD_IN_1(1), 0x0 },
>> +       };
>> +       struct s32n_ufs *ufs = ufshcd_get_variant(hba);
>> +       unsigned int data = 0;
>> +       int ret;
>> +
>> +       if (ufs->mphy_boot_mode == MPHY_BOOT_NONE)
>> +               return 0;
>> +
>> +       /* Set Connection State to IDLE (it allows CPort Attributes to be set). */
>> +       ret = ufshcd_dwc_dme_set_attrs(hba, cport_setup, ARRAY_SIZE(cport_setup));
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(T_CONNECTIONSTATE, CPORT_0), &data);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (data != CPORT_CONNECTED)
>> +               return -EIO;
>> +
>> +       /* Post Link Startup Calibration sequence */
>> +       ret = ufs_s32n_phy_write_sequence(hba, post_calib,
>> +                                          ARRAY_SIZE(post_calib));
>> +       if (ret)
>> +               return ret;
>> +
>> +       /*
>> +        * Performing MPHY configuration for rate change:
>> +        * CB rate selection: 0 - rate A, 1 - rate B;
>> +        */
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(CBRATESEL, 0), 0x1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       ret = ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VS_MPHYCFGUPDT, 0), 0x1);
>> +       if (ret)
>> +               return ret;
>> +
>> +       /* End of Gear4 calibration */
>> +       return 0;
>> +}
>> +
>> +static int ufs_s32n_link_startup_notify(struct ufs_hba *hba,
>> +                            enum ufs_notify_change_status status)
>> +{
>> +       int err;
>> +
>> +       if (status == PRE_CHANGE) {
>> +               err = ufs_s32n_link_startup_pre_change(hba);
>> +               if (err) {
>> +                       dev_err(hba->dev, "MPHY setup failed (%d).\n", err);
>> +                       return err;
>> +               }
>> +               return 0;
>> +       }
>> +
>> +       /* POST_CHANGE */
>> +       err = ufshcd_dwc_link_is_up(hba);
>> +       if (err) {
>> +               dev_err(hba->dev, "Link is not up.\n");
>> +               return err;
>> +       }
>> +
>> +       err = ufs_s32n_link_startup_post_change(hba);
>> +       if (err)
>> +               dev_err(hba->dev, "Connection setup failed (%d).\n", err);
>> +
>> +       return err;
>> +}
>> +
>> +static int ufs_s32n_init(struct ufs_hba *hba)
>> +{
>> +       struct device *dev = hba->dev;
>> +       struct platform_device *pdev = to_platform_device(dev);
>> +       struct device_node *np = dev->of_node;
>> +       const char *mphy_boot_mode;
>> +       struct s32n_ufs *ufs;
>> +       int ret;
>> +
>> +       ufs = devm_kzalloc(dev, sizeof(*ufs), GFP_KERNEL);
>> +       if (!ufs)
>> +               return -ENOMEM;
>> +
>> +       ufs->hba = hba;
>> +       ufs->mphy_boot_mode = MPHY_BOOT_NONE;
>> +
>> +       hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
>> +       hba->quirks |= UFSHCD_QUIRK_PERFORM_LINK_STARTUP_ONCE;
>> +       hba->spm_lvl = UFS_PM_LVL_5;
>> +
>> +       ret = of_property_read_string(np, "nxp,mphy-boot-mode", &mphy_boot_mode);
>> +       if (ret || !mphy_boot_mode) {
>> +               dev_dbg(dev,
>> +                       "nxp,mphy-boot-mode property not found. UFS MPHY init will be skipped.\n");
>> +               goto init_out;
>> +       }
>> +
>> +       if (!strcmp(mphy_boot_mode, "rom")) {
>> +               ufs->mphy_boot_mode = MPHY_BOOT_ROM;
>> +       } else {
>> +               return dev_err_probe(dev, -EINVAL,
>> +                                    "Unrecognized nxp,mphy-boot-mode property. UFS MPHY init will be skipped.\n");
>> +       }
>> +
>> +       ufs->reg_scm = devm_platform_ioremap_resource_byname(pdev, "scm");
>> +       if (IS_ERR(ufs->reg_scm))
>> +               return dev_err_probe(dev, PTR_ERR(ufs->reg_scm),
>> +                                    "ioremap failed for SCM registers.\n");
>> +
>> +       ufs->core_clk = devm_clk_get(dev, "core_clk");
>> +       if (IS_ERR(ufs->core_clk))
>> +               return dev_err_probe(dev, PTR_ERR(ufs->core_clk),
>> +                                    "Failed to get core clock.\n");
>> +
>> +init_out:
>> +       ufshcd_set_variant(hba, ufs);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct ufs_hba_variant_ops ufs_hba_s32n79_vops = {
>> +       .name = "s32n79",
>> +       .init = ufs_s32n_init,
>> +       .link_startup_notify = ufs_s32n_link_startup_notify,
>> +};
>> +
>> +static const struct of_device_id ufs_s32n7_match[] = {
>> +       {
>> +               .compatible = "nxp,s32n79-ufshc",
>> +               .data = &ufs_hba_s32n79_vops,
>> +       },
>> +       { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, ufs_s32n7_match);
>> +
>> +static int ufs_s32n7_probe(struct platform_device *pdev)
>> +{
>> +       const struct ufs_hba_variant_ops *vops;
>> +
>> +       vops = device_get_match_data(&pdev->dev);
>> +       if (!vops)
>> +               return -ENODEV;
>> +
>> +       return ufshcd_pltfrm_init(pdev, vops);
>> +}
>> +
>> +static void ufs_s32n7_remove(struct platform_device *pdev)
>> +{
>> +       ufshcd_pltfrm_remove(pdev);
>> +}
>> +
>> +static const struct dev_pm_ops ufs_s32n7_pm_ops = {
>> +       SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume)
>> +       SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL)
> 
> Use  SYSTEM_SLEEP_PM_OPS()
> 
>> +       .prepare        = ufshcd_suspend_prepare,
>> +       .complete       = ufshcd_resume_complete,
>> +};
>> +
>> +static struct platform_driver ufs_s32n7_driver = {
>> +       .probe = ufs_s32n7_probe,
>> +       .remove = ufs_s32n7_remove,
>> +       .driver = {
>> +               .name   = "ufs-s32n7",
>> +               .pm     = &ufs_s32n7_pm_ops,
> 
> pm_ptr(&ufs_s32n7_pm_ops)
> 
> Frank

I will address these comments in v2.

Best regards,
Larisa

> 
>> +               .of_match_table = ufs_s32n7_match,
>> +       },
>> +};
>> +
>> +module_platform_driver(ufs_s32n7_driver);
>> +
>> +MODULE_AUTHOR("Larisa Grigore <larisa.grigore@oss.nxp.com>");
>> +MODULE_DESCRIPTION("NXP S32N7 UFS Host Controller platform driver");
>> +MODULE_LICENSE("Dual BSD/GPL");
>> diff --git a/drivers/ufs/host/ufshcd-dwc.h b/drivers/ufs/host/ufshcd-dwc.h
>> index 53b523ef59df..747383cfb839 100644
>> --- a/drivers/ufs/host/ufshcd-dwc.h
>> +++ b/drivers/ufs/host/ufshcd-dwc.h
>> @@ -13,8 +13,7 @@
>>   #include <ufs/ufshcd.h>
>>
>>   /* RMMI Attributes */
>> -#define CBREFCLKCTRL2          0x8132
>> -#define CBCRCTRL               0x811F
>> +#define RXSQCONTROL            0x8009
>>   #define CBC10DIRECTCONF2       0x810E
>>   #define CBRATESEL              0x8114
>>   #define CBCREGADDRLSB          0x8116
>> @@ -24,10 +23,16 @@
>>   #define CBCREGRDLSB            0x811A
>>   #define CBCREGRDMSB            0x811B
>>   #define CBCREGRDWRSEL          0x811C
>> +#define CBCRCTRL               0x811F
>> +#define CBREFCLKCTRL2          0x8132
>>
>>   #define CBREFREFCLK_GATE_OVR_EN                BIT(7)
>>
>>   /* M-PHY registers */
>> +#define RX_DAC_CTRL(n)         (0x10AF + ((n) * 0x100))
>> +#define RX_DAC_CTRL_OVRD(n)    (0x10B0 + ((n) * 0x100))
>> +#define RX_DAC_CTRL_SEL(n)     (0x10B1 + ((n) * 0x100))
>> +#define RX_DAC_CTRL_EN(n)      (0x10B8 + ((n) * 0x100))
>>   #define RX_OVRD_IN_1(n)                (0x3006 + ((n) * 0x100))
>>   #define RX_PCS_OUT(n)          (0x300F + ((n) * 0x100))
>>   #define FAST_FLAGS(n)          (0x401C + ((n) * 0x100))
>> --
>> 2.43.0
>>
>>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver
  2026-08-27 14:36     ` Larisa Ileana Grigore
@ 2026-08-27 14:45       ` Frank Li
  2026-08-27 15:09         ` Larisa Ileana Grigore
  0 siblings, 1 reply; 35+ messages in thread
From: Frank Li @ 2026-08-27 14:45 UTC (permalink / raw)
  To: Larisa Ileana Grigore
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On Thu, Aug 27, 2026 at 05:36:27PM +0300, Larisa Ileana Grigore wrote:
> On 8/26/2026 5:35 PM, Frank Li wrote:
> > On Wed, Aug 26, 2026 at 03:40:44PM +0200, Larisa Grigore wrote:
> > > [You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
> > >
> > > Document the device tree binding for the NXP S32N79 DWC-based UFS host
> > > controller which is compliant with JESD-223D "Universal Flash Storage Host
> > > Controller Interface" v3.0.
> > >
> > > The controller requires access to the SCM register block and the core
> > > clock rate when M-PHY initialization is performed. Add the optional
> > > nxp,mphy-boot-mode property to select the M-PHY firmware execution
> > > environment.
> > > Currently, the M-PHY firmware can only run from ROM. If the property is
> > > omitted, M-PHY initialization is assumed to be handled externally.
> > >
> > > Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
> > > ---
> > >   .../bindings/ufs/nxp,s32n7-ufshc.yaml         | 99 +++++++++++++++++++
> > >   1 file changed, 99 insertions(+)
> > >   create mode 100644 Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
> > >
> > > diff --git a/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
> > > new file mode 100644
> > > index 000000000000..d9d5669be313
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
> > > @@ -0,0 +1,99 @@
> > > +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
> > > +%YAML 1.2
> > > +---
> > > +$id: http://devicetree.org/schemas/ufs/nxp,s32n7-ufshc.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: NXP S32N79 Universal Flash Storage (UFS) Controller
> > > +
> > > +maintainers:
> > > +  - Larisa Grigore <larisa.grigore@oss.nxp.com>
> > > +
> > > +# Select only our matches, not all jedec,ufs
> > > +select:
> > > +  properties:
> > > +    compatible:
> > > +      contains:
> > > +        const: nxp,s32n79-ufshc
> > > +  required:
> > > +    - compatible
> > > +
> > > +properties:
> > > +  compatible:
> > > +    items:
> > > +      - const: nxp,s32n79-ufshc
> > > +      - const: jedec,ufs-3.0
> >
> > Any place actually use this fallback?
>
> You're right Frank! I will switch to `jedec,ufs-2.0` in v2.
>
> >
> > > +
> > > +  reg:
> > > +    minItems: 1
> > > +    items:
> > > +      - description: UFS Host Controller registers
> > > +      - description: System Control Module registers
> > > +
> > > +  reg-names:
> > > +    description: |
> > > +      Names for the register regions. The first entry must be "ufshc".
> > > +      The second entry "scm" is mandatory only when nxp,mphy-boot-mode
> > > +      property is present.
> >
> > you already us if - else branch to restrict this limition, needn't
> > description here.
>
> Thank you! Will drop reg-names description in v2.
>
> > > +    minItems: 1
> > > +    items:
> > > +      - const: ufshc
> > > +      - const: scm
> > > +
> > > +  clocks:
> > > +    maxItems: 1
> > > +    description: UFS core clock
> > > +
> > > +  clock-names:
> > > +    items:
> > > +      - const: core_clk
> >
> > Needn't suffix _clk, just "core", generally needn't clock-names if only
> > one clocks.
>
> I will rename it to "core" in v2, if that works for you.
>
> > > +
> > > +  nxp,mphy-boot-mode:
> > > +    description: |
> > > +      MPHY initialization mode. Defines how the MPHY firmware should be
> > > +      initialized and executed during UFS controller startup.
> > > +      If this property is not specified, MPHY initialization will be skipped.
> > > +    $ref: /schemas/types.yaml#/definitions/string
> > > +    enum:
> > > +      - rom
> >
> > why not use standard "phy" property, if no "phy" means ROM init it.
>
> First, apologies for not including enough background in the commit message.
>
> On the S32N79, the M-PHY firmware can be brought up in one of three ways,
> selected through the SoC's SCM MPHY_RAM_CONFIG_STATUS register:
>
> 1. running MPHY from internal FW ROM,
> 2. running MPHY from a (modified) FW ROM copy in external SRAM, or
> 3. running MPHY from a FW image in external SRAM.
>
> `nxp,mphy-boot-mode` was introduced to select which of these boot sequences
> the controller driver programs. Today only option 1, "rom", is implemented
> and validated, but the property was meant to leave room for the two
> SRAM-based modes.


You need define all options in binding.  who load FW?

Frank

>
> Given this setup, what would you suggest as the appropriate DT
> representation? I would greatly appreciate any guidance you can provide.
>
> > Frank
> > > +
> > > +required:
> > > +  - compatible
> > > +  - reg
> > > +
> > > +allOf:
> > > +  - $ref: ufs-common.yaml
> > > +  - if:
> > > +      required:
> > > +        - nxp,mphy-boot-mode
> > > +    then:
> > > +      properties:
> > > +        reg:
> > > +          minItems: 2
> > > +        reg-names:
> > > +          minItems: 2
> > > +      required:
> > > +        - reg-names
> > > +        - clocks
> > > +        - clock-names
> > > +    else:
> > > +      properties:
> > > +        reg:
> > > +          maxItems: 1
> > > +        reg-names:
> > > +          maxItems: 1
> > > +
> > > +unevaluatedProperties: false
> > > +
> > > +examples:
> > > +  - |
> > > +    ufshc@4ac80000 {
> > > +        compatible = "nxp,s32n79-ufshc", "jedec,ufs-3.0";
> > > +        reg = <0x4ac80000 0x1000>, <0x4ac40000 0x1000>;
> > > +        reg-names = "ufshc", "scm";
> > > +        interrupt-parent = <&irqsteer_coss>;
> > > +        interrupts = <211>;
> > > +        clocks = <&sys_clk>;
> > > +        clock-names = "core_clk";
> > > +        nxp,mphy-boot-mode = "rom";
> > > +    };
> > > --
> > > 2.43.0
> > >
> > >
>

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper
  2026-08-26 13:40 ` [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper Larisa Grigore
                     ` (2 preceding siblings ...)
  2026-08-27  9:14   ` Bough Chen
@ 2026-08-27 15:00   ` Stanley Jhu
  3 siblings, 0 replies; 35+ messages in thread
From: Stanley Jhu @ 2026-08-27 15:00 UTC (permalink / raw)
  To: Larisa Grigore, NXP S32 Linux Team, Alim Akhtar, Avri Altman,
	Bart Van Assche, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude
  Cc: linux-scsi, devicetree, linux-kernel, imx, linux-arm-kernel,
	linux-arm-msm

On Wed, Aug 26, 2026 at 03:40:41PM +0200, Larisa Grigore wrote:
> [...]
> diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
> index c2dafb583cf5..034ee988924a 100644
> --- a/drivers/ufs/host/ufshcd-pltfrm.c
> +++ b/drivers/ufs/host/ufshcd-pltfrm.c
> @@ -563,6 +564,84 @@ void ufshcd_pltfrm_remove(struct platform_device *pdev)
>  }
>  EXPORT_SYMBOL_GPL(ufshcd_pltfrm_remove);
>
> +/**
> + * ufshcd_check_hibern8 - Check if all TX lanes entered Hibern8 state
> + * @hba: host controller instance
> + * @num_lanes: number of TX lanes to check
> + * @timeout_ms: timeout in milliseconds for all lanes
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int ufshcd_check_hibern8(struct ufs_hba *hba, unsigned int num_lanes,
> +                      unsigned int timeout_ms)

Hi Larisa,

Thanks for the great cleanup!

Would you consider moving this helper to drivers/ufs/core/ufshcd.c and
renaming it to ufshcd_dme_check_tx_hibern8()?

Since ufshcd-pltfrm.c primarily handles platform device glue and DT parsing,
polling M-PHY states via DME commands would fit more naturally in the UFS
core. This also keeps layering clean and allows non-platform controllers to
reuse it.

Additionally, the ufshcd_dme_ prefix would align nicely with existing DME
conventions in ufshcd.c while avoiding potential confusion with
ufshcd_is_link_hibern8().

Thank you,
Stanley

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver
  2026-08-27 14:45       ` Frank Li
@ 2026-08-27 15:09         ` Larisa Ileana Grigore
  0 siblings, 0 replies; 35+ messages in thread
From: Larisa Ileana Grigore @ 2026-08-27 15:09 UTC (permalink / raw)
  To: Frank Li
  Cc: NXP S32 Linux Team, Alim Akhtar, Avri Altman, Bart Van Assche,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	James E . J . Bottomley, Martin K . Petersen,
	Sai Krishna Potthuri, Ajay Neeli, Manivannan Sadhasivam,
	Pedro Sousa, clizzi, aruizrui, eballetb, echanude, linux-scsi,
	devicetree, linux-kernel, imx, linux-arm-kernel, linux-arm-msm

On 8/27/2026 5:45 PM, Frank Li wrote:
> On Thu, Aug 27, 2026 at 05:36:27PM +0300, Larisa Ileana Grigore wrote:
>> On 8/26/2026 5:35 PM, Frank Li wrote:
>>> On Wed, Aug 26, 2026 at 03:40:44PM +0200, Larisa Grigore wrote:
>>>> [You don't often get email from larisa.grigore@oss.nxp.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>>>>
>>>> Document the device tree binding for the NXP S32N79 DWC-based UFS host
>>>> controller which is compliant with JESD-223D "Universal Flash Storage Host
>>>> Controller Interface" v3.0.
>>>>
>>>> The controller requires access to the SCM register block and the core
>>>> clock rate when M-PHY initialization is performed. Add the optional
>>>> nxp,mphy-boot-mode property to select the M-PHY firmware execution
>>>> environment.
>>>> Currently, the M-PHY firmware can only run from ROM. If the property is
>>>> omitted, M-PHY initialization is assumed to be handled externally.
>>>>
>>>> Signed-off-by: Larisa Grigore <larisa.grigore@oss.nxp.com>
>>>> ---
>>>>    .../bindings/ufs/nxp,s32n7-ufshc.yaml         | 99 +++++++++++++++++++
>>>>    1 file changed, 99 insertions(+)
>>>>    create mode 100644 Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>>>> new file mode 100644
>>>> index 000000000000..d9d5669be313
>>>> --- /dev/null
>>>> +++ b/Documentation/devicetree/bindings/ufs/nxp,s32n7-ufshc.yaml
>>>> @@ -0,0 +1,99 @@
>>>> +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
>>>> +%YAML 1.2
>>>> +---
>>>> +$id: http://devicetree.org/schemas/ufs/nxp,s32n7-ufshc.yaml#
>>>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>>>> +
>>>> +title: NXP S32N79 Universal Flash Storage (UFS) Controller
>>>> +
>>>> +maintainers:
>>>> +  - Larisa Grigore <larisa.grigore@oss.nxp.com>
>>>> +
>>>> +# Select only our matches, not all jedec,ufs
>>>> +select:
>>>> +  properties:
>>>> +    compatible:
>>>> +      contains:
>>>> +        const: nxp,s32n79-ufshc
>>>> +  required:
>>>> +    - compatible
>>>> +
>>>> +properties:
>>>> +  compatible:
>>>> +    items:
>>>> +      - const: nxp,s32n79-ufshc
>>>> +      - const: jedec,ufs-3.0
>>>
>>> Any place actually use this fallback?
>>
>> You're right Frank! I will switch to `jedec,ufs-2.0` in v2.
>>
>>>
>>>> +
>>>> +  reg:
>>>> +    minItems: 1
>>>> +    items:
>>>> +      - description: UFS Host Controller registers
>>>> +      - description: System Control Module registers
>>>> +
>>>> +  reg-names:
>>>> +    description: |
>>>> +      Names for the register regions. The first entry must be "ufshc".
>>>> +      The second entry "scm" is mandatory only when nxp,mphy-boot-mode
>>>> +      property is present.
>>>
>>> you already us if - else branch to restrict this limition, needn't
>>> description here.
>>
>> Thank you! Will drop reg-names description in v2.
>>
>>>> +    minItems: 1
>>>> +    items:
>>>> +      - const: ufshc
>>>> +      - const: scm
>>>> +
>>>> +  clocks:
>>>> +    maxItems: 1
>>>> +    description: UFS core clock
>>>> +
>>>> +  clock-names:
>>>> +    items:
>>>> +      - const: core_clk
>>>
>>> Needn't suffix _clk, just "core", generally needn't clock-names if only
>>> one clocks.
>>
>> I will rename it to "core" in v2, if that works for you.
>>
>>>> +
>>>> +  nxp,mphy-boot-mode:
>>>> +    description: |
>>>> +      MPHY initialization mode. Defines how the MPHY firmware should be
>>>> +      initialized and executed during UFS controller startup.
>>>> +      If this property is not specified, MPHY initialization will be skipped.
>>>> +    $ref: /schemas/types.yaml#/definitions/string
>>>> +    enum:
>>>> +      - rom
>>>
>>> why not use standard "phy" property, if no "phy" means ROM init it.
>>
>> First, apologies for not including enough background in the commit message.
>>
>> On the S32N79, the M-PHY firmware can be brought up in one of three ways,
>> selected through the SoC's SCM MPHY_RAM_CONFIG_STATUS register:
>>
>> 1. running MPHY from internal FW ROM,
>> 2. running MPHY from a (modified) FW ROM copy in external SRAM, or
>> 3. running MPHY from a FW image in external SRAM.
>>
>> `nxp,mphy-boot-mode` was introduced to select which of these boot sequences
>> the controller driver programs. Today only option 1, "rom", is implemented
>> and validated, but the property was meant to leave room for the two
>> SRAM-based modes.
> 
> 
> You need define all options in binding.  who load FW?
> 
> Frank
> 

No external firmware loader is needed for the first mode. In the second 
mode, the initial ROM-to-SRAM copy is performed by the M-PHY internal 
bootloader, while system software may update the SRAM contents 
afterwards. In the third mode, system software must provide and write 
the complete firmware image into SRAM before starting it.

The current Linux implementation only supports and validates the first 
mode. The SRAM-based flows, including the Linux-side firmware loading 
mechanism, are not implemented by this series.

I agree that defining only "rom" is incomplete if nxp,mphy-boot-mode is 
retained. I will reconsider the binding for v2, either by describing all 
three modes together with their firmware-loading requirements, or by 
dropping the property until the SRAM-based flows are implemented.

Best regards,
Larisa

>>
>> Given this setup, what would you suggest as the appropriate DT
>> representation? I would greatly appreciate any guidance you can provide.
>>
>>> Frank
>>>> +
>>>> +required:
>>>> +  - compatible
>>>> +  - reg
>>>> +
>>>> +allOf:
>>>> +  - $ref: ufs-common.yaml
>>>> +  - if:
>>>> +      required:
>>>> +        - nxp,mphy-boot-mode
>>>> +    then:
>>>> +      properties:
>>>> +        reg:
>>>> +          minItems: 2
>>>> +        reg-names:
>>>> +          minItems: 2
>>>> +      required:
>>>> +        - reg-names
>>>> +        - clocks
>>>> +        - clock-names
>>>> +    else:
>>>> +      properties:
>>>> +        reg:
>>>> +          maxItems: 1
>>>> +        reg-names:
>>>> +          maxItems: 1
>>>> +
>>>> +unevaluatedProperties: false
>>>> +
>>>> +examples:
>>>> +  - |
>>>> +    ufshc@4ac80000 {
>>>> +        compatible = "nxp,s32n79-ufshc", "jedec,ufs-3.0";
>>>> +        reg = <0x4ac80000 0x1000>, <0x4ac40000 0x1000>;
>>>> +        reg-names = "ufshc", "scm";
>>>> +        interrupt-parent = <&irqsteer_coss>;
>>>> +        interrupts = <211>;
>>>> +        clocks = <&sys_clk>;
>>>> +        clock-names = "core_clk";
>>>> +        nxp,mphy-boot-mode = "rom";
>>>> +    };
>>>> --
>>>> 2.43.0
>>>>
>>>>
>>


^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2026-08-27 15:10 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-26 13:40 [PATCH 00/10] Add S32N79RDB UFS support Larisa Grigore
2026-08-26 13:40 ` [PATCH 01/10] ufs: unipro: Move Tx/Rx FSM state definitions to unipro.h Larisa Grigore
2026-08-26 15:05   ` Frank Li
2026-08-27 13:59     ` Larisa Ileana Grigore
2026-08-26 13:40 ` [PATCH 02/10] ufs: unipro: Add TX/RX FSM state attributes Larisa Grigore
2026-08-26 15:09   ` Frank Li
2026-08-26 13:40 ` [PATCH 03/10] ufs: host: Add common Hibern8 TX FSM polling helper Larisa Grigore
2026-08-26 15:17   ` Frank Li
2026-08-26 15:33   ` Bart Van Assche
2026-08-27 14:03     ` Larisa Ileana Grigore
2026-08-27  9:14   ` Bough Chen
2026-08-27 14:10     ` Larisa Ileana Grigore
2026-08-27 15:00   ` Stanley Jhu
2026-08-26 13:40 ` [PATCH 04/10] scsi: ufs: Move Versal2 M-PHY CREG access helpers into ufshcd-dwc Larisa Grigore
2026-08-26 15:20   ` Frank Li
2026-08-27 14:11     ` Larisa Ileana Grigore
2026-08-26 15:36   ` Bart Van Assche
2026-08-27 14:13     ` Larisa Ileana Grigore
2026-08-27 10:47   ` Bough Chen
2026-08-26 13:40 ` [PATCH 05/10] scsi: ufs: dwc: Export common clock divider and link status helpers Larisa Grigore
2026-08-26 15:22   ` Frank Li
2026-08-26 13:40 ` [PATCH 06/10] dt-bindings: ufs: Add compatibles for S32N79 Host Controller platform driver Larisa Grigore
2026-08-26 14:35   ` Frank Li
2026-08-26 15:57     ` Conor Dooley
2026-08-27 14:37       ` Larisa Ileana Grigore
2026-08-27 14:36     ` Larisa Ileana Grigore
2026-08-27 14:45       ` Frank Li
2026-08-27 15:09         ` Larisa Ileana Grigore
2026-08-26 13:40 ` [PATCH 07/10] scsi: ufs: Add NXP S32N79 UFS host controller driver Larisa Grigore
2026-08-26 14:01   ` sashiko-bot
2026-08-26 15:49   ` Frank Li
2026-08-27 14:44     ` Larisa Ileana Grigore
2026-08-26 13:40 ` [PATCH 08/10] arm64: dts: freescale: s32n79: Add UFS host controller Larisa Grigore
2026-08-26 13:40 ` [PATCH 09/10] arm64: dts: freescale: s32n79-rdb: Enable UFS Larisa Grigore
2026-08-26 13:40 ` [PATCH 10/10] MAINTAINERS: Add NXP S32N7 UFS host controller entry Larisa Grigore

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox