Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d86c0014 authored by Can Guo's avatar Can Guo
Browse files

scsi: ufs: add PM notifier support for UFS card



If the removable UFS card is removed or inserted when UFS host is doing
system PM, the card detect routine may run into race condition with host
suspend/resume.
In order to avoid these race conditions, this change adds the PM notifier
support for UFS card to monitor system PM events. Before enter suspend,
unregister the UFS card detect notifier from extcon and cancel card detect
work if any. After exit suspend, register the card detect notifier back to
extcon and sync extcon state to schedule card detect work.

Change-Id: I504d793e7c7809454a597ec3bdc91b6b217bb180
Signed-off-by: default avatarCan Guo <cang@codeaurora.org>
parent 03b45949
Loading
Loading
Loading
Loading
+69 −1
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
#include <linux/nls.h>
#include <linux/of.h>
#include <linux/bitfield.h>
#include <linux/suspend.h>
#include "ufshcd.h"
#include "ufs_quirks.h"
#include "unipro.h"
@@ -394,6 +395,20 @@ static bool ufshcd_is_card_present(struct ufs_hba *hba)
	return true;
}

static int ufshcd_card_get_extcon_state(struct ufs_hba *hba)
{
	int ret;

	if (!hba->extcon)
		return -EINVAL;

	ret = extcon_get_state(hba->extcon, EXTCON_MECHANICAL);
	if (ret < 0)
		dev_err(hba->dev, "%s: Failed to check card Extcon state, ret=%d\n",
				 __func__, ret);
	return ret;
}

static inline enum ufs_pm_level
ufs_get_desired_pm_lvl_for_dev_link_state(enum ufs_dev_pwr_mode dev_state,
					enum uic_link_state link_state)
@@ -495,6 +510,8 @@ static int ufshcd_config_vreg(struct device *dev,
				struct ufs_vreg *vreg, bool on);
static int ufshcd_enable_vreg(struct device *dev, struct ufs_vreg *vreg);
static int ufshcd_disable_vreg(struct device *dev, struct ufs_vreg *vreg);
static void ufshcd_register_pm_notifier(struct ufs_hba *hba);
static void ufshcd_unregister_pm_notifier(struct ufs_hba *hba);

#if IS_ENABLED(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND)
static struct devfreq_simple_ondemand_data ufshcd_ondemand_data = {
@@ -10087,6 +10104,54 @@ static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba)
		ufshcd_setup_hba_vreg(hba, true);
}

#ifdef CONFIG_PM_SLEEP
static int ufshcd_pm_notify(struct notifier_block *notify_block,
			 unsigned long mode, void *unused)
{
	struct ufs_hba *hba = container_of(
		notify_block, struct ufs_hba, pm_notify);
	int ret = 0;

	if (!hba->extcon)
		return ret;

	switch (mode) {
	case PM_SUSPEND_PREPARE:
		ret = ufshcd_extcon_unregister(hba);
		if (ret)
			break;
		cancel_work_sync(&hba->card_detect_work);
		break;
	case PM_POST_SUSPEND:
		ret = ufshcd_extcon_register(hba);
		if (ret)
			break;
		extcon_sync(hba->extcon, EXTCON_MECHANICAL);
	}

	return ret;
}

static void ufshcd_register_pm_notifier(struct ufs_hba *hba)
{
	hba->pm_notify.notifier_call = ufshcd_pm_notify;
	register_pm_notifier(&hba->pm_notify);
}

static void ufshcd_unregister_pm_notifier(struct ufs_hba *hba)
{
	unregister_pm_notifier(&hba->pm_notify);
}
#else
static void ufshcd_register_pm_notifier(struct ufs_hba *hba)
{
}

static void ufshcd_unregister_pm_notifier(struct ufs_hba *hba)
{
}
#endif /* CONFIG_PM_SLEEP */

/**
 * ufshcd_suspend - helper function for suspend operations
 * @hba: per adapter instance
@@ -10292,7 +10357,8 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)

	if (hba->extcon &&
	    (ufshcd_is_card_offline(hba) ||
	     (ufshcd_is_card_online(hba) && !hba->sdev_ufs_device)))
	     (ufshcd_is_card_online(hba) && !hba->sdev_ufs_device) ||
	     !ufshcd_card_get_extcon_state(hba)))
		goto skip_dev_ops;

	if (ufshcd_is_link_hibern8(hba)) {
@@ -10631,6 +10697,7 @@ void ufshcd_remove(struct ufs_hba *hba)
	}
	ufshcd_hba_exit(hba);
	ufsdbg_remove_debugfs(hba);
	ufshcd_unregister_pm_notifier(hba);
}
EXPORT_SYMBOL_GPL(ufshcd_remove);

@@ -10908,6 +10975,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)

	ufs_sysfs_add_nodes(hba->dev);

	ufshcd_register_pm_notifier(hba);
	return 0;

out_remove_scsi_host:
+3 −1
Original line number Diff line number Diff line
@@ -58,7 +58,7 @@
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
#include <linux/reset.h>
#include <linux/extcon.h>
#include <linux/extcon-provider.h>
#include <linux/devfreq.h>
#include "unipro.h"

@@ -769,6 +769,7 @@ enum ufshcd_card_state {
 * @card_detect_work: work to exectute the card detect function
 * @card_state: card state event, enum ufshcd_card_state defines possible states
 * @card_removal_in_progress: to track card removal progress
 * @pm_notify: used to register for PM events
 * @vreg_info: UFS device voltage regulator information
 * @clk_list_head: UFS host controller clocks list node head
 * @pwr_info: holds current power mode
@@ -1007,6 +1008,7 @@ struct ufs_hba {
	struct work_struct card_detect_work;
	atomic_t card_state;
	int card_removal_in_progress;
	struct notifier_block pm_notify;

	struct ufs_pa_layer_attr pwr_info;
	struct ufs_pwr_mode_info max_pwr_info;