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

Commit fcb0c4b0 authored by Sahitya Tummala's avatar Sahitya Tummala Committed by Martin K. Petersen
Browse files

scsi: ufs: Add sysfs node to dynamically control clock scaling



Provide an option to enable/disable clock scaling during runtime.
Write 1/0 to "clkscale_enable" sysfs node to enable/disable clock
scaling.

Signed-off-by: default avatarSahitya Tummala <stummala@codeaurora.org>
Signed-off-by: default avatarSubhash Jadavani <subhashj@codeaurora.org>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent b427411a
Loading
Loading
Loading
Loading
+79 −16
Original line number Original line Diff line number Diff line
@@ -230,6 +230,9 @@ static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba);
static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
static int ufshcd_uic_hibern8_enter(struct ufs_hba *hba);
static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
static inline void ufshcd_add_delay_before_dme_cmd(struct ufs_hba *hba);
static int ufshcd_host_reset_and_restore(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 int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up);
static irqreturn_t ufshcd_intr(int irq, void *__hba);
static irqreturn_t ufshcd_intr(int irq, void *__hba);
static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
static int ufshcd_config_pwr_mode(struct ufs_hba *hba,
		struct ufs_pa_layer_attr *desired_pwr_mode);
		struct ufs_pa_layer_attr *desired_pwr_mode);
@@ -713,18 +716,60 @@ static bool ufshcd_is_unipro_pa_params_tuning_req(struct ufs_hba *hba)


static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
static void ufshcd_suspend_clkscaling(struct ufs_hba *hba)
{
{
	if (ufshcd_is_clkscaling_enabled(hba)) {
	if (!ufshcd_is_clkscaling_supported(hba))
		return;

	devfreq_suspend_device(hba->devfreq);
	devfreq_suspend_device(hba->devfreq);
	hba->clk_scaling.window_start_t = 0;
	hba->clk_scaling.window_start_t = 0;
}
}
}


static void ufshcd_resume_clkscaling(struct ufs_hba *hba)
static void ufshcd_resume_clkscaling(struct ufs_hba *hba)
{
{
	if (ufshcd_is_clkscaling_enabled(hba))
	devfreq_resume_device(hba->devfreq);
	devfreq_resume_device(hba->devfreq);
}
}


static ssize_t ufshcd_clkscale_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->clk_scaling.is_allowed);
}

static ssize_t ufshcd_clkscale_enable_store(struct device *dev,
		struct device_attribute *attr, const char *buf, size_t count)
{
	struct ufs_hba *hba = dev_get_drvdata(dev);
	u32 value;
	int err;

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

	value = !!value;
	if (value == hba->clk_scaling.is_allowed)
		goto out;

	pm_runtime_get_sync(hba->dev);
	ufshcd_hold(hba, false);

	if (value) {
		ufshcd_resume_clkscaling(hba);
	} else {
		ufshcd_suspend_clkscaling(hba);
		err = ufshcd_scale_clks(hba, true);
		if (err)
			dev_err(hba->dev, "%s: failed to scale clocks up %d\n",
					__func__, err);
	}
	hba->clk_scaling.is_allowed = value;

	ufshcd_release(hba);
	pm_runtime_put_sync(hba->dev);
out:
	return count;
}

static void ufshcd_ungate_work(struct work_struct *work)
static void ufshcd_ungate_work(struct work_struct *work)
{
{
	int ret;
	int ret;
@@ -758,6 +803,7 @@ static void ufshcd_ungate_work(struct work_struct *work)
		hba->clk_gating.is_suspended = false;
		hba->clk_gating.is_suspended = false;
	}
	}
unblock_reqs:
unblock_reqs:
	if (hba->clk_scaling.is_allowed)
		ufshcd_resume_clkscaling(hba);
		ufshcd_resume_clkscaling(hba);
	scsi_unblock_requests(hba->host);
	scsi_unblock_requests(hba->host);
}
}
@@ -1046,7 +1092,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba)
/* Must be called with host lock acquired */
/* Must be called with host lock acquired */
static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba)
static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba)
{
{
	if (!ufshcd_is_clkscaling_enabled(hba))
	if (!ufshcd_is_clkscaling_supported(hba))
		return;
		return;


	if (!hba->clk_scaling.is_busy_started) {
	if (!hba->clk_scaling.is_busy_started) {
@@ -1059,7 +1105,7 @@ static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba)
{
{
	struct ufs_clk_scaling *scaling = &hba->clk_scaling;
	struct ufs_clk_scaling *scaling = &hba->clk_scaling;


	if (!ufshcd_is_clkscaling_enabled(hba))
	if (!ufshcd_is_clkscaling_supported(hba))
		return;
		return;


	if (!hba->outstanding_reqs && scaling->is_busy_started) {
	if (!hba->outstanding_reqs && scaling->is_busy_started) {
@@ -5526,11 +5572,14 @@ static int ufshcd_probe_hba(struct ufs_hba *hba)
		pm_runtime_put_sync(hba->dev);
		pm_runtime_put_sync(hba->dev);
	}
	}


	if (!hba->is_init_prefetch)
		hba->is_init_prefetch = true;

	/* Resume devfreq after UFS device is detected */
	/* Resume devfreq after UFS device is detected */
	if (ufshcd_is_clkscaling_supported(hba)) {
		ufshcd_resume_clkscaling(hba);
		ufshcd_resume_clkscaling(hba);
		hba->clk_scaling.is_allowed = true;
	}

	if (!hba->is_init_prefetch)
		hba->is_init_prefetch = true;


out:
out:
	/*
	/*
@@ -6484,6 +6533,7 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op)
	ufshcd_urgent_bkops(hba);
	ufshcd_urgent_bkops(hba);
	hba->clk_gating.is_suspended = false;
	hba->clk_gating.is_suspended = false;


	if (hba->clk_scaling.is_allowed)
		ufshcd_resume_clkscaling(hba);
		ufshcd_resume_clkscaling(hba);


	/* Schedule clock gating in case of no access to UFS device yet */
	/* Schedule clock gating in case of no access to UFS device yet */
@@ -6702,6 +6752,8 @@ void ufshcd_remove(struct ufs_hba *hba)
	ufshcd_hba_stop(hba, true);
	ufshcd_hba_stop(hba, true);


	ufshcd_exit_clk_gating(hba);
	ufshcd_exit_clk_gating(hba);
	if (ufshcd_is_clkscaling_supported(hba))
		device_remove_file(hba->dev, &hba->clk_scaling.enable_attr);
	ufshcd_hba_exit(hba);
	ufshcd_hba_exit(hba);
}
}
EXPORT_SYMBOL_GPL(ufshcd_remove);
EXPORT_SYMBOL_GPL(ufshcd_remove);
@@ -6835,7 +6887,7 @@ static int ufshcd_devfreq_target(struct device *dev,
	bool release_clk_hold = false;
	bool release_clk_hold = false;
	unsigned long irq_flags;
	unsigned long irq_flags;


	if (!ufshcd_is_clkscaling_enabled(hba))
	if (!ufshcd_is_clkscaling_supported(hba))
		return -EINVAL;
		return -EINVAL;


	spin_lock_irqsave(hba->host->host_lock, irq_flags);
	spin_lock_irqsave(hba->host->host_lock, irq_flags);
@@ -6883,7 +6935,7 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev,
	struct ufs_clk_scaling *scaling = &hba->clk_scaling;
	struct ufs_clk_scaling *scaling = &hba->clk_scaling;
	unsigned long flags;
	unsigned long flags;


	if (!ufshcd_is_clkscaling_enabled(hba))
	if (!ufshcd_is_clkscaling_supported(hba))
		return -EINVAL;
		return -EINVAL;


	memset(stat, 0, sizeof(*stat));
	memset(stat, 0, sizeof(*stat));
@@ -6919,6 +6971,16 @@ static struct devfreq_dev_profile ufs_devfreq_profile = {
	.target		= ufshcd_devfreq_target,
	.target		= ufshcd_devfreq_target,
	.get_dev_status	= ufshcd_devfreq_get_dev_status,
	.get_dev_status	= ufshcd_devfreq_get_dev_status,
};
};
static void ufshcd_clkscaling_init_sysfs(struct ufs_hba *hba)
{
	hba->clk_scaling.enable_attr.show = ufshcd_clkscale_enable_show;
	hba->clk_scaling.enable_attr.store = ufshcd_clkscale_enable_store;
	sysfs_attr_init(&hba->clk_scaling.enable_attr.attr);
	hba->clk_scaling.enable_attr.attr.name = "clkscale_enable";
	hba->clk_scaling.enable_attr.attr.mode = 0644;
	if (device_create_file(hba->dev, &hba->clk_scaling.enable_attr))
		dev_err(hba->dev, "Failed to create sysfs for clkscale_enable\n");
}


/**
/**
 * ufshcd_init - Driver initialization routine
 * ufshcd_init - Driver initialization routine
@@ -7045,7 +7107,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
		goto out_remove_scsi_host;
		goto out_remove_scsi_host;
	}
	}


	if (ufshcd_is_clkscaling_enabled(hba)) {
	if (ufshcd_is_clkscaling_supported(hba)) {
		hba->devfreq = devm_devfreq_add_device(dev, &ufs_devfreq_profile,
		hba->devfreq = devm_devfreq_add_device(dev, &ufs_devfreq_profile,
						   "simple_ondemand", NULL);
						   "simple_ondemand", NULL);
		if (IS_ERR(hba->devfreq)) {
		if (IS_ERR(hba->devfreq)) {
@@ -7056,6 +7118,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq)
		}
		}
		/* Suspend devfreq until the UFS device is detected */
		/* Suspend devfreq until the UFS device is detected */
		ufshcd_suspend_clkscaling(hba);
		ufshcd_suspend_clkscaling(hba);
		ufshcd_clkscaling_init_sysfs(hba);
	}
	}


	/* Hold auto suspend until async scan completes */
	/* Hold auto suspend until async scan completes */
+3 −1
Original line number Original line Diff line number Diff line
@@ -342,6 +342,8 @@ struct ufs_clk_scaling {
	bool is_busy_started;
	bool is_busy_started;
	unsigned long  tot_busy_t;
	unsigned long  tot_busy_t;
	unsigned long window_start_t;
	unsigned long window_start_t;
	struct device_attribute enable_attr;
	bool is_allowed;
};
};


/**
/**
@@ -580,7 +582,7 @@ static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba)
{
{
	return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
	return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
}
}
static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba)
static inline int ufshcd_is_clkscaling_supported(struct ufs_hba *hba)
{
{
	return hba->caps & UFSHCD_CAP_CLK_SCALING;
	return hba->caps & UFSHCD_CAP_CLK_SCALING;
}
}