[PATCH V1] scsi: ufs: core: store min and max clk freq from OPP table

Nitin Rawat
2023-12-06T05:41:08Z

OPP support will make use of OPP table in device tree and removes
freq-table-hz property from device tree.

With OPP enabled in devicetree, clki->min_freq and clki->maxfreq
currently is not getting updated and the value is set to 0.

Soc vendors like qcom, mediatek uses clki->minfreq and clki->maxfreq
in vendor specific file. These frequencies values are used to update
vendor specific configurations. Since the value is 0, it is causing
functional issue.

Add code to store the min and max ufs clk frequency from OPP table.

Fixes: 72208ebe181e ("scsi: ufs: core: Add support for parsing OPP")
Co-developed-by: Manish Pandey <quic_mapa@quicinc.com>
Signed-off-by: Manish Pandey <quic_mapa@quicinc.com>
Signed-off-by: Nitin Rawat <quic_nitirawa@quicinc.com>
---
drivers/ufs/host/ufshcd-pltfrm.c | 56 ++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)

diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c
index da2558e274b4..12fa6f7d6a97 100644
--- a/drivers/ufs/host/ufshcd-pltfrm.c
+++ b/drivers/ufs/host/ufshcd-pltfrm.c
@@ -13,6 +13,7 @@
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
+#include <linux/clk.h>

#include <ufs/ufshcd.h>
#include "ufshcd-pltfrm.h"
@@ -213,6 +214,55 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba)
}
}

+/**
+ * ufshcd_config_min_max_clk_freq - update min and max freq
+ * @hba: per adapter instance
+ *
+ * This function store min and max freq for all the clocks.
+ *
+ * Returns 0 for success and non-zero for failure
+ */
+static int ufshcd_config_min_max_clk_freq(struct ufs_hba *hba)
+{
+ struct list_head *head = &hba->clk_list_head;
+ struct dev_pm_opp *opp;
+ struct ufs_clk_info *clki;
+ unsigned long freq;
+ u8 idx = 0;
+ int ret;
+
+ list_for_each_entry(clki, head, list) {
+ if (!clki->name)
+ continue;
+
+ clki->clk = devm_clk_get(hba->dev, clki->name);
+ if (!IS_ERR_OR_NULL(clki->clk)) {
+ /* Find Max Freq */
+ freq = ULONG_MAX;
+ opp = dev_pm_opp_find_freq_floor_indexed(hba->dev, &freq, idx);
+ if (IS_ERR(opp)) {
+ dev_err(hba->dev, "failed to find dev_pm_opp\n");
+ ret = PTR_ERR(opp);
+ return ret;
+ }
+ clki->max_freq = dev_pm_opp_get_freq_indexed(opp, idx);
+
+ /* Find Min Freq */
+ freq = 0;
+ opp = dev_pm_opp_find_freq_ceil_indexed(hba->dev, &freq, idx);
+ if (IS_ERR(opp)) {
+ dev_err(hba->dev, "failed to find dev_pm_opp\n");
+ ret = PTR_ERR(opp);
+ return ret;
+ }
+ clki->min_freq = dev_pm_opp_get_freq_indexed(opp, idx);
+ idx++;
+ }
+ }
+
+ return 0;
+}
+
static int ufshcd_parse_operating_points(struct ufs_hba *hba)
{
struct device *dev = hba->dev;
@@ -279,6 +329,12 @@ static int ufshcd_parse_operating_points(struct ufs_hba *hba)
return ret;
}

+ ret = ufshcd_config_min_max_clk_freq(hba);
+ if (ret) {
+ dev_err(dev, "Failed to get min max freq: %d\n", ret);
+ return ret;
+ }
+
hba->use_pm_opp = true;

return 0;
--
2.17.1