diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
index 432df76e6318..57b0e8b14543 100644
--- a/drivers/scsi/ufs/Kconfig
+++ b/drivers/scsi/ufs/Kconfig
@@ -199,3 +199,12 @@ config SCSI_UFS_FAULT_INJECTION
help
Enable fault injection support in the UFS driver. This makes it easier
to test the UFS error handler and abort handler.
+
+config SCSI_UFS_FBO
+ bool "Support UFS File-based Optimization"
+ depends on SCSI_UFSHCD
+ help
+ The UFS FBO feature improves Sequential read performance. The Host can
+ send the LBA to device. The device will return a fragmented state. It
+ FBO_DESC_PARAM_MAX_LBA_RANGE_CONUT = 0xF,
+static void ufsfbo_scsi_unblock_requests(struct ufs_hba *hba)
+{
+ if (atomic_dec_and_test(&hba->scsi_block_reqs_cnt))
+ scsi_unblock_requests(hba->host);
+}
+
+static void ufsfbo_scsi_block_requests(struct ufs_hba *hba)
+{
+ if (atomic_inc_return(&hba->scsi_block_reqs_cnt) == 1)
+ scsi_block_requests(hba->host);
+}
+static int ufsfbo_wait_for_doorbell_clr(struct ufs_hba *hba,
+ u64 wait_timeout_us)
+{
+ unsigned long flags;
+ int ret = 0;
+ u32 tm_doorbell;
+ u32 tr_doorbell;
+ bool timeout = false, do_last_check = false;
+ ktime_t start;
+
+ ufshcd_hold(hba, false);
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ /*
+ * Wait for all the outstanding tasks/transfer requests.
+ * Verify by checking the doorbell registers are clear.
+ */
+ start = ktime_get();
+ do {
+ if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) {
+ ret = -EBUSY;
+ goto out;
+ }
+ tm_doorbell = ufshcd_readl(hba, REG_UTP_TASK_REQ_DOOR_BELL);
+ tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
+ if (!tm_doorbell && !tr_doorbell) {
+ timeout = false;
+ break;
+ } else if (do_last_check) {
+ break;
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ schedule();
+ if (ktime_to_us(ktime_sub(ktime_get(), start)) >
+ wait_timeout_us) {
+ timeout = true;
+ /*
+ * We might have scheduled out for long time so make
+ * sure to check if doorbells are cleared by this time
+ * or not.
+ */
+ do_last_check = true;
+ }
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ } while (tm_doorbell || tr_doorbell);
+ if (timeout) {
+ dev_err(hba->dev,
+ "%s: timedout waiting for doorbell to clear (tm=0x%x, tr=0x%x)\n",
+ __func__, tm_doorbell, tr_doorbell);
+ ret = -EBUSY;
+ }
+out:
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ ufshcd_release(hba);
+ return ret;
+}
+int ufsfbo_read_frag_level(struct ufsfbo_dev *fbo, char *buf)[ ... ]
+{
+ hba->host->eh_noresume = 1;
+int ufsfbo_init_lba_buffer(struct ufsfbo_dev *fbo, const char *buf, char *lba_buf)
+{
+ int ret = 0;
+ char *buf_ptr;
+ char *lba;
+ u64 lba_value_pre, lba_value_post;
+ int len_index = 1, lba_index = FBO_HEADER_SIZE + FBO_BODY_HEADER_SIZE;
+
+ buf_ptr = kzalloc(strlen(buf) + 1, GFP_KERNEL);
+ if (!buf_ptr) {
+ FBO_ERR_MSG("alloc buffer fail");
+ ret = -ENOMEM;
+ return ret;
+ }
+ memcpy(buf_ptr, buf, strlen(buf) + 1);
+ /*create lba range buf send for device*/
+ lba_buf[5] = fbo->fbo_lba_count; //lba range count
+ lba_buf[6] = fbo->fbo_wholefile; //calculate whole file
+ while ((lba = strsep(&buf_ptr, ",")) != NULL) {
+int ufsfbo_lba_list_write(struct ufsfbo_dev *fbo, const char *buf)
+{
+ hba->host->eh_noresume = 1;
+void ufsfbo_init(struct ufs_hba *hba)
+{
+ ret = ufsfbo_create_sysfs(fbo);
+ ufsfbo_set_state(hba, FBO_PRESENT);
+/* SYSFS DEFINE */
+#define define_sysfs_ro(_name) __ATTR(_name, 0444, \
+ ufsfbo_sysfs_show_##_name, NULL)
+#define define_sysfs_wo(_name) __ATTR(_name, 0200, \
+ NULL, ufsfbo_sysfs_store_##_name)
+#define define_sysfs_rw(_name) __ATTR(_name, 0644, \
+ ufsfbo_sysfs_show_##_name, \
+ ufsfbo_sysfs_store_##_name)
diff --git a/drivers/scsi/ufs/ufsfbo.h b/drivers/scsi/ufs/ufsfbo.h
new file mode 100644
index 000000000000..c2549032815e
--- /dev/null
+++ b/drivers/scsi/ufs/ufsfbo.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Universal Flash Storage File-based Optimization
+ *
+ * Copyright (C) 2022 Xiaomi Mobile Software Co., Ltd
+ *
+ * Authors:
+ * lijiaming <lijiaming3@xxxxxxxxxx>
+ */
+
+#ifndef _UFSFBO_H_
+#define _UFSFBO_H_
+#include <linux/interrupt.h>
+#include <linux/blktrace_api.h>
+#include <linux/blkdev.h>
+#include <linux/bitfield.h>
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 2f6468f22b48..9c377c514e17 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -4956,6 +4956,14 @@ static void ufshcd_hpb_configure(struct ufs_hba *hba, struct scsi_device *sdev)
ufshpb_init_hpb_lu(hba, sdev);
}
+static void ufshcd_fbo_configure(struct ufs_hba *hba, struct scsi_device *sdev)
+{
+#ifdef CONFIG_SCSI_UFS_FBO
+ if (sdev->lun == 0)
+ hba->fbo.sdev_ufs_lu = sdev;
+#endif
+}