Loading drivers/scsi/ufs/ufshcd.c +88 −22 Original line number Diff line number Diff line Loading @@ -281,6 +281,8 @@ static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on); static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba); 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 inline void ufshcd_enable_irq(struct ufs_hba *hba) { Loading Loading @@ -761,8 +763,8 @@ static void ufshcd_ungate_work(struct work_struct *work) hba->clk_gating.is_suspended = false; } unblock_reqs: if (ufshcd_is_clkscaling_enabled(hba)) devfreq_resume_device(hba->devfreq); if (hba->clk_scaling.is_allowed) ufshcd_resume_clkscaling(hba); scsi_unblock_requests(hba->host); } Loading Loading @@ -867,10 +869,7 @@ static void ufshcd_gate_work(struct work_struct *work) ufshcd_set_link_hibern8(hba); } if (ufshcd_is_clkscaling_enabled(hba)) { devfreq_suspend_device(hba->devfreq); hba->clk_scaling.window_start_t = 0; } ufshcd_suspend_clkscaling(hba); if (!ufshcd_is_link_active(hba)) ufshcd_setup_clocks(hba, false); Loading Loading @@ -1028,7 +1027,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) /* Must be called with host lock acquired */ static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba) { if (!ufshcd_is_clkscaling_enabled(hba)) if (!ufshcd_is_clkscaling_supported(hba)) return; if (!hba->clk_scaling.is_busy_started) { Loading @@ -1041,7 +1040,7 @@ static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba) { struct ufs_clk_scaling *scaling = &hba->clk_scaling; if (!ufshcd_is_clkscaling_enabled(hba)) if (!ufshcd_is_clkscaling_supported(hba)) return; if (!hba->outstanding_reqs && scaling->is_busy_started) { Loading Loading @@ -4792,8 +4791,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) pm_runtime_put_sync(hba->dev); } /* Resume devfreq after UFS device is detected */ if (ufshcd_is_clkscaling_enabled(hba)) devfreq_resume_device(hba->devfreq); if (ufshcd_is_clkscaling_supported(hba)) { ufshcd_resume_clkscaling(hba); hba->clk_scaling.is_allowed = true; } if (!hba->is_init_prefetch) hba->is_init_prefetch = true; out: Loading Loading @@ -5737,10 +5738,7 @@ disable_clks: * for pending clock scaling work to be done before clocks are * turned off. */ if (ufshcd_is_clkscaling_enabled(hba)) { devfreq_suspend_device(hba->devfreq); hba->clk_scaling.window_start_t = 0; } ufshcd_suspend_clkscaling(hba); /* * Call vendor specific suspend callback. As these callbacks may access * vendor specific host controller register space call them before the Loading Loading @@ -5866,8 +5864,8 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) ufshcd_disable_auto_bkops(hba); hba->clk_gating.is_suspended = false; if (ufshcd_is_clkscaling_enabled(hba)) devfreq_resume_device(hba->devfreq); if (hba->clk_scaling.is_allowed) ufshcd_resume_clkscaling(hba); /* Schedule clock gating in case of no access to UFS device yet */ ufshcd_release(hba); Loading Loading @@ -6087,8 +6085,10 @@ void ufshcd_remove(struct ufs_hba *hba) ufshcd_hba_stop(hba, true); ufshcd_exit_clk_gating(hba); if (ufshcd_is_clkscaling_enabled(hba)) if (ufshcd_is_clkscaling_supported(hba)) { device_remove_file(hba->dev, &hba->clk_scaling.enable_attr); devfreq_remove_device(hba->devfreq); } ufshcd_hba_exit(hba); } EXPORT_SYMBOL_GPL(ufshcd_remove); Loading Loading @@ -6216,13 +6216,69 @@ out: return ret; } static void ufshcd_suspend_clkscaling(struct ufs_hba *hba) { if (!ufshcd_is_clkscaling_supported(hba)) return; devfreq_suspend_device(hba->devfreq); hba->clk_scaling.window_start_t = 0; } static void ufshcd_resume_clkscaling(struct ufs_hba *hba) { 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 int ufshcd_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) { int err = 0; struct ufs_hba *hba = dev_get_drvdata(dev); if (!ufshcd_is_clkscaling_enabled(hba)) if (!ufshcd_is_clkscaling_supported(hba)) return -EINVAL; if (*freq == UINT_MAX) Loading @@ -6240,7 +6296,7 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev, struct ufs_clk_scaling *scaling = &hba->clk_scaling; unsigned long flags; if (!ufshcd_is_clkscaling_enabled(hba)) if (!ufshcd_is_clkscaling_supported(hba)) return -EINVAL; memset(stat, 0, sizeof(*stat)); Loading Loading @@ -6292,6 +6348,16 @@ static struct devfreq_dev_profile ufs_devfreq_profile = { .num_governor_data = ARRAY_SIZE(ufshcd_governors), #endif }; 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 = S_IRUGO | S_IWUSR; 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 Loading Loading @@ -6404,7 +6470,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) goto out_remove_scsi_host; } if (ufshcd_is_clkscaling_enabled(hba)) { if (ufshcd_is_clkscaling_supported(hba)) { hba->devfreq = devfreq_add_device(dev, &ufs_devfreq_profile, "simple_ondemand", NULL); if (IS_ERR(hba->devfreq)) { Loading @@ -6413,8 +6479,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) goto out_remove_scsi_host; } /* Suspend devfreq until the UFS device is detected */ devfreq_suspend_device(hba->devfreq); hba->clk_scaling.window_start_t = 0; ufshcd_suspend_clkscaling(hba); ufshcd_clkscaling_init_sysfs(hba); } /* Loading drivers/scsi/ufs/ufshcd.h +3 −1 Original line number Diff line number Diff line Loading @@ -372,6 +372,8 @@ struct ufs_clk_scaling { bool is_busy_started; unsigned long tot_busy_t; unsigned long window_start_t; struct device_attribute enable_attr; bool is_allowed; }; /** Loading Loading @@ -582,7 +584,7 @@ static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba) { 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; } Loading Loading
drivers/scsi/ufs/ufshcd.c +88 −22 Original line number Diff line number Diff line Loading @@ -281,6 +281,8 @@ static int ufshcd_setup_clocks(struct ufs_hba *hba, bool on); static int ufshcd_uic_hibern8_exit(struct ufs_hba *hba); 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 inline void ufshcd_enable_irq(struct ufs_hba *hba) { Loading Loading @@ -761,8 +763,8 @@ static void ufshcd_ungate_work(struct work_struct *work) hba->clk_gating.is_suspended = false; } unblock_reqs: if (ufshcd_is_clkscaling_enabled(hba)) devfreq_resume_device(hba->devfreq); if (hba->clk_scaling.is_allowed) ufshcd_resume_clkscaling(hba); scsi_unblock_requests(hba->host); } Loading Loading @@ -867,10 +869,7 @@ static void ufshcd_gate_work(struct work_struct *work) ufshcd_set_link_hibern8(hba); } if (ufshcd_is_clkscaling_enabled(hba)) { devfreq_suspend_device(hba->devfreq); hba->clk_scaling.window_start_t = 0; } ufshcd_suspend_clkscaling(hba); if (!ufshcd_is_link_active(hba)) ufshcd_setup_clocks(hba, false); Loading Loading @@ -1028,7 +1027,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) /* Must be called with host lock acquired */ static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba) { if (!ufshcd_is_clkscaling_enabled(hba)) if (!ufshcd_is_clkscaling_supported(hba)) return; if (!hba->clk_scaling.is_busy_started) { Loading @@ -1041,7 +1040,7 @@ static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba) { struct ufs_clk_scaling *scaling = &hba->clk_scaling; if (!ufshcd_is_clkscaling_enabled(hba)) if (!ufshcd_is_clkscaling_supported(hba)) return; if (!hba->outstanding_reqs && scaling->is_busy_started) { Loading Loading @@ -4792,8 +4791,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) pm_runtime_put_sync(hba->dev); } /* Resume devfreq after UFS device is detected */ if (ufshcd_is_clkscaling_enabled(hba)) devfreq_resume_device(hba->devfreq); if (ufshcd_is_clkscaling_supported(hba)) { ufshcd_resume_clkscaling(hba); hba->clk_scaling.is_allowed = true; } if (!hba->is_init_prefetch) hba->is_init_prefetch = true; out: Loading Loading @@ -5737,10 +5738,7 @@ disable_clks: * for pending clock scaling work to be done before clocks are * turned off. */ if (ufshcd_is_clkscaling_enabled(hba)) { devfreq_suspend_device(hba->devfreq); hba->clk_scaling.window_start_t = 0; } ufshcd_suspend_clkscaling(hba); /* * Call vendor specific suspend callback. As these callbacks may access * vendor specific host controller register space call them before the Loading Loading @@ -5866,8 +5864,8 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) ufshcd_disable_auto_bkops(hba); hba->clk_gating.is_suspended = false; if (ufshcd_is_clkscaling_enabled(hba)) devfreq_resume_device(hba->devfreq); if (hba->clk_scaling.is_allowed) ufshcd_resume_clkscaling(hba); /* Schedule clock gating in case of no access to UFS device yet */ ufshcd_release(hba); Loading Loading @@ -6087,8 +6085,10 @@ void ufshcd_remove(struct ufs_hba *hba) ufshcd_hba_stop(hba, true); ufshcd_exit_clk_gating(hba); if (ufshcd_is_clkscaling_enabled(hba)) if (ufshcd_is_clkscaling_supported(hba)) { device_remove_file(hba->dev, &hba->clk_scaling.enable_attr); devfreq_remove_device(hba->devfreq); } ufshcd_hba_exit(hba); } EXPORT_SYMBOL_GPL(ufshcd_remove); Loading Loading @@ -6216,13 +6216,69 @@ out: return ret; } static void ufshcd_suspend_clkscaling(struct ufs_hba *hba) { if (!ufshcd_is_clkscaling_supported(hba)) return; devfreq_suspend_device(hba->devfreq); hba->clk_scaling.window_start_t = 0; } static void ufshcd_resume_clkscaling(struct ufs_hba *hba) { 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 int ufshcd_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) { int err = 0; struct ufs_hba *hba = dev_get_drvdata(dev); if (!ufshcd_is_clkscaling_enabled(hba)) if (!ufshcd_is_clkscaling_supported(hba)) return -EINVAL; if (*freq == UINT_MAX) Loading @@ -6240,7 +6296,7 @@ static int ufshcd_devfreq_get_dev_status(struct device *dev, struct ufs_clk_scaling *scaling = &hba->clk_scaling; unsigned long flags; if (!ufshcd_is_clkscaling_enabled(hba)) if (!ufshcd_is_clkscaling_supported(hba)) return -EINVAL; memset(stat, 0, sizeof(*stat)); Loading Loading @@ -6292,6 +6348,16 @@ static struct devfreq_dev_profile ufs_devfreq_profile = { .num_governor_data = ARRAY_SIZE(ufshcd_governors), #endif }; 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 = S_IRUGO | S_IWUSR; 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 Loading Loading @@ -6404,7 +6470,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) goto out_remove_scsi_host; } if (ufshcd_is_clkscaling_enabled(hba)) { if (ufshcd_is_clkscaling_supported(hba)) { hba->devfreq = devfreq_add_device(dev, &ufs_devfreq_profile, "simple_ondemand", NULL); if (IS_ERR(hba->devfreq)) { Loading @@ -6413,8 +6479,8 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) goto out_remove_scsi_host; } /* Suspend devfreq until the UFS device is detected */ devfreq_suspend_device(hba->devfreq); hba->clk_scaling.window_start_t = 0; ufshcd_suspend_clkscaling(hba); ufshcd_clkscaling_init_sysfs(hba); } /* Loading
drivers/scsi/ufs/ufshcd.h +3 −1 Original line number Diff line number Diff line Loading @@ -372,6 +372,8 @@ struct ufs_clk_scaling { bool is_busy_started; unsigned long tot_busy_t; unsigned long window_start_t; struct device_attribute enable_attr; bool is_allowed; }; /** Loading Loading @@ -582,7 +584,7 @@ static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba) { 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; } Loading