scsi: ufs: introduce the capability and quirk for interrupt aggregation
UFS HCI (Host Controller Interface) allows the transfer requests
interrupts to be aggregated to generate the single interrupt but
this can impact the performance. Hence introduce the capability which
gives choice to use the interrupt aggregation capability or not.
By default interrupt aggregation capability is kept disabled.
This change also introduces a quirk for broken interrupt aggregation
feature, as in some UFS controllers, this feature may not work.
Signed-off-by: Yaniv Gardi <ygardi@codeaurora.org>
Reviewed-by: Akinobu Mita <akinobu.mita@gmail.com>
Signed-off-by: James Bottomley <JBottomley@Odin.com>
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 648a446..9641bcb 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -481,6 +481,15 @@
}
/**
+ * ufshcd_disable_intr_aggr - Disables interrupt aggregation.
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_disable_intr_aggr(struct ufs_hba *hba)
+{
+ ufshcd_writel(hba, 0, REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL);
+}
+
+/**
* ufshcd_enable_run_stop_reg - Enable run-stop registers,
* When run-stop registers are set to 1, it indicates the
* host controller that it can process the requests
@@ -1326,7 +1335,7 @@
lrbp->sense_buffer = cmd->sense_buffer;
lrbp->task_tag = tag;
lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
- lrbp->intr_cmd = false;
+ lrbp->intr_cmd = !ufshcd_is_intr_aggr_allowed(hba) ? true : false;
lrbp->command_type = UTP_CMD_TYPE_SCSI;
/* form UPIU before issuing the command */
@@ -2522,7 +2531,10 @@
ufshcd_enable_intr(hba, UFSHCD_ENABLE_INTRS);
/* Configure interrupt aggregation */
- ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
+ if (ufshcd_is_intr_aggr_allowed(hba))
+ ufshcd_config_intr_aggr(hba, hba->nutrs - 1, INT_AGGR_DEF_TO);
+ else
+ ufshcd_disable_intr_aggr(hba);
/* Configure UTRL and UTMRL base address registers */
ufshcd_writel(hba, lower_32_bits(hba->utrdl_dma_addr),
@@ -3073,7 +3085,8 @@
* false interrupt if device completes another request after resetting
* aggregation and before reading the DB.
*/
- ufshcd_reset_intr_aggr(hba);
+ if (ufshcd_is_intr_aggr_allowed(hba))
+ ufshcd_reset_intr_aggr(hba);
tr_doorbell = ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL);
completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index b47ff07..fc8bec9 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -417,11 +417,15 @@
unsigned int irq;
bool is_irq_enabled;
+ /* Interrupt aggregation support is broken */
+ #define UFSHCD_QUIRK_BROKEN_INTR_AGGR UFS_BIT(0)
+
/*
* delay before each dme command is required as the unipro
* layer has shown instabilities
*/
- #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS UFS_BIT(0)
+ #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS UFS_BIT(1)
+
unsigned int quirks; /* Deviations from standard UFSHCI spec. */
@@ -478,6 +482,12 @@
#define UFSHCD_CAP_CLK_SCALING (1 << 2)
/* Allow auto bkops to enabled during runtime suspend */
#define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3)
+ /*
+ * This capability allows host controller driver to use the UFS HCI's
+ * interrupt aggregation capability.
+ * CAUTION: Enabling this might reduce overall UFS throughput.
+ */
+#define UFSHCD_CAP_INTR_AGGR (1 << 4)
struct devfreq *devfreq;
struct ufs_clk_scaling clk_scaling;
@@ -502,6 +512,15 @@
return hba->caps & UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
}
+static inline bool ufshcd_is_intr_aggr_allowed(struct ufs_hba *hba)
+{
+ if ((hba->caps & UFSHCD_CAP_INTR_AGGR) &&
+ !(hba->quirks & UFSHCD_QUIRK_BROKEN_INTR_AGGR))
+ return true;
+ else
+ return false;
+}
+
#define ufshcd_writel(hba, val, reg) \
writel((val), (hba)->mmio_base + (reg))
#define ufshcd_readl(hba, reg) \