Commit 4f3e900b authored by Jaegeuk Kim's avatar Jaegeuk Kim Committed by Martin K. Petersen
Browse files

scsi: ufs: Clear UAC for FFU and RPMB LUNs

In order to conduct FFU or RPMB operations, UFS needs to clear UNIT
ATTENTION condition. Clear it explicitly so that we get no failures during
initialization.

Link: https://lore.kernel.org/r/20201117165839.1643377-4-jaegeuk@kernel.org


Signed-off-by: default avatarJaegeuk Kim <jaegeuk@google.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent b6645112
Loading
Loading
Loading
Loading
+64 −6
Original line number Original line Diff line number Diff line
@@ -7091,7 +7091,6 @@ static inline void ufshcd_blk_pm_runtime_init(struct scsi_device *sdev)
static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
{
{
	int ret = 0;
	int ret = 0;
	struct scsi_device *sdev_rpmb;
	struct scsi_device *sdev_boot;
	struct scsi_device *sdev_boot;


	hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
	hba->sdev_ufs_device = __scsi_add_device(hba->host, 0, 0,
@@ -7104,14 +7103,14 @@ static int ufshcd_scsi_add_wlus(struct ufs_hba *hba)
	ufshcd_blk_pm_runtime_init(hba->sdev_ufs_device);
	ufshcd_blk_pm_runtime_init(hba->sdev_ufs_device);
	scsi_device_put(hba->sdev_ufs_device);
	scsi_device_put(hba->sdev_ufs_device);


	sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
	hba->sdev_rpmb = __scsi_add_device(hba->host, 0, 0,
		ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
		ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_RPMB_WLUN), NULL);
	if (IS_ERR(sdev_rpmb)) {
	if (IS_ERR(hba->sdev_rpmb)) {
		ret = PTR_ERR(sdev_rpmb);
		ret = PTR_ERR(hba->sdev_rpmb);
		goto remove_sdev_ufs_device;
		goto remove_sdev_ufs_device;
	}
	}
	ufshcd_blk_pm_runtime_init(sdev_rpmb);
	ufshcd_blk_pm_runtime_init(hba->sdev_rpmb);
	scsi_device_put(sdev_rpmb);
	scsi_device_put(hba->sdev_rpmb);


	sdev_boot = __scsi_add_device(hba->host, 0, 0,
	sdev_boot = __scsi_add_device(hba->host, 0, 0,
		ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL);
		ufshcd_upiu_wlun_to_scsi_wlun(UFS_UPIU_BOOT_WLUN), NULL);
@@ -7635,6 +7634,63 @@ static int ufshcd_add_lus(struct ufs_hba *hba)
	return ret;
	return ret;
}
}


static int
ufshcd_send_request_sense(struct ufs_hba *hba, struct scsi_device *sdp);

static int ufshcd_clear_ua_wlun(struct ufs_hba *hba, u8 wlun)
{
	struct scsi_device *sdp;
	unsigned long flags;
	int ret = 0;

	spin_lock_irqsave(hba->host->host_lock, flags);
	if (wlun == UFS_UPIU_UFS_DEVICE_WLUN)
		sdp = hba->sdev_ufs_device;
	else if (wlun == UFS_UPIU_RPMB_WLUN)
		sdp = hba->sdev_rpmb;
	else
		BUG_ON(1);
	if (sdp) {
		ret = scsi_device_get(sdp);
		if (!ret && !scsi_device_online(sdp)) {
			ret = -ENODEV;
			scsi_device_put(sdp);
		}
	} else {
		ret = -ENODEV;
	}
	spin_unlock_irqrestore(hba->host->host_lock, flags);
	if (ret)
		goto out_err;

	ret = ufshcd_send_request_sense(hba, sdp);
	scsi_device_put(sdp);
out_err:
	if (ret)
		dev_err(hba->dev, "%s: UAC clear LU=%x ret = %d\n",
				__func__, wlun, ret);
	return ret;
}

static int ufshcd_clear_ua_wluns(struct ufs_hba *hba)
{
	int ret = 0;

	if (!hba->wlun_dev_clr_ua)
		goto out;

	ret = ufshcd_clear_ua_wlun(hba, UFS_UPIU_UFS_DEVICE_WLUN);
	if (!ret)
		ret = ufshcd_clear_ua_wlun(hba, UFS_UPIU_RPMB_WLUN);
	if (!ret)
		hba->wlun_dev_clr_ua = false;
out:
	if (ret)
		dev_err(hba->dev, "%s: Failed to clear UAC WLUNS ret = %d\n",
				__func__, ret);
	return ret;
}

/**
/**
 * ufshcd_probe_hba - probe hba to detect device and initialize
 * ufshcd_probe_hba - probe hba to detect device and initialize
 * @hba: per-adapter instance
 * @hba: per-adapter instance
@@ -7754,6 +7810,8 @@ static void ufshcd_async_scan(void *data, async_cookie_t cookie)
		pm_runtime_put_sync(hba->dev);
		pm_runtime_put_sync(hba->dev);
		ufshcd_exit_clk_scaling(hba);
		ufshcd_exit_clk_scaling(hba);
		ufshcd_hba_exit(hba);
		ufshcd_hba_exit(hba);
	} else {
		ufshcd_clear_ua_wluns(hba);
	}
	}
}
}


+1 −0
Original line number Original line Diff line number Diff line
@@ -703,6 +703,7 @@ struct ufs_hba {
	 * "UFS device" W-LU.
	 * "UFS device" W-LU.
	 */
	 */
	struct scsi_device *sdev_ufs_device;
	struct scsi_device *sdev_ufs_device;
	struct scsi_device *sdev_rpmb;


	enum ufs_dev_pwr_mode curr_dev_pwr_mode;
	enum ufs_dev_pwr_mode curr_dev_pwr_mode;
	enum uic_link_state uic_link_state;
	enum uic_link_state uic_link_state;