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

Commit a335ba76 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "scsi: ufs: Add sysfs node to dynamically control hibern8 on idle"

parents 05e3251a e1e5802c
Loading
Loading
Loading
Loading
+92 −3
Original line number Diff line number Diff line
@@ -293,6 +293,7 @@ static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
static int ufshcd_host_reset_and_restore(struct ufs_hba *hba);
static void ufshcd_resume_clkscaling(struct ufs_hba *hba);
static void ufshcd_suspend_clkscaling(struct ufs_hba *hba);
static void ufshcd_release_all(struct ufs_hba *hba);

static inline void ufshcd_enable_irq(struct ufs_hba *hba)
{
@@ -956,7 +957,8 @@ static void ufshcd_gate_work(struct work_struct *work)

	spin_unlock_irqrestore(hba->host->host_lock, flags);

	if (ufshcd_is_hibern8_on_idle_allowed(hba))
	if (ufshcd_is_hibern8_on_idle_allowed(hba) &&
	    hba->hibern8_on_idle.is_enabled)
		/*
		 * Hibern8 enter work (on Idle) needs clocks to be ON hence
		 * make sure that it is flushed before turning off the clocks.
@@ -1338,6 +1340,70 @@ unblock_reqs:
	scsi_unblock_requests(hba->host);
}

static ssize_t ufshcd_hibern8_on_idle_delay_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct ufs_hba *hba = dev_get_drvdata(dev);

	return snprintf(buf, PAGE_SIZE, "%lu\n", hba->hibern8_on_idle.delay_ms);
}

static ssize_t ufshcd_hibern8_on_idle_delay_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct ufs_hba *hba = dev_get_drvdata(dev);
	unsigned long flags, value;

	if (kstrtoul(buf, 0, &value))
		return -EINVAL;

	spin_lock_irqsave(hba->host->host_lock, flags);
	hba->hibern8_on_idle.delay_ms = value;
	spin_unlock_irqrestore(hba->host->host_lock, flags);
	return count;
}

static ssize_t ufshcd_hibern8_on_idle_enable_show(struct device *dev,
		struct device_attribute *attr, char *buf)
{
	struct ufs_hba *hba = dev_get_drvdata(dev);

	return snprintf(buf, PAGE_SIZE, "%d\n",
			hba->hibern8_on_idle.is_enabled);
}

static ssize_t ufshcd_hibern8_on_idle_enable_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct ufs_hba *hba = dev_get_drvdata(dev);
	unsigned long flags;
	u32 value;

	if (kstrtou32(buf, 0, &value))
		return -EINVAL;

	value = !!value;
	if (value == hba->hibern8_on_idle.is_enabled)
		goto out;

	if (value) {
		/*
		 * As clock gating work would wait for the hibern8 enter work
		 * to finish, clocks would remain on during hibern8 enter work.
		 */
		ufshcd_hold(hba, false);
		ufshcd_release_all(hba);
	} else {
		spin_lock_irqsave(hba->host->host_lock, flags);
		hba->hibern8_on_idle.active_reqs++;
		spin_unlock_irqrestore(hba->host->host_lock, flags);
	}

	hba->hibern8_on_idle.is_enabled = value;
out:
	return count;
}

static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba)
{
	if (!ufshcd_is_hibern8_on_idle_allowed(hba))
@@ -1349,13 +1415,35 @@ static void ufshcd_init_hibern8_on_idle(struct ufs_hba *hba)

	hba->hibern8_on_idle.delay_ms = 10;
	hba->hibern8_on_idle.state = HIBERN8_EXITED;
	hba->hibern8_on_idle.is_enabled = true;

	hba->hibern8_on_idle.delay_attr.show =
					ufshcd_hibern8_on_idle_delay_show;
	hba->hibern8_on_idle.delay_attr.store =
					ufshcd_hibern8_on_idle_delay_store;
	sysfs_attr_init(&hba->hibern8_on_idle.delay_attr.attr);
	hba->hibern8_on_idle.delay_attr.attr.name = "hibern8_on_idle_delay_ms";
	hba->hibern8_on_idle.delay_attr.attr.mode = S_IRUGO | S_IWUSR;
	if (device_create_file(hba->dev, &hba->hibern8_on_idle.delay_attr))
		dev_err(hba->dev, "Failed to create sysfs for hibern8_on_idle_delay\n");

	hba->hibern8_on_idle.enable_attr.show =
					ufshcd_hibern8_on_idle_enable_show;
	hba->hibern8_on_idle.enable_attr.store =
					ufshcd_hibern8_on_idle_enable_store;
	sysfs_attr_init(&hba->hibern8_on_idle.enable_attr.attr);
	hba->hibern8_on_idle.enable_attr.attr.name = "hibern8_on_idle_enable";
	hba->hibern8_on_idle.enable_attr.attr.mode = S_IRUGO | S_IWUSR;
	if (device_create_file(hba->dev, &hba->hibern8_on_idle.enable_attr))
		dev_err(hba->dev, "Failed to create sysfs for hibern8_on_idle_enable\n");
}

static void ufshcd_exit_hibern8_on_idle(struct ufs_hba *hba)
{
	if (!ufshcd_is_hibern8_on_idle_allowed(hba))
		return;
	/* Don't have anything to do for now */
	device_remove_file(hba->dev, &hba->hibern8_on_idle.delay_attr);
	device_remove_file(hba->dev, &hba->hibern8_on_idle.enable_attr);
}

static void ufshcd_hold_all(struct ufs_hba *hba)
@@ -6278,7 +6366,8 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
	 * If we can't transition into any of the low power modes
	 * just gate the clocks.
	 */
	WARN_ON(hba->hibern8_on_idle.active_reqs);
	WARN_ON(hba->hibern8_on_idle.is_enabled &&
		hba->hibern8_on_idle.active_reqs);
	ufshcd_hold_all(hba);
	hba->clk_gating.is_suspended = true;
	hba->hibern8_on_idle.is_suspended = true;
+6 −0
Original line number Diff line number Diff line
@@ -389,6 +389,9 @@ enum ufshcd_hibern8_on_idle_state {
 * during suspend/resume
 * @active_reqs: number of requests that are pending and should be waited for
 * completion before scheduling delayed "enter_work".
 * @delay_attr: sysfs attribute to control delay_attr
 * @enable_attr: sysfs attribute to enable/disable hibern8 on idle
 * @is_enabled: Indicates the current status of hibern8
 */
struct ufs_hibern8_on_idle {
	struct delayed_work enter_work;
@@ -397,6 +400,9 @@ struct ufs_hibern8_on_idle {
	unsigned long delay_ms;
	bool is_suspended;
	int active_reqs;
	struct device_attribute delay_attr;
	struct device_attribute enable_attr;
	bool is_enabled;
};

struct ufs_clk_scaling {