aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
authorMark Brown <broonie@kernel.org>2026-05-29 23:13:47 +0100
committerMark Brown <broonie@kernel.org>2026-05-29 23:13:47 +0100
commitb5d6adf32744ef61b5ed065152baff796b55fd2c (patch)
treebc27109fcdf495bb9d6c94d6a1487e3aeb006bc1 /drivers
parent90aadaa2537394eaf9d6331ce201bb8894a5897b (diff)
parent1747233ee1af0b89d39995ad09506cbadd3d3e50 (diff)
downloadlinux-next-history-b5d6adf32744ef61b5ed065152baff796b55fd2c.tar.gz
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi.git
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/Kconfig1
-rw-r--r--drivers/scsi/Makefile4
-rw-r--r--drivers/scsi/advansys.c1
-rw-r--r--drivers/scsi/hisi_sas/hisi_sas_v3_hw.c12
-rw-r--r--drivers/scsi/isci/scu_task_context.h2
-rw-r--r--drivers/scsi/libiscsi.c4
-rw-r--r--drivers/scsi/mvsas/mv_sas.h6
-rw-r--r--drivers/scsi/ncr53c8xx.c2
-rw-r--r--drivers/scsi/ncr53c8xx.h4
-rw-r--r--drivers/scsi/pm8001/pm8001_ctl.c8
-rw-r--r--drivers/scsi/pm8001/pm80xx_hwi.c7
-rw-r--r--drivers/scsi/scsi_scan.c2
-rw-r--r--drivers/scsi/scsi_transport_srp.c10
-rw-r--r--drivers/scsi/snic/vnic_dev.c1
-rw-r--r--drivers/scsi/storvsc_drv.c8
-rw-r--r--drivers/ufs/core/ufs-sysfs.c30
-rw-r--r--drivers/ufs/core/ufs-txeq.c287
-rw-r--r--drivers/ufs/core/ufshcd-priv.h5
-rw-r--r--drivers/ufs/core/ufshcd.c164
-rw-r--r--drivers/ufs/host/Kconfig2
-rw-r--r--drivers/ufs/host/tc-dwc-g210-pci.c4
-rw-r--r--drivers/ufs/host/ufs-exynos.c110
-rw-r--r--drivers/ufs/host/ufs-qcom.c10
-rw-r--r--drivers/ufs/host/ufs-qcom.h11
-rw-r--r--drivers/ufs/host/ufshcd-pci.c29
25 files changed, 615 insertions, 109 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index fc8e8b0bfa391..c3042393af234 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -474,7 +474,6 @@ config SCSI_ADVANSYS
tristate "AdvanSys SCSI support"
depends on SCSI
depends on (ISA || EISA || PCI) && HAS_IOPORT
- depends on ISA_DMA_API || !ISA
help
This is a driver for all SCSI host adapters manufactured by
AdvanSys. It is documented in the kernel source in
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 16de3e41f94c4..842c254bb2269 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -177,8 +177,8 @@ sd_mod-$(CONFIG_BLK_DEV_ZONED) += sd_zbc.o
sr_mod-objs := sr.o sr_ioctl.o sr_vendor.o
ncr53c8xx-flags-$(CONFIG_SCSI_ZALON) \
- := -DCONFIG_NCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \
- -DCONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
+ := -DNCR53C8XX_PREFETCH -DSCSI_NCR_BIG_ENDIAN \
+ -DSCSI_NCR53C8XX_NO_WORD_TRANSFERS
CFLAGS_ncr53c8xx.o := $(ncr53c8xx-flags-y) $(ncr53c8xx-flags-m)
zalon7xx-objs := zalon.o ncr53c8xx.o
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index fcf059bf41e85..5cdbf2bdb13df 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -36,7 +36,6 @@
#include <linux/dmapool.h>
#include <asm/io.h>
-#include <asm/dma.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
index 14d563e82d208..213d5b5dea94f 100644
--- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
+++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c
@@ -2977,7 +2977,7 @@ static int sdev_configure_v3_hw(struct scsi_device *sdev,
return 0;
if (!device_link_add(&sdev->sdev_gendev, dev,
- DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) {
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)) {
if (pm_runtime_enabled(dev)) {
dev_info(dev, "add device link failed, disable runtime PM for the host\n");
pm_runtime_disable(dev);
@@ -2987,6 +2987,15 @@ static int sdev_configure_v3_hw(struct scsi_device *sdev,
return 0;
}
+static void hisi_sas_sdev_destroy(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = dev_to_shost(&sdev->sdev_gendev);
+ struct hisi_hba *hisi_hba = shost_priv(shost);
+ struct device *dev = hisi_hba->dev;
+
+ device_link_remove(&sdev->sdev_gendev, dev);
+}
+
static struct attribute *host_v3_hw_attrs[] = {
&dev_attr_phy_event_threshold.attr,
&dev_attr_intr_conv_v3_hw.attr,
@@ -3401,6 +3410,7 @@ static const struct scsi_host_template sht_v3_hw = {
.sg_tablesize = HISI_SAS_SGE_PAGE_CNT,
.sg_prot_tablesize = HISI_SAS_SGE_PAGE_CNT,
.sdev_init = hisi_sas_sdev_init,
+ .sdev_destroy = hisi_sas_sdev_destroy,
.shost_groups = host_v3_hw_groups,
.sdev_groups = sdev_groups_v3_hw,
.tag_alloc_policy_rr = true,
diff --git a/drivers/scsi/isci/scu_task_context.h b/drivers/scsi/isci/scu_task_context.h
index 582d22d54689d..e56cd0fdc35f1 100644
--- a/drivers/scsi/isci/scu_task_context.h
+++ b/drivers/scsi/isci/scu_task_context.h
@@ -211,8 +211,6 @@ typedef enum {
#define SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT 12
#define SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK 0x00007000
-#define scu_get_command_reqeust_logical_port(x) \
- ((x) & SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK)
#define MAKE_SCU_CONTEXT_COMMAND_TYPE(type) \
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 25857d6ed6e84..160f02f2f51d1 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -3012,7 +3012,7 @@ static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost)
* This can be used by software iscsi_transports that allocate
* a session per scsi host.
*
- * Callers should set cmds_max to the largest total numer (mgmt + scsi) of
+ * Callers should set cmds_max to the largest total number (mgmt + scsi) of
* tasks they support. The iscsi layer reserves ISCSI_MGMT_CMDS_MAX tasks
* for nop handling and login/logout requests.
*/
@@ -3307,7 +3307,7 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
if (conn->ping_timeout && !conn->recv_timeout) {
iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of "
- "zero. Using 5 seconds\n.");
+ "zero. Using 5 seconds.\n");
conn->recv_timeout = 5;
}
diff --git a/drivers/scsi/mvsas/mv_sas.h b/drivers/scsi/mvsas/mv_sas.h
index 09ce3f2241f24..7521f969aa87d 100644
--- a/drivers/scsi/mvsas/mv_sas.h
+++ b/drivers/scsi/mvsas/mv_sas.h
@@ -35,10 +35,10 @@
#define MVS_ID_NOT_MAPPED 0x7f
#define WIDE_PORT_MAX_PHY 4
#define mv_printk(fmt, arg ...) \
- printk(KERN_DEBUG"%s %d:" fmt, __FILE__, __LINE__, ## arg)
+ printk(KERN_DEBUG "%s: " fmt, __func__, ## arg)
#ifdef MV_DEBUG
-#define mv_dprintk(format, arg...) \
- printk(KERN_DEBUG"%s %d:" format, __FILE__, __LINE__, ## arg)
+#define mv_dprintk(fmt, arg...) \
+ mv_printk(fmt, ## arg)
#else
#define mv_dprintk(format, arg...) no_printk(format, ## arg)
#endif
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 4a255aafed806..5369ca3fe4fd3 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -1776,7 +1776,7 @@ struct ncb {
** return from the subroutine.
*/
-#ifdef CONFIG_NCR53C8XX_PREFETCH
+#ifdef NCR53C8XX_PREFETCH
#define PREFETCH_FLUSH_CNT 2
#define PREFETCH_FLUSH SCR_CALL, PADDRH (wait_dma),
#else
diff --git a/drivers/scsi/ncr53c8xx.h b/drivers/scsi/ncr53c8xx.h
index be38c902859e0..2f6865ca1b87d 100644
--- a/drivers/scsi/ncr53c8xx.h
+++ b/drivers/scsi/ncr53c8xx.h
@@ -397,7 +397,7 @@
#else
-#ifdef CONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
+#ifdef SCSI_NCR53C8XX_NO_WORD_TRANSFERS
/* Only 8 or 32 bit transfers allowed */
#define INW_OFF(o) (readb((char __iomem *)np->reg + ncr_offw(o)) << 8 | readb((char __iomem *)np->reg + ncr_offw(o) + 1))
#else
@@ -405,7 +405,7 @@
#endif
#define INL_OFF(o) readl_raw((char __iomem *)np->reg + (o))
-#ifdef CONFIG_SCSI_NCR53C8XX_NO_WORD_TRANSFERS
+#ifdef SCSI_NCR53C8XX_NO_WORD_TRANSFERS
/* Only 8 or 32 bit transfers allowed */
#define OUTW_OFF(o, val) do { writeb((char)((val) >> 8), (char __iomem *)np->reg + ncr_offw(o)); writeb((char)(val), (char __iomem *)np->reg + ncr_offw(o) + 1); } while (0)
#else
diff --git a/drivers/scsi/pm8001/pm8001_ctl.c b/drivers/scsi/pm8001/pm8001_ctl.c
index cbfda8c04e956..bb38b2d63acb8 100644
--- a/drivers/scsi/pm8001/pm8001_ctl.c
+++ b/drivers/scsi/pm8001/pm8001_ctl.c
@@ -826,6 +826,14 @@ static ssize_t pm8001_store_update_fw(struct device *cdev,
goto out;
}
+ if (pm8001_ha->controller_fatal_error) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "controller in fatal error state, firmware update rejected\n");
+ pm8001_ha->fw_status = FAIL_PARAMETERS;
+ ret = -EINVAL;
+ goto out;
+ }
+
for (i = 0; flash_command_table[i].code != FLASH_CMD_NONE; i++) {
if (!memcmp(flash_command_table[i].command,
cmd_ptr, strlen(cmd_ptr))) {
diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c
index 954f307352e61..2c0fa7ab33d22 100644
--- a/drivers/scsi/pm8001/pm80xx_hwi.c
+++ b/drivers/scsi/pm8001/pm80xx_hwi.c
@@ -401,6 +401,13 @@ ssize_t pm80xx_get_non_fatal_dump(struct device *cdev,
char *buf_copy = buf;
temp = (u32 *)pm8001_ha->memoryMap.region[FORENSIC_MEM].virt_ptr;
+
+ if (pm8001_ha->controller_fatal_error) {
+ pm8001_dbg(pm8001_ha, FAIL,
+ "non-fatal dump not available in fatal error state\n");
+ return -EINVAL;
+ }
+
if (++pm8001_ha->non_fatal_count == 1) {
if (pm8001_ha->chip_id == chip_8001) {
snprintf(pm8001_ha->forensic_info.data_buf.direct_data,
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index ef22a4228b855..a35a5f777d166 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -858,7 +858,7 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result,
}
/**
- * scsi_add_lun - allocate and fully initialze a scsi_device
+ * scsi_add_lun - allocate and fully initialize a scsi_device
* @sdev: holds information to be stored in the new scsi_device
* @inq_result: holds the result of a previous INQUIRY to the LUN
* @bflags: black/white list flag
diff --git a/drivers/scsi/scsi_transport_srp.c b/drivers/scsi/scsi_transport_srp.c
index d71ab5fdb7588..a61cbb079ab4e 100644
--- a/drivers/scsi/scsi_transport_srp.c
+++ b/drivers/scsi/scsi_transport_srp.c
@@ -234,7 +234,7 @@ static ssize_t store_reconnect_delay(struct device *dev,
if (rport->reconnect_delay <= 0 && delay > 0 &&
rport->state != SRP_RPORT_RUNNING) {
- queue_delayed_work(system_long_wq, &rport->reconnect_work,
+ queue_delayed_work(system_dfl_long_wq, &rport->reconnect_work,
delay * HZ);
} else if (delay <= 0) {
cancel_delayed_work(&rport->reconnect_work);
@@ -390,7 +390,7 @@ static void srp_reconnect_work(struct work_struct *work)
delay = rport->reconnect_delay *
clamp(rport->failed_reconnects - 10, 1, 100);
if (delay > 0)
- queue_delayed_work(system_long_wq,
+ queue_delayed_work(system_dfl_long_wq,
&rport->reconnect_work, delay * HZ);
}
}
@@ -474,7 +474,7 @@ static void __srp_start_tl_fail_timers(struct srp_rport *rport)
if (rport->state == SRP_RPORT_LOST)
return;
if (delay > 0)
- queue_delayed_work(system_long_wq, &rport->reconnect_work,
+ queue_delayed_work(system_dfl_long_wq, &rport->reconnect_work,
1UL * delay * HZ);
if ((fast_io_fail_tmo >= 0 || dev_loss_tmo >= 0) &&
srp_rport_set_state(rport, SRP_RPORT_BLOCKED) == 0) {
@@ -482,11 +482,11 @@ static void __srp_start_tl_fail_timers(struct srp_rport *rport)
rport->state);
scsi_block_targets(shost, &shost->shost_gendev);
if (fast_io_fail_tmo >= 0)
- queue_delayed_work(system_long_wq,
+ queue_delayed_work(system_dfl_long_wq,
&rport->fast_io_fail_work,
1UL * fast_io_fail_tmo * HZ);
if (dev_loss_tmo >= 0)
- queue_delayed_work(system_long_wq,
+ queue_delayed_work(system_dfl_long_wq,
&rport->dev_loss_work,
1UL * dev_loss_tmo * HZ);
}
diff --git a/drivers/scsi/snic/vnic_dev.c b/drivers/scsi/snic/vnic_dev.c
index ed7771e628541..22303f8275836 100644
--- a/drivers/scsi/snic/vnic_dev.c
+++ b/drivers/scsi/snic/vnic_dev.c
@@ -132,7 +132,6 @@ static int vnic_dev_discover_res(struct vnic_dev *vdev,
case RES_TYPE_INTR_PBA_LEGACY:
case RES_TYPE_DEVCMD:
case RES_TYPE_DEVCMD2:
- len = count;
break;
default:
diff --git a/drivers/scsi/storvsc_drv.c b/drivers/scsi/storvsc_drv.c
index 6977ca8a06582..571ea549152b1 100644
--- a/drivers/scsi/storvsc_drv.c
+++ b/drivers/scsi/storvsc_drv.c
@@ -156,7 +156,7 @@ static bool hv_dev_is_fc(struct hv_device *hv_dev);
#define STORVSC_LOGGING_WARN 2
static int logging_level = STORVSC_LOGGING_ERROR;
-module_param(logging_level, int, S_IRUGO|S_IWUSR);
+module_param(logging_level, int, 0644);
MODULE_PARM_DESC(logging_level,
"Logging level, 0 - None, 1 - Error (default), 2 - Warning.");
@@ -345,17 +345,17 @@ static int storvsc_change_queue_depth(struct scsi_device *sdev, int queue_depth)
static int storvsc_vcpus_per_sub_channel = 4;
static unsigned int storvsc_max_hw_queues;
-module_param(storvsc_ringbuffer_size, int, S_IRUGO);
+module_param(storvsc_ringbuffer_size, int, 0444);
MODULE_PARM_DESC(storvsc_ringbuffer_size, "Ring buffer size (bytes)");
module_param(storvsc_max_hw_queues, uint, 0644);
MODULE_PARM_DESC(storvsc_max_hw_queues, "Maximum number of hardware queues");
-module_param(storvsc_vcpus_per_sub_channel, int, S_IRUGO);
+module_param(storvsc_vcpus_per_sub_channel, int, 0444);
MODULE_PARM_DESC(storvsc_vcpus_per_sub_channel, "Ratio of VCPUs to subchannels");
static int ring_avail_percent_lowater = 10;
-module_param(ring_avail_percent_lowater, int, S_IRUGO);
+module_param(ring_avail_percent_lowater, int, 0444);
MODULE_PARM_DESC(ring_avail_percent_lowater,
"Select a channel if available ring size > this in percent");
diff --git a/drivers/ufs/core/ufs-sysfs.c b/drivers/ufs/core/ufs-sysfs.c
index 99af3c73f1af7..d9dc4cc3452ee 100644
--- a/drivers/ufs/core/ufs-sysfs.c
+++ b/drivers/ufs/core/ufs-sysfs.c
@@ -594,8 +594,13 @@ static ssize_t device_lvl_exception_id_show(struct device *dev,
u64 exception_id;
int err;
+ if (hba->dev_info.wspecversion < 0x410)
+ return -EOPNOTSUPP;
+
ufshcd_rpm_get_sync(hba);
- err = ufshcd_read_device_lvl_exception_id(hba, &exception_id);
+ err = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID,
+ 0, 0, &exception_id);
ufshcd_rpm_put_sync(hba);
if (err)
@@ -1670,6 +1675,12 @@ static inline bool ufshcd_is_wb_attrs(enum attr_idn idn)
idn <= QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE;
}
+static inline bool ufshcd_is_qword_attr(enum attr_idn idn)
+{
+ return idn == QUERY_ATTR_IDN_TIMESTAMP ||
+ idn == QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID;
+}
+
static int wb_read_resize_attrs(struct ufs_hba *hba,
enum attr_idn idn, u32 *attr_val)
{
@@ -1736,6 +1747,7 @@ static ssize_t _name##_show(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct ufs_hba *hba = dev_get_drvdata(dev); \
+ u64 qword_value; \
u32 value; \
int ret; \
u8 index = 0; \
@@ -1748,14 +1760,24 @@ static ssize_t _name##_show(struct device *dev, \
if (ufshcd_is_wb_attrs(QUERY_ATTR_IDN##_uname)) \
index = ufshcd_wb_get_query_index(hba); \
ufshcd_rpm_get_sync(hba); \
- ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR, \
- QUERY_ATTR_IDN##_uname, index, 0, &value); \
+ if (ufshcd_is_qword_attr(QUERY_ATTR_IDN##_uname)) \
+ ret = ufshcd_query_attr_qword(hba, \
+ UPIU_QUERY_OPCODE_READ_ATTR, \
+ QUERY_ATTR_IDN##_uname, \
+ index, 0, &qword_value); \
+ else \
+ ret = ufshcd_query_attr(hba, \
+ UPIU_QUERY_OPCODE_READ_ATTR, \
+ QUERY_ATTR_IDN##_uname, index, 0, &value); \
ufshcd_rpm_put_sync(hba); \
if (ret) { \
ret = -EINVAL; \
goto out; \
} \
- ret = sysfs_emit(buf, "0x%08X\n", value); \
+ if (ufshcd_is_qword_attr(QUERY_ATTR_IDN##_uname)) \
+ ret = sysfs_emit(buf, "0x%016llX\n", qword_value); \
+ else \
+ ret = sysfs_emit(buf, "0x%08X\n", value); \
out: \
up(&hba->host_sem); \
return ret; \
diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c
index b2dc891243538..4b264adfdf49a 100644
--- a/drivers/ufs/core/ufs-txeq.c
+++ b/drivers/ufs/core/ufs-txeq.c
@@ -14,6 +14,9 @@
#include <ufs/unipro.h>
#include "ufshcd-priv.h"
+#define TX_EQ_SETTING_MASK 0x7
+#define TX_EQ_SETTINGS_VALID_BIT BIT(15)
+
static bool use_adaptive_txeq;
module_param(use_adaptive_txeq, bool, 0644);
MODULE_PARM_DESC(use_adaptive_txeq, "Find and apply optimal TX Equalization settings before changing Power Mode (default: false)");
@@ -40,6 +43,28 @@ static bool txeq_presets_selected[UFS_TX_EQ_PRESET_MAX] = {[0 ... (UFS_TX_EQ_PRE
module_param_array(txeq_presets_selected, bool, NULL, 0644);
MODULE_PARM_DESC(txeq_presets_selected, "Use only the selected Presets out of the 8 TX Equalization Presets for TX EQTR");
+static int txeq_setting_sel_set(const char *val, const struct kernel_param *kp)
+{
+ return param_set_uint_minmax(val, kp, 0, 1);
+}
+
+static const struct kernel_param_ops txeq_setting_sel_ops = {
+ .set = txeq_setting_sel_set,
+ .get = param_get_uint,
+};
+
+static unsigned int txeq_setting_sel;
+module_param_cb(txeq_setting_sel, &txeq_setting_sel_ops, &txeq_setting_sel, 0644);
+MODULE_PARM_DESC(txeq_setting_sel, "The qTxEQGnSettings and wTxEQGnSettingsExt Attributes selector used to retrieve and store TX Equalization settings");
+
+static bool retrieve_txeq_setting = true;
+module_param(retrieve_txeq_setting, bool, 0644);
+MODULE_PARM_DESC(retrieve_txeq_setting, "Retrieve TX Equalization settings from qTxEQGnSettings and wTxEQGnSettingsExt Attributes (default: true)");
+
+static bool store_txeq_setting = true;
+module_param(store_txeq_setting, bool, 0644);
+MODULE_PARM_DESC(store_txeq_setting, "Store the optimal TX Equalization settings to qTxEQGnSettings and wTxEQGnSettingsExt Attributes (default: true)");
+
/*
* ufs_tx_eq_preset - Table of minimum required list of presets.
*
@@ -117,6 +142,126 @@ static const u32 pa_tx_eq_setting[UFS_HS_GEAR_MAX] = {
PA_TXEQG6SETTING
};
+/*
+ * Decode Device TX Equalization PreShoot value based on qTxEQGnSettings bit assignment:
+ * bit[3:0]: Device TX Logical LANE 0 PreShoot
+ * bit[7:4]: Device TX Logical LANE 1 PreShoot
+ */
+static inline u8 tx_eq_device_preshoot_decode(u64 eq, u8 lane)
+{
+ return (u8)((eq >> (lane * TX_HS_PRESHOOT_SHIFT)) & TX_EQ_SETTING_MASK);
+}
+
+/*
+ * Decode Device TX Equalization DeEmphasis value based on qTxEQGnSettings bit assignment:
+ * bit[19:16]: Device TX Logical LANE 0 DeEmphasis
+ * bit[23:20]: Device TX Logical LANE 1 DeEmphasis
+ */
+static inline u8 tx_eq_device_deemphasis_decode(u64 eq, u8 lane)
+{
+ return (u8)((eq >> (lane * TX_HS_DEEMPHASIS_SHIFT + 16)) & TX_EQ_SETTING_MASK);
+}
+
+/*
+ * Decode Host TX Equalization PreShoot value based on qTxEQGnSettings bit assignment:
+ * bit[35:32]: Host TX Logical LANE 0 PreShoot
+ * bit[39:36]: Host TX Logical LANE 1 PreShoot
+ */
+static inline u8 tx_eq_host_preshoot_decode(u64 eq, u8 lane)
+{
+ return (u8)((eq >> (lane * TX_HS_PRESHOOT_SHIFT + 32)) & TX_EQ_SETTING_MASK);
+}
+
+/*
+ * Decode Host TX Equalization DeEmphasis value based on qTxEQGnSettings bit assignment:
+ * bit[51:48]: Host TX Logical LANE 0 DeEmphasis
+ * bit[55:52]: Host TX Logical LANE 1 DeEmphasis
+ */
+static inline u8 tx_eq_host_deemphasis_decode(u64 eq, u8 lane)
+{
+ return (u8)((eq >> (lane * TX_HS_DEEMPHASIS_SHIFT + 48)) & TX_EQ_SETTING_MASK);
+}
+
+/*
+ * Decode Device TX precode_en indication based on wTxEQGnSettingsExt bit assignment:
+ * bit[0]: PreCodeEn for Device TX Logical LANE 0
+ * bit[1]: PreCodeEn for Device TX Logical LANE 1
+ */
+static inline bool tx_eq_device_precode_en_decode(u16 eq_ext, u8 lane)
+{
+ return eq_ext & BIT(lane);
+}
+
+/*
+ * Decode Host TX precode_en indication based on wTxEQGnSettingsExt bit assignment:
+ * bit[4]: PreCodeEn for Device RX Logical LANE 0
+ * bit[5]: PreCodeEn for Device RX Logical LANE 1
+ */
+static inline bool tx_eq_host_precode_en_decode(u16 eq_ext, u8 lane)
+{
+ return eq_ext & BIT(lane + 4);
+}
+
+/*
+ * Encode Device TX Equalization PreShoot value based on qTxEQGnSettings bit assignment:
+ * bit[3:0]: Device TX Logical LANE 0 PreShoot
+ * bit[7:4]: Device TX Logical LANE 1 PreShoot
+ */
+static inline u64 tx_eq_device_preshoot_encode(u64 val, u8 lane)
+{
+ return (val & TX_EQ_SETTING_MASK) << (lane * TX_HS_PRESHOOT_SHIFT);
+}
+
+/*
+ * Encode Device TX Equalization DeEmphasis value based on qTxEQGnSettings bit assignment:
+ * bit[19:16]: Device TX Logical LANE 0 DeEmphasis
+ * bit[23:20]: Device TX Logical LANE 1 DeEmphasis
+ */
+static inline u64 tx_eq_device_deemphasis_encode(u64 val, u8 lane)
+{
+ return (val & TX_EQ_SETTING_MASK) << (lane * TX_HS_DEEMPHASIS_SHIFT + 16);
+}
+
+/*
+ * Encode Host TX Equalization PreShoot value based on qTxEQGnSettings bit assignment:
+ * bit[35:32]: Host TX Logical LANE 0 PreShoot
+ * bit[39:36]: Host TX Logical LANE 1 PreShoot
+ */
+static inline u64 tx_eq_host_preshoot_encode(u64 val, u8 lane)
+{
+ return (val & TX_EQ_SETTING_MASK) << (lane * TX_HS_PRESHOOT_SHIFT + 32);
+}
+
+/*
+ * Encode Host TX Equalization DeEmphasis value based on qTxEQGnSettings bit assignment:
+ * bit[51:48]: Host TX Logical LANE 0 DeEmphasis
+ * bit[55:52]: Host TX Logical LANE 1 DeEmphasis
+ */
+static inline u64 tx_eq_host_deemphasis_encode(u64 val, u8 lane)
+{
+ return (val & TX_EQ_SETTING_MASK) << (lane * TX_HS_DEEMPHASIS_SHIFT + 48);
+}
+
+/*
+ * Encode Device precode_en based on wTxEQGnSettingsExt bit assignment:
+ * bit[0]: PreCodeEn for Device TX Logical LANE 0
+ * bit[1]: PreCodeEn for Device TX Logical LANE 1
+ */
+static inline u16 tx_eq_device_precode_en_encode(bool en, u8 lane)
+{
+ return (u16)en << lane;
+}
+
+/*
+ * Encode Host precode_en based on wTxEQGnSettingsExt bit assignment:
+ * bit[4]: PreCodeEn for Device RX Logical LANE 0
+ * bit[5]: PreCodeEn for Device RX Logical LANE 1
+ */
+static inline u16 tx_eq_host_precode_en_encode(bool en, u8 lane)
+{
+ return (u16)en << (lane + 4);
+}
+
/**
* ufshcd_configure_precoding - Configure Pre-Coding for all active lanes
* @hba: per adapter instance
@@ -1164,6 +1309,7 @@ int ufshcd_config_tx_eq_settings(struct ufs_hba *hba,
/* Mark TX Equalization settings as valid */
params->is_valid = true;
+ params->is_trained = true;
params->is_applied = false;
}
@@ -1291,3 +1437,144 @@ out:
return ret;
}
+
+/**
+ * ufshcd_extract_tx_eq_settings_attrs - Extract TX Equalization settings from UFS attributes
+ * @hba: per adapter instance
+ * @gear: target gear
+ *
+ * This function extracts previously stored TX Equalization settings from UFS
+ * attributes qTxEQGnSettings and wTxEQGnSettingsExt. These attributes contain
+ * the optimal TX Equalization parameters (PreShoot, DeEmphasis, and PreCoding
+ * enable) that were determined during a previous EQTR procedure.
+ *
+ * The function reads:
+ * 1. qTxEQGnSettings (64-bit): Main attribute containing PreShoot and
+ * DeEmphasis values for both host and device TX lanes
+ * 2. wTxEQGnSettingsExt (16-bit): Extended attribute containing PreCoding
+ * enable flags and validity indicator
+ */
+static void ufshcd_extract_tx_eq_settings_attrs(struct ufs_hba *hba, u8 gear)
+{
+ struct ufshcd_tx_eq_params *params;
+ u32 lane, eq_ext;
+ int ret;
+ u64 eq;
+
+ ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS_EXT, gear - 1,
+ (u8)txeq_setting_sel, &eq_ext);
+ if (ret)
+ return;
+
+ dev_dbg(hba->dev, "%s: HS-G%u wTxEQGnSettingsExt (Selector %u) = 0x%08x\n",
+ __func__, gear, txeq_setting_sel, eq_ext);
+
+ if (!(eq_ext & TX_EQ_SETTINGS_VALID_BIT))
+ return;
+
+ ret = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_READ_ATTR,
+ QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS,
+ gear - 1, (u8)txeq_setting_sel, &eq);
+ if (ret)
+ return;
+
+ dev_dbg(hba->dev, "%s: HS-G%u qTxEQGnSettings (Selector %u) = 0x%016llx\n",
+ __func__, gear, txeq_setting_sel, eq);
+
+ params = &hba->tx_eq_params[gear - 1];
+
+ for (lane = 0; lane < UFS_MAX_LANES; lane++) {
+ params->host[lane].preshoot = tx_eq_host_preshoot_decode(eq, lane);
+ params->host[lane].deemphasis = tx_eq_host_deemphasis_decode(eq, lane);
+ params->host[lane].precode_en = tx_eq_host_precode_en_decode(eq_ext, lane);
+
+ params->device[lane].preshoot = tx_eq_device_preshoot_decode(eq, lane);
+ params->device[lane].deemphasis = tx_eq_device_deemphasis_decode(eq, lane);
+ params->device[lane].precode_en = tx_eq_device_precode_en_decode(eq_ext, lane);
+ }
+
+ params->is_valid = true;
+}
+
+void ufshcd_retrieve_tx_eq_settings(struct ufs_hba *hba)
+{
+ u8 gear = (u8)adaptive_txeq_gear;
+
+ if (!hba->max_pwr_info.is_valid || !ufshcd_is_tx_eq_supported(hba) ||
+ !use_adaptive_txeq || !retrieve_txeq_setting)
+ return;
+
+ for (; gear <= UFS_HS_GEAR_MAX; gear++)
+ ufshcd_extract_tx_eq_settings_attrs(hba, gear);
+}
+
+/**
+ * ufshcd_update_tx_eq_settings_attrs - Update TX EQ settings in UFS attributes
+ * @hba: per adapter instance
+ * @gear: target gear
+ *
+ * This function stores the optimal TX Equalization settings obtained from
+ * TX EQTR procedure into UFS device attributes for future fast-path retrieval.
+ * The settings are stored in two complementary attributes:
+ *
+ * 1. qTxEQGnSettings (64-bit): Main attribute containing PreShoot and
+ * DeEmphasis values for both host and device TX lanes
+ * 2. wTxEQGnSettingsExt (16-bit): Extended attribute containing PreCoding
+ * enable flags and validity indicator
+ */
+static void ufshcd_update_tx_eq_settings_attrs(struct ufs_hba *hba, u8 gear)
+{
+ struct ufshcd_tx_eq_params *params;
+ u32 lane, eq_ext = 0;
+ u64 eq = 0;
+ int ret;
+
+ params = &hba->tx_eq_params[gear - 1];
+ if (!params->is_valid || !params->is_trained)
+ return;
+
+ for (lane = 0; lane < UFS_MAX_LANES; lane++) {
+ eq |= tx_eq_host_preshoot_encode((u64)params->host[lane].preshoot, lane);
+ eq |= tx_eq_host_deemphasis_encode((u64)params->host[lane].deemphasis, lane);
+ eq_ext |= tx_eq_host_precode_en_encode(params->host[lane].precode_en, lane);
+
+ eq |= tx_eq_device_preshoot_encode((u64)params->device[lane].preshoot, lane);
+ eq |= tx_eq_device_deemphasis_encode((u64)params->device[lane].deemphasis, lane);
+ eq_ext |= tx_eq_device_precode_en_encode(params->device[lane].precode_en, lane);
+ }
+
+ /* Set validity flag to indicate valid settings are stored */
+ eq_ext |= TX_EQ_SETTINGS_VALID_BIT;
+
+ /* Write qTxEQGnSettings */
+ ret = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+ QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS,
+ gear - 1, (u8)txeq_setting_sel, &eq);
+ if (ret)
+ return;
+
+ /* Write wTxEQGnSettingsExt */
+ ret = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+ QUERY_ATTR_IDN_TX_EQ_GN_SETTINGS_EXT, gear - 1,
+ (u8)txeq_setting_sel, &eq_ext);
+ if (ret)
+ return;
+
+ dev_dbg(hba->dev, "%s: Saved HS-G%u qTxEQGnSettings (Selector %u) = 0x%016llx\n",
+ __func__, gear, txeq_setting_sel, eq);
+ dev_dbg(hba->dev, "%s: Saved HS-G%u wTxEQGnSettingsExt (Selector %u) = 0x%08x\n",
+ __func__, gear, txeq_setting_sel, eq_ext);
+}
+
+void ufshcd_store_tx_eq_settings(struct ufs_hba *hba)
+{
+ u8 gear = (u8)adaptive_txeq_gear;
+
+ if (!hba->max_pwr_info.is_valid || !ufshcd_is_tx_eq_supported(hba) ||
+ !use_adaptive_txeq || !store_txeq_setting)
+ return;
+
+ for (; gear <= UFS_HS_GEAR_MAX; gear++)
+ ufshcd_update_tx_eq_settings_attrs(hba, gear);
+}
diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h
index 0a72148cb053e..70f90d97f2177 100644
--- a/drivers/ufs/core/ufshcd-priv.h
+++ b/drivers/ufs/core/ufshcd-priv.h
@@ -60,6 +60,8 @@ int ufshcd_query_attr_retry(struct ufs_hba *hba, enum query_opcode opcode,
u32 *attr_val);
int ufshcd_query_attr(struct ufs_hba *hba, enum query_opcode opcode,
enum attr_idn idn, u8 index, u8 selector, u32 *attr_val);
+int ufshcd_query_attr_qword(struct ufs_hba *hba, enum query_opcode opcode,
+ enum attr_idn idn, u8 index, u8 sel, u64 *attr_val);
int ufshcd_query_flag(struct ufs_hba *hba, enum query_opcode opcode,
enum flag_idn idn, u8 index, bool *flag_res);
void ufshcd_auto_hibern8_update(struct ufs_hba *hba, u32 ahit);
@@ -106,7 +108,6 @@ int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba,
enum query_opcode desc_op);
int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable);
-int ufshcd_read_device_lvl_exception_id(struct ufs_hba *hba, u64 *exception_id);
int ufshcd_uic_tx_eqtr(struct ufs_hba *hba, int gear);
void ufshcd_apply_valid_tx_eq_settings(struct ufs_hba *hba);
@@ -117,6 +118,8 @@ void ufshcd_print_tx_eq_params(struct ufs_hba *hba);
bool ufshcd_is_txeq_presets_used(struct ufs_hba *hba);
bool ufshcd_is_txeq_preset_selected(u8 preshoot, u8 deemphasis);
int ufshcd_retrain_tx_eq(struct ufs_hba *hba, u32 gear);
+void ufshcd_retrieve_tx_eq_settings(struct ufs_hba *hba);
+void ufshcd_store_tx_eq_settings(struct ufs_hba *hba);
/* Wrapper functions for safely calling variant operations */
static inline const char *ufshcd_get_var_name(struct ufs_hba *hba)
diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
index c3f08957d179a..06b965080fb0e 100644
--- a/drivers/ufs/core/ufshcd.c
+++ b/drivers/ufs/core/ufshcd.c
@@ -3611,6 +3611,67 @@ int ufshcd_query_attr_retry(struct ufs_hba *hba,
return ret;
}
+/**
+ * ufshcd_query_attr_qword - Function of sending query requests for quad-word attributes
+ * @hba: per-adapter instance
+ * @opcode: attribute opcode
+ * @idn: attribute idn to access
+ * @index: index field
+ * @sel: selector field
+ * @attr_val: the attribute value after the query request completes
+ *
+ * Return: 0 for success, non-zero in case of failure.
+ */
+int ufshcd_query_attr_qword(struct ufs_hba *hba, enum query_opcode opcode,
+ enum attr_idn idn, u8 index, u8 sel, u64 *attr_val)
+{
+ struct utp_upiu_query_v4_0 *upiu_req;
+ struct utp_upiu_query_v4_0 *upiu_resp;
+ struct ufs_query_req *request = NULL;
+ struct ufs_query_res *response = NULL;
+ int err;
+
+ if (!attr_val) {
+ dev_err(hba->dev, "%s: attribute value required for opcode 0x%x\n",
+ __func__, opcode);
+ return -EINVAL;
+ }
+
+ ufshcd_dev_man_lock(hba);
+
+ ufshcd_init_query(hba, &request, &response, opcode, idn, index, sel);
+
+ switch (opcode) {
+ case UPIU_QUERY_OPCODE_WRITE_ATTR:
+ request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
+ upiu_req = (struct utp_upiu_query_v4_0 *)&request->upiu_req;
+ put_unaligned_be64(*attr_val, &upiu_req->osf3);
+ break;
+ case UPIU_QUERY_OPCODE_READ_ATTR:
+ request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
+ break;
+ default:
+ dev_err(hba->dev, "%s: Expected query attr opcode but got = 0x%.2x\n",
+ __func__, opcode);
+ err = -EINVAL;
+ goto out_unlock;
+ }
+
+ err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, dev_cmd_timeout);
+ if (err) {
+ dev_err(hba->dev, "%s: opcode 0x%.2x for idn %d failed, index %d, selector %d, err = %d\n",
+ __func__, opcode, idn, index, sel, err);
+ goto out_unlock;
+ }
+
+ upiu_resp = (struct utp_upiu_query_v4_0 *)response;
+ *attr_val = get_unaligned_be64(&upiu_resp->osf3);
+
+out_unlock:
+ ufshcd_dev_man_unlock(hba);
+ return err;
+}
+
/*
* Return: 0 upon success; > 0 in case the UFS device reported an OCS error;
* < 0 if another error occurred.
@@ -5157,6 +5218,35 @@ void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val)
}
EXPORT_SYMBOL_GPL(ufshcd_update_evt_hist);
+static int ufshcd_validate_link_params(struct ufs_hba *hba)
+{
+ int ret, val;
+
+ ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES),
+ &val);
+ if (ret)
+ return ret;
+
+ if (val != hba->lanes_per_direction) {
+ dev_err(hba->dev, "Tx lane mismatch [config,reported] [%d,%d]\n",
+ hba->lanes_per_direction, val);
+ return -ENOLINK;
+ }
+
+ ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDRXDATALANES),
+ &val);
+ if (ret)
+ return ret;
+
+ if (val != hba->lanes_per_direction) {
+ dev_err(hba->dev, "Rx lane mismatch [config,reported] [%d,%d]\n",
+ hba->lanes_per_direction, val);
+ return -ENOLINK;
+ }
+
+ return 0;
+}
+
/**
* ufshcd_link_startup - Initialize unipro link startup
* @hba: per adapter instance
@@ -5230,6 +5320,10 @@ link_startup:
goto out;
}
+ ret = ufshcd_validate_link_params(hba);
+ if (ret)
+ goto out;
+
/* Include any host controller configuration via UIC commands */
ret = ufshcd_vops_link_startup_notify(hba, POST_CHANGE);
if (ret)
@@ -6224,46 +6318,6 @@ out:
__func__, err);
}
-/*
- * Return: 0 upon success; > 0 in case the UFS device reported an OCS error;
- * < 0 if another error occurred.
- */
-int ufshcd_read_device_lvl_exception_id(struct ufs_hba *hba, u64 *exception_id)
-{
- struct utp_upiu_query_v4_0 *upiu_resp;
- struct ufs_query_req *request = NULL;
- struct ufs_query_res *response = NULL;
- int err;
-
- if (hba->dev_info.wspecversion < 0x410)
- return -EOPNOTSUPP;
-
- ufshcd_hold(hba);
- mutex_lock(&hba->dev_cmd.lock);
-
- ufshcd_init_query(hba, &request, &response,
- UPIU_QUERY_OPCODE_READ_ATTR,
- QUERY_ATTR_IDN_DEV_LVL_EXCEPTION_ID, 0, 0);
-
- request->query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST;
-
- err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, dev_cmd_timeout);
-
- if (err) {
- dev_err(hba->dev, "%s: failed to read device level exception %d\n",
- __func__, err);
- goto out;
- }
-
- upiu_resp = (struct utp_upiu_query_v4_0 *)response;
- *exception_id = get_unaligned_be64(&upiu_resp->osf3);
-out:
- mutex_unlock(&hba->dev_cmd.lock);
- ufshcd_release(hba);
-
- return err;
-}
-
static int __ufshcd_wb_toggle(struct ufs_hba *hba, bool set, enum flag_idn idn)
{
u8 index;
@@ -9107,41 +9161,28 @@ static int ufshcd_device_params_init(struct ufs_hba *hba)
dev_err(hba->dev,
"%s: Failed getting max supported power mode\n",
__func__);
+
+ ufshcd_retrieve_tx_eq_settings(hba);
out:
return ret;
}
static void ufshcd_set_timestamp_attr(struct ufs_hba *hba)
{
- int err;
- struct ufs_query_req *request = NULL;
- struct ufs_query_res *response = NULL;
struct ufs_dev_info *dev_info = &hba->dev_info;
- struct utp_upiu_query_v4_0 *upiu_data;
+ u64 ts_ns;
+ int err;
if (dev_info->wspecversion < 0x400 ||
hba->dev_quirks & UFS_DEVICE_QUIRK_NO_TIMESTAMP_SUPPORT)
return;
- ufshcd_dev_man_lock(hba);
-
- ufshcd_init_query(hba, &request, &response,
- UPIU_QUERY_OPCODE_WRITE_ATTR,
- QUERY_ATTR_IDN_TIMESTAMP, 0, 0);
-
- request->query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST;
-
- upiu_data = (struct utp_upiu_query_v4_0 *)&request->upiu_req;
-
- put_unaligned_be64(ktime_get_real_ns(), &upiu_data->osf3);
-
- err = ufshcd_exec_dev_cmd(hba, DEV_CMD_TYPE_QUERY, dev_cmd_timeout);
-
+ ts_ns = ktime_get_real_ns();
+ err = ufshcd_query_attr_qword(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
+ QUERY_ATTR_IDN_TIMESTAMP, 0, 0, &ts_ns);
if (err)
dev_err(hba->dev, "%s: failed to set timestamp %d\n",
__func__, err);
-
- ufshcd_dev_man_unlock(hba);
}
/**
@@ -10768,6 +10809,9 @@ static void ufshcd_wl_shutdown(struct scsi_device *sdev)
/* Turn on everything while shutting down */
ufshcd_rpm_get_sync(hba);
+
+ ufshcd_store_tx_eq_settings(hba);
+
scsi_device_quiesce(sdev);
shost_for_each_device(sdev, hba->host) {
if (sdev == hba->ufs_device_wlun)
diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig
index 964ae70e7390d..ff170c0b6da0c 100644
--- a/drivers/ufs/host/Kconfig
+++ b/drivers/ufs/host/Kconfig
@@ -55,7 +55,7 @@ config SCSI_UFS_DWC_TC_PLATFORM
If unsure, say N.
config SCSI_UFS_QCOM
- tristate "QCOM specific hooks to UFS controller platform driver"
+ tristate "Qualcomm specific hooks to UFS controller platform driver"
depends on SCSI_UFSHCD_PLATFORM && ARCH_QCOM
depends on GENERIC_MSI_IRQ
depends on RESET_CONTROLLER
diff --git a/drivers/ufs/host/tc-dwc-g210-pci.c b/drivers/ufs/host/tc-dwc-g210-pci.c
index 0167d8bef71a1..c6d89f9c44ae0 100644
--- a/drivers/ufs/host/tc-dwc-g210-pci.c
+++ b/drivers/ufs/host/tc-dwc-g210-pci.c
@@ -114,8 +114,8 @@ static const struct dev_pm_ops tc_dwc_g210_pci_pm_ops = {
};
static const struct pci_device_id tc_dwc_g210_pci_tbl[] = {
- { PCI_VENDOR_ID_SYNOPSYS, 0xB101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { PCI_VENDOR_ID_SYNOPSYS, 0xB102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { PCI_VDEVICE(SYNOPSYS, 0xB101) },
+ { PCI_VDEVICE(SYNOPSYS, 0xB102) },
{ } /* terminate list */
};
diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c
index 77a6c8e44485b..b2f65c4655251 100644
--- a/drivers/ufs/host/ufs-exynos.c
+++ b/drivers/ufs/host/ufs-exynos.c
@@ -97,6 +97,10 @@
#define UFS_EXYNOSAUTO_RD_SHARABLE BIT(1)
#define UFS_EXYNOSAUTO_SHARABLE (UFS_EXYNOSAUTO_WR_SHARABLE | \
UFS_EXYNOSAUTO_RD_SHARABLE)
+#define UFS_EXYNOSAUTOV920_WR_SHARABLE BIT(3)
+#define UFS_EXYNOSAUTOV920_RD_SHARABLE BIT(2)
+#define UFS_EXYNOSAUTOV920_SHARABLE (UFS_EXYNOSAUTOV920_WR_SHARABLE |\
+ UFS_EXYNOSAUTOV920_RD_SHARABLE)
#define UFS_GS101_WR_SHARABLE BIT(1)
#define UFS_GS101_RD_SHARABLE BIT(0)
#define UFS_GS101_SHARABLE (UFS_GS101_WR_SHARABLE | \
@@ -417,6 +421,95 @@ static int exynos7_ufs_post_pwr_change(struct exynos_ufs *ufs,
return 0;
}
+static int exynosautov920_ufs_pre_link(struct exynos_ufs *ufs)
+{
+ struct ufs_hba *hba = ufs->hba;
+ int i;
+ u32 tx_line_reset_period, rx_line_reset_period;
+
+ rx_line_reset_period = (RX_LINE_RESET_TIME * ufs->mclk_rate)
+ / NSEC_PER_MSEC;
+ tx_line_reset_period = (TX_LINE_RESET_TIME * ufs->mclk_rate)
+ / NSEC_PER_MSEC;
+
+ unipro_writel(ufs, 0x5f, 0x44);
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x202), 0x02);
+
+ for_each_ufs_rx_lane(ufs, i) {
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD, i),
+ DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_CLK_PRD_EN, i), 0x0);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE2, i),
+ (rx_line_reset_period >> 16) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE1, i),
+ (rx_line_reset_period >> 8) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_RX_LINERESET_VALUE0, i),
+ (rx_line_reset_period) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x2f, i), 0x69);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x84, i), 0x1);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x25, i), 0xf6);
+ }
+
+ for_each_ufs_tx_lane(ufs, i) {
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD, i),
+ DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate));
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_CLK_PRD_EN, i),
+ 0x02);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE2, i),
+ (tx_line_reset_period >> 16) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE1, i),
+ (tx_line_reset_period >> 8) & 0xFF);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(VND_TX_LINERESET_PVALUE0, i),
+ (tx_line_reset_period) & 0xFF);
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x04, i), 0x1);
+ ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x7f, i), 0x0);
+ }
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0xa011), 0x8000);
+
+ return 0;
+}
+
+static int exynosautov920_ufs_post_link(struct exynos_ufs *ufs)
+{
+ struct ufs_hba *hba = ufs->hba;
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x9529), 0x1);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x15a4), 0x3e8);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x9529), 0x0);
+
+ return 0;
+}
+
+static int exynosautov920_ufs_pre_pwr_change(struct exynos_ufs *ufs,
+ struct ufs_pa_layer_attr *pwr)
+{
+ struct ufs_hba *hba = ufs->hba;
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(0x15d4), 0x1);
+
+ ufshcd_dme_set(hba, UIC_ARG_MIB(DL_FC0PROTTIMEOUTVAL), 8064);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(DL_TC0REPLAYTIMEOUTVAL), 28224);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(DL_AFC0REQTIMEOUTVAL), 20160);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000);
+ ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000);
+
+ unipro_writel(ufs, 8064, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER0);
+ unipro_writel(ufs, 28224, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER1);
+ unipro_writel(ufs, 20160, UNIPRO_DME_POWERMODE_REQ_LOCALL2TIMER2);
+ unipro_writel(ufs, 12000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0);
+ unipro_writel(ufs, 32000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1);
+ unipro_writel(ufs, 16000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2);
+
+ return 0;
+}
+
/*
* exynos_ufs_auto_ctrl_hcc - HCI core clock control by h/w
* Control should be disabled in the below cases
@@ -2201,6 +2294,21 @@ static const struct exynos_ufs_drv_data gs101_ufs_drvs = {
.suspend = gs101_ufs_suspend,
};
+static const struct exynos_ufs_drv_data exynosautov920_ufs_drvs = {
+ .uic_attr = &exynos7_uic_attr,
+ .quirks = UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING,
+ .opts = EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL |
+ EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR |
+ EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX |
+ EXYNOS_UFS_OPT_TIMER_TICK_SELECT,
+ .iocc_mask = UFS_EXYNOSAUTOV920_SHARABLE,
+ .drv_init = exynosauto_ufs_drv_init,
+ .post_hce_enable = exynosauto_ufs_post_hce_enable,
+ .pre_link = exynosautov920_ufs_pre_link,
+ .post_link = exynosautov920_ufs_post_link,
+ .pre_pwr_change = exynosautov920_ufs_pre_pwr_change,
+};
+
static const struct of_device_id exynos_ufs_of_match[] = {
{ .compatible = "google,gs101-ufs",
.data = &gs101_ufs_drvs },
@@ -2210,6 +2318,8 @@ static const struct of_device_id exynos_ufs_of_match[] = {
.data = &exynosauto_ufs_drvs },
{ .compatible = "samsung,exynosautov9-ufs-vh",
.data = &exynosauto_ufs_vh_drvs },
+ { .compatible = "samsung,exynosautov920-ufs",
+ .data = &exynosautov920_ufs_drvs },
{ .compatible = "tesla,fsd-ufs",
.data = &fsd_ufs_drvs },
{},
diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c
index 9c0973a7ffc3a..9664b8ae67b78 100644
--- a/drivers/ufs/host/ufs-qcom.c
+++ b/drivers/ufs/host/ufs-qcom.c
@@ -705,6 +705,13 @@ cfg_timers:
return 0;
}
+static void ufs_qcom_link_startup_post_change(struct ufs_hba *hba)
+{
+ if (ufshcd_is_auto_hibern8_supported(hba))
+ ufshcd_rmwl(hba, UFS_HW_CLK_CTRL_EN, UFS_HW_CLK_CTRL_EN,
+ UFS_AH8_CFG);
+}
+
static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
enum ufs_notify_change_status status)
{
@@ -731,6 +738,9 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
err = ufshcd_disable_host_tx_lcc(hba);
break;
+ case POST_CHANGE:
+ ufs_qcom_link_startup_post_change(hba);
+ break;
default:
break;
}
diff --git a/drivers/ufs/host/ufs-qcom.h b/drivers/ufs/host/ufs-qcom.h
index 5d083331a7f43..e20b3ca505775 100644
--- a/drivers/ufs/host/ufs-qcom.h
+++ b/drivers/ufs/host/ufs-qcom.h
@@ -268,6 +268,17 @@ enum {
*/
#define NUM_TX_R1W1 13
+/* bit definitions for UFS_AH8_CFG register */
+#define CC_UFS_SYS_CLK_REQ_EN BIT(2)
+#define CC_UFS_ICE_CORE_CLK_REQ_EN BIT(3)
+#define CC_UFS_UNIPRO_CORE_CLK_REQ_EN BIT(4)
+#define CC_UFS_AUXCLK_REQ_EN BIT(5)
+
+#define UFS_HW_CLK_CTRL_EN (CC_UFS_SYS_CLK_REQ_EN |\
+ CC_UFS_ICE_CORE_CLK_REQ_EN |\
+ CC_UFS_UNIPRO_CORE_CLK_REQ_EN |\
+ CC_UFS_AUXCLK_REQ_EN)
+
static inline void
ufs_qcom_get_controller_revision(struct ufs_hba *hba,
u8 *major, u16 *minor, u16 *step)
diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c
index effa3c7a01c57..13293e83064ce 100644
--- a/drivers/ufs/host/ufshcd-pci.c
+++ b/drivers/ufs/host/ufshcd-pci.c
@@ -680,21 +680,20 @@ static const struct dev_pm_ops ufshcd_pci_pm_ops = {
};
static const struct pci_device_id ufshcd_pci_tbl[] = {
- { PCI_VENDOR_ID_REDHAT, 0x0013, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
- (kernel_ulong_t)&ufs_qemu_hba_vops },
- { PCI_VENDOR_ID_SAMSUNG, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { PCI_VDEVICE(INTEL, 0x9DFA), (kernel_ulong_t)&ufs_intel_cnl_hba_vops },
- { PCI_VDEVICE(INTEL, 0x4B41), (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
- { PCI_VDEVICE(INTEL, 0x4B43), (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
- { PCI_VDEVICE(INTEL, 0x98FA), (kernel_ulong_t)&ufs_intel_lkf_hba_vops },
- { PCI_VDEVICE(INTEL, 0x51FF), (kernel_ulong_t)&ufs_intel_adl_hba_vops },
- { PCI_VDEVICE(INTEL, 0x54FF), (kernel_ulong_t)&ufs_intel_adl_hba_vops },
- { PCI_VDEVICE(INTEL, 0x7E47), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
- { PCI_VDEVICE(INTEL, 0xA847), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
- { PCI_VDEVICE(INTEL, 0x7747), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
- { PCI_VDEVICE(INTEL, 0xE447), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
- { PCI_VDEVICE(INTEL, 0x4D47), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
- { PCI_VDEVICE(INTEL, 0xD335), (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
+ { PCI_VDEVICE(REDHAT, 0x0013), .driver_data = (kernel_ulong_t)&ufs_qemu_hba_vops },
+ { PCI_VDEVICE(SAMSUNG, 0xC00C), .driver_data = 0 },
+ { PCI_VDEVICE(INTEL, 0x9DFA), .driver_data = (kernel_ulong_t)&ufs_intel_cnl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0x4B41), .driver_data = (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0x4B43), .driver_data = (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0x98FA), .driver_data = (kernel_ulong_t)&ufs_intel_lkf_hba_vops },
+ { PCI_VDEVICE(INTEL, 0x51FF), .driver_data = (kernel_ulong_t)&ufs_intel_adl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0x54FF), .driver_data = (kernel_ulong_t)&ufs_intel_adl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0x7E47), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0xA847), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0x7747), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0xE447), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0x4D47), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
+ { PCI_VDEVICE(INTEL, 0xD335), .driver_data = (kernel_ulong_t)&ufs_intel_mtl_hba_vops },
{ } /* terminate list */
};