Merge patch series "ufs: Add a PCI UFS controller support"

To quote the author:

This adds a PCI UFS controller support and enables the support on
QEMU RISC-V for testing.

Requiring QEMU v8.2+.
diff --git a/board/emulation/qemu-riscv/Kconfig b/board/emulation/qemu-riscv/Kconfig
index 108e9fd..2709c9c 100644
--- a/board/emulation/qemu-riscv/Kconfig
+++ b/board/emulation/qemu-riscv/Kconfig
@@ -81,5 +81,7 @@
 	imply USB_XHCI_PCI
 	imply USB_KEYBOARD
 	imply CMD_USB
+	imply UFS
+	imply UFS_PCI
 
 endif
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 4569c06..7418c20 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1520,7 +1520,7 @@
 	  Turndra tsi148 device. See the command help for full details.
 
 config CMD_UFS
-	bool "Enable UFS - Universal Flash Subsystem commands"
+	bool "ufs - Universal Flash Storage commands"
 	depends on UFS
 	help
 	  "This provides commands to initialise and configure universal flash
diff --git a/cmd/ufs.c b/cmd/ufs.c
index 282b414..536bd85 100644
--- a/cmd/ufs.c
+++ b/cmd/ufs.c
@@ -32,6 +32,6 @@
 }
 
 U_BOOT_CMD(ufs, 3, 1, do_ufs,
-	   "UFS  sub system",
+	   "UFS sub-system",
 	   "init [dev] - init UFS subsystem\n"
 );
diff --git a/doc/board/emulation/qemu-riscv.rst b/doc/board/emulation/qemu-riscv.rst
index 61137bc..8a5eb1e 100644
--- a/doc/board/emulation/qemu-riscv.rst
+++ b/doc/board/emulation/qemu-riscv.rst
@@ -131,7 +131,13 @@
     -drive if=none,file=riscv64.img,format=raw,id=mydisk \
     -device ide-hd,drive=mydisk,bus=ahci.0
 
-You will have to run 'scsi scan' to use it.
+or alternatively attach an emulated UFS::
+
+    -device ufs,id=ufs0 \
+    -drive if=none,file=test.img,format=raw,id=lun0 \
+    -device ufs-lu,drive=lun0,bus=ufs0
+
+You will have to run 'scsi scan' to use them.
 
 A video console can be emulated in RISC-V virt machine by removing "-nographic"
 and adding::
diff --git a/drivers/ufs/Kconfig b/drivers/ufs/Kconfig
index ee021c7..7da46fa 100644
--- a/drivers/ufs/Kconfig
+++ b/drivers/ufs/Kconfig
@@ -15,6 +15,17 @@
 	  This selects the platform driver for the Cadence UFS host
 	  controller present on present TI's J721e devices.
 
+config UFS_PCI
+	bool "PCI bus based UFS Controller support"
+	depends on PCI && UFS
+	help
+	  This selects the PCI UFS Host Controller Interface. Select this if
+	  you have UFS Host Controller with PCI Interface.
+
+	  If you have a controller with this interface, say Y here.
+
+	  If unsure, say N.
+
 config TI_J721E_UFS
 	bool "Glue Layer driver for UFS on TI J721E devices"
 	help
diff --git a/drivers/ufs/Makefile b/drivers/ufs/Makefile
index 56a4b07..67c4262 100644
--- a/drivers/ufs/Makefile
+++ b/drivers/ufs/Makefile
@@ -6,4 +6,5 @@
 obj-$(CONFIG_UFS) += ufs.o ufs-uclass.o
 obj-$(CONFIG_CADENCE_UFS) += cdns-platform.o
 obj-$(CONFIG_TI_J721E_UFS) += ti-j721e-ufs.o
+obj-$(CONFIG_UFS_PCI) += ufs-pci.o
 obj-$(CONFIG_UFS_RENESAS) += ufs-renesas.o
diff --git a/drivers/ufs/ufs-pci.c b/drivers/ufs/ufs-pci.c
new file mode 100644
index 0000000..ad41358
--- /dev/null
+++ b/drivers/ufs/ufs-pci.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 tinylab.org
+ * Author: Bin Meng <bmeng@tinylab.org>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <pci.h>
+#include <ufs.h>
+#include <dm/device_compat.h>
+#include "ufs.h"
+
+static int ufs_pci_bind(struct udevice *dev)
+{
+	struct udevice *scsi_dev;
+
+	return ufs_scsi_bind(dev, &scsi_dev);
+}
+
+static int ufs_pci_probe(struct udevice *dev)
+{
+	int err;
+
+	err = ufshcd_probe(dev, NULL);
+	if (err)
+		dev_err(dev, "%s failed (ret=%d)\n", __func__, err);
+
+	return err;
+}
+
+U_BOOT_DRIVER(ufs_pci) = {
+	.name	= "ufs_pci",
+	.id	= UCLASS_UFS,
+	.bind	= ufs_pci_bind,
+	.probe	= ufs_pci_probe,
+};
+
+static struct pci_device_id ufs_supported[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_UFS) },
+	{},
+};
+
+U_BOOT_PCI_DEVICE(ufs_pci, ufs_supported);
diff --git a/drivers/ufs/ufs-uclass.c b/drivers/ufs/ufs-uclass.c
index e6478a9..92fcdf4 100644
--- a/drivers/ufs/ufs-uclass.c
+++ b/drivers/ufs/ufs-uclass.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /**
- * ufs-uclass.c - Universal Flash Subsystem (UFS) Uclass driver
+ * ufs-uclass.c - Universal Flash Storage (UFS) Uclass driver
  *
  * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
  */
diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c
index 346f0fd..e4400f3 100644
--- a/drivers/ufs/ufs.c
+++ b/drivers/ufs/ufs.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0+
 /**
- * ufs.c - Universal Flash Subsystem (UFS) driver
+ * ufs.c - Universal Flash Storage (UFS) driver
  *
  * Taken from Linux Kernel v5.2 (drivers/scsi/ufs/ufshcd.c) and ported
  * to u-boot.
@@ -320,7 +320,7 @@
 					UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)),
 					0);
 		if (err) {
-			dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d",
+			dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d\n",
 				__func__, peer, i, err);
 			break;
 		}
@@ -441,7 +441,7 @@
 		ufshcd_enable_run_stop_reg(hba);
 	} else {
 		dev_err(hba->dev,
-			"Host controller not ready to process requests");
+			"Host controller not ready to process requests\n");
 		err = -EIO;
 		goto out;
 	}
@@ -930,7 +930,7 @@
 			memcpy(hba->dev_cmd.query.descriptor, descp, resp_len);
 		} else {
 			dev_warn(hba->dev,
-				 "%s: Response size is bigger than buffer",
+				 "%s: Response size is bigger than buffer\n",
 				 __func__);
 			return -EINVAL;
 		}
@@ -1179,11 +1179,11 @@
 					    &header_len);
 
 	if (ret) {
-		dev_err(hba->dev, "%s: Failed to get descriptor header id %d",
+		dev_err(hba->dev, "%s: Failed to get descriptor header id %d\n",
 			__func__, desc_id);
 		return ret;
 	} else if (desc_id != header[QUERY_DESC_DESC_TYPE_OFFSET]) {
-		dev_warn(hba->dev, "%s: descriptor header id %d and desc_id %d mismatch",
+		dev_warn(hba->dev, "%s: descriptor header id %d and desc_id %d mismatch\n",
 			 __func__, header[QUERY_DESC_DESC_TYPE_OFFSET],
 			 desc_id);
 		ret = -EINVAL;
@@ -1302,7 +1302,7 @@
 
 	/* Sanity checks */
 	if (ret || !buff_len) {
-		dev_err(hba->dev, "%s: Failed to get full descriptor length",
+		dev_err(hba->dev, "%s: Failed to get full descriptor length\n",
 			__func__);
 		return ret;
 	}
@@ -1323,14 +1323,14 @@
 					    &buff_len);
 
 	if (ret) {
-		dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d",
+		dev_err(hba->dev, "%s: Failed reading descriptor. desc_id %d, desc_index %d, param_offset %d, ret %d\n",
 			__func__, desc_id, desc_index, param_offset, ret);
 		goto out;
 	}
 
 	/* Sanity check */
 	if (desc_buf[QUERY_DESC_DESC_TYPE_OFFSET] != desc_id) {
-		dev_err(hba->dev, "%s: invalid desc_id %d in descriptor header",
+		dev_err(hba->dev, "%s: invalid desc_id %d in descriptor header\n",
 			__func__, desc_buf[QUERY_DESC_DESC_TYPE_OFFSET]);
 		ret = -EINVAL;
 		goto out;
@@ -1914,6 +1914,7 @@
 	struct ufs_hba *hba = dev_get_uclass_priv(ufs_dev);
 	struct scsi_plat *scsi_plat;
 	struct udevice *scsi_dev;
+	void __iomem *mmio_base;
 	int err;
 
 	device_find_first_child(ufs_dev, &scsi_dev);
@@ -1927,7 +1928,14 @@
 
 	hba->dev = ufs_dev;
 	hba->ops = hba_ops;
-	hba->mmio_base = dev_read_addr_ptr(ufs_dev);
+
+	if (device_is_on_pci_bus(ufs_dev)) {
+		mmio_base = dm_pci_map_bar(ufs_dev, PCI_BASE_ADDRESS_0, 0, 0,
+					   PCI_REGION_TYPE, PCI_REGION_MEM);
+	} else {
+		mmio_base = dev_read_addr_ptr(ufs_dev);
+	}
+	hba->mmio_base = mmio_base;
 
 	/* Set descriptor lengths to specification defaults */
 	ufshcd_def_desc_sizes(hba);
@@ -1945,7 +1953,8 @@
 	    hba->version != UFSHCI_VERSION_11 &&
 	    hba->version != UFSHCI_VERSION_20 &&
 	    hba->version != UFSHCI_VERSION_21 &&
-	    hba->version != UFSHCI_VERSION_30)
+	    hba->version != UFSHCI_VERSION_30 &&
+	    hba->version != UFSHCI_VERSION_31)
 		dev_err(hba->dev, "invalid UFS version 0x%x\n",
 			hba->version);
 
diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h
index 9daaf03..816a5ce 100644
--- a/drivers/ufs/ufs.h
+++ b/drivers/ufs/ufs.h
@@ -782,6 +782,7 @@
 	UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */
 	UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */
 	UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */
+	UFSHCI_VERSION_31 = 0x00000310, /* 3.1 */
 };
 
 /* Interrupt disable masks */
diff --git a/include/pci_ids.h b/include/pci_ids.h
index 88b0a64..b63bf45 100644
--- a/include/pci_ids.h
+++ b/include/pci_ids.h
@@ -1363,6 +1363,13 @@
 #define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP 0x3fc5
 #define PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI 0x3fc6
 
+/* Per https://www.qemu.org/docs/master/specs/pci-ids.html */
+#define PCI_VENDOR_ID_REDHAT            0x1b36
+#define PCI_DEVICE_ID_REDHAT_SDHCI      0x0007
+#define PCI_DEVICE_ID_REDHAT_XHCI       0x000d
+#define PCI_DEVICE_ID_REDHAT_NVME       0x0010
+#define PCI_DEVICE_ID_REDHAT_UFS        0x0013
+
 #define PCI_VENDOR_ID_INIT		0x1101
 
 #define PCI_VENDOR_ID_CREATIVE		0x1102 /* duplicate: ECTIVA */