diff options
| -rw-r--r-- | drivers/scsi/ufs/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.c | 178 | ||||
| -rw-r--r-- | drivers/scsi/ufs/ufshcd.h | 20 |
3 files changed, 199 insertions, 1 deletions
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig index f07f90179bbc..6e07b2afddeb 100644 --- a/drivers/scsi/ufs/Kconfig +++ b/drivers/scsi/ufs/Kconfig | |||
| @@ -35,6 +35,8 @@ | |||
| 35 | config SCSI_UFSHCD | 35 | config SCSI_UFSHCD |
| 36 | tristate "Universal Flash Storage Controller Driver Core" | 36 | tristate "Universal Flash Storage Controller Driver Core" |
| 37 | depends on SCSI && SCSI_DMA | 37 | depends on SCSI && SCSI_DMA |
| 38 | select PM_DEVFREQ | ||
| 39 | select DEVFREQ_GOV_SIMPLE_ONDEMAND | ||
| 38 | ---help--- | 40 | ---help--- |
| 39 | This selects the support for UFS devices in Linux, say Y and make | 41 | This selects the support for UFS devices in Linux, say Y and make |
| 40 | sure that you know the name of your UFS host adapter (the card | 42 | sure that you know the name of your UFS host adapter (the card |
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 6f1ea5192db6..df525e3b2c19 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c | |||
| @@ -38,6 +38,7 @@ | |||
| 38 | */ | 38 | */ |
| 39 | 39 | ||
| 40 | #include <linux/async.h> | 40 | #include <linux/async.h> |
| 41 | #include <linux/devfreq.h> | ||
| 41 | 42 | ||
| 42 | #include "ufshcd.h" | 43 | #include "ufshcd.h" |
| 43 | #include "unipro.h" | 44 | #include "unipro.h" |
| @@ -545,6 +546,8 @@ static void ufshcd_ungate_work(struct work_struct *work) | |||
| 545 | hba->clk_gating.is_suspended = false; | 546 | hba->clk_gating.is_suspended = false; |
| 546 | } | 547 | } |
| 547 | unblock_reqs: | 548 | unblock_reqs: |
| 549 | if (ufshcd_is_clkscaling_enabled(hba)) | ||
| 550 | devfreq_resume_device(hba->devfreq); | ||
| 548 | scsi_unblock_requests(hba->host); | 551 | scsi_unblock_requests(hba->host); |
| 549 | } | 552 | } |
| 550 | 553 | ||
| @@ -561,10 +564,10 @@ int ufshcd_hold(struct ufs_hba *hba, bool async) | |||
| 561 | 564 | ||
| 562 | if (!ufshcd_is_clkgating_allowed(hba)) | 565 | if (!ufshcd_is_clkgating_allowed(hba)) |
| 563 | goto out; | 566 | goto out; |
| 564 | start: | ||
| 565 | spin_lock_irqsave(hba->host->host_lock, flags); | 567 | spin_lock_irqsave(hba->host->host_lock, flags); |
| 566 | hba->clk_gating.active_reqs++; | 568 | hba->clk_gating.active_reqs++; |
| 567 | 569 | ||
| 570 | start: | ||
| 568 | switch (hba->clk_gating.state) { | 571 | switch (hba->clk_gating.state) { |
| 569 | case CLKS_ON: | 572 | case CLKS_ON: |
| 570 | break; | 573 | break; |
| @@ -596,6 +599,7 @@ start: | |||
| 596 | spin_unlock_irqrestore(hba->host->host_lock, flags); | 599 | spin_unlock_irqrestore(hba->host->host_lock, flags); |
| 597 | flush_work(&hba->clk_gating.ungate_work); | 600 | flush_work(&hba->clk_gating.ungate_work); |
| 598 | /* Make sure state is CLKS_ON before returning */ | 601 | /* Make sure state is CLKS_ON before returning */ |
| 602 | spin_lock_irqsave(hba->host->host_lock, flags); | ||
| 599 | goto start; | 603 | goto start; |
| 600 | default: | 604 | default: |
| 601 | dev_err(hba->dev, "%s: clk gating is in invalid state %d\n", | 605 | dev_err(hba->dev, "%s: clk gating is in invalid state %d\n", |
| @@ -636,6 +640,11 @@ static void ufshcd_gate_work(struct work_struct *work) | |||
| 636 | ufshcd_set_link_hibern8(hba); | 640 | ufshcd_set_link_hibern8(hba); |
| 637 | } | 641 | } |
| 638 | 642 | ||
| 643 | if (ufshcd_is_clkscaling_enabled(hba)) { | ||
| 644 | devfreq_suspend_device(hba->devfreq); | ||
| 645 | hba->clk_scaling.window_start_t = 0; | ||
| 646 | } | ||
| 647 | |||
| 639 | if (!ufshcd_is_link_active(hba)) | 648 | if (!ufshcd_is_link_active(hba)) |
| 640 | ufshcd_setup_clocks(hba, false); | 649 | ufshcd_setup_clocks(hba, false); |
| 641 | else | 650 | else |
| @@ -737,6 +746,32 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) | |||
| 737 | device_remove_file(hba->dev, &hba->clk_gating.delay_attr); | 746 | device_remove_file(hba->dev, &hba->clk_gating.delay_attr); |
| 738 | } | 747 | } |
| 739 | 748 | ||
| 749 | /* Must be called with host lock acquired */ | ||
| 750 | static void ufshcd_clk_scaling_start_busy(struct ufs_hba *hba) | ||
| 751 | { | ||
| 752 | if (!ufshcd_is_clkscaling_enabled(hba)) | ||
| 753 | return; | ||
| 754 | |||
| 755 | if (!hba->clk_scaling.is_busy_started) { | ||
| 756 | hba->clk_scaling.busy_start_t = ktime_get(); | ||
| 757 | hba->clk_scaling.is_busy_started = true; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | static void ufshcd_clk_scaling_update_busy(struct ufs_hba *hba) | ||
| 762 | { | ||
| 763 | struct ufs_clk_scaling *scaling = &hba->clk_scaling; | ||
| 764 | |||
| 765 | if (!ufshcd_is_clkscaling_enabled(hba)) | ||
| 766 | return; | ||
| 767 | |||
| 768 | if (!hba->outstanding_reqs && scaling->is_busy_started) { | ||
| 769 | scaling->tot_busy_t += ktime_to_us(ktime_sub(ktime_get(), | ||
| 770 | scaling->busy_start_t)); | ||
| 771 | scaling->busy_start_t = ktime_set(0, 0); | ||
| 772 | scaling->is_busy_started = false; | ||
| 773 | } | ||
| 774 | } | ||
| 740 | /** | 775 | /** |
| 741 | * ufshcd_send_command - Send SCSI or device management commands | 776 | * ufshcd_send_command - Send SCSI or device management commands |
| 742 | * @hba: per adapter instance | 777 | * @hba: per adapter instance |
| @@ -745,6 +780,7 @@ static void ufshcd_exit_clk_gating(struct ufs_hba *hba) | |||
| 745 | static inline | 780 | static inline |
| 746 | void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) | 781 | void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag) |
| 747 | { | 782 | { |
| 783 | ufshcd_clk_scaling_start_busy(hba); | ||
| 748 | __set_bit(task_tag, &hba->outstanding_reqs); | 784 | __set_bit(task_tag, &hba->outstanding_reqs); |
| 749 | ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL); | 785 | ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL); |
| 750 | } | 786 | } |
| @@ -3027,6 +3063,8 @@ static void ufshcd_transfer_req_compl(struct ufs_hba *hba) | |||
| 3027 | /* clear corresponding bits of completed commands */ | 3063 | /* clear corresponding bits of completed commands */ |
| 3028 | hba->outstanding_reqs ^= completed_reqs; | 3064 | hba->outstanding_reqs ^= completed_reqs; |
| 3029 | 3065 | ||
| 3066 | ufshcd_clk_scaling_update_busy(hba); | ||
| 3067 | |||
| 3030 | /* we might have free'd some tags above */ | 3068 | /* we might have free'd some tags above */ |
| 3031 | wake_up(&hba->dev_cmd.tag_wq); | 3069 | wake_up(&hba->dev_cmd.tag_wq); |
| 3032 | } | 3070 | } |
| @@ -4151,6 +4189,10 @@ static int ufshcd_probe_hba(struct ufs_hba *hba) | |||
| 4151 | if (!hba->is_init_prefetch) | 4189 | if (!hba->is_init_prefetch) |
| 4152 | hba->is_init_prefetch = true; | 4190 | hba->is_init_prefetch = true; |
| 4153 | 4191 | ||
| 4192 | /* Resume devfreq after UFS device is detected */ | ||
| 4193 | if (ufshcd_is_clkscaling_enabled(hba)) | ||
| 4194 | devfreq_resume_device(hba->devfreq); | ||
| 4195 | |||
| 4154 | out: | 4196 | out: |
| 4155 | /* | 4197 | /* |
| 4156 | * If we failed to initialize the device or the device is not | 4198 | * If we failed to initialize the device or the device is not |
| @@ -4472,6 +4514,7 @@ static int ufshcd_init_clocks(struct ufs_hba *hba) | |||
| 4472 | clki->max_freq, ret); | 4514 | clki->max_freq, ret); |
| 4473 | goto out; | 4515 | goto out; |
| 4474 | } | 4516 | } |
| 4517 | clki->curr_freq = clki->max_freq; | ||
| 4475 | } | 4518 | } |
| 4476 | dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__, | 4519 | dev_dbg(dev, "%s: clk: %s, rate: %lu\n", __func__, |
| 4477 | clki->name, clk_get_rate(clki->clk)); | 4520 | clki->name, clk_get_rate(clki->clk)); |
| @@ -4868,6 +4911,15 @@ static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) | |||
| 4868 | 4911 | ||
| 4869 | disable_clks: | 4912 | disable_clks: |
| 4870 | /* | 4913 | /* |
| 4914 | * The clock scaling needs access to controller registers. Hence, Wait | ||
| 4915 | * for pending clock scaling work to be done before clocks are | ||
| 4916 | * turned off. | ||
| 4917 | */ | ||
| 4918 | if (ufshcd_is_clkscaling_enabled(hba)) { | ||
| 4919 | devfreq_suspend_device(hba->devfreq); | ||
| 4920 | hba->clk_scaling.window_start_t = 0; | ||
| 4921 | } | ||
| 4922 | /* | ||
| 4871 | * Call vendor specific suspend callback. As these callbacks may access | 4923 | * Call vendor specific suspend callback. As these callbacks may access |
| 4872 | * vendor specific host controller register space call them before the | 4924 | * vendor specific host controller register space call them before the |
| 4873 | * host clocks are ON. | 4925 | * host clocks are ON. |
| @@ -4989,6 +5041,9 @@ static int ufshcd_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) | |||
| 4989 | ufshcd_disable_auto_bkops(hba); | 5041 | ufshcd_disable_auto_bkops(hba); |
| 4990 | hba->clk_gating.is_suspended = false; | 5042 | hba->clk_gating.is_suspended = false; |
| 4991 | 5043 | ||
| 5044 | if (ufshcd_is_clkscaling_enabled(hba)) | ||
| 5045 | devfreq_resume_device(hba->devfreq); | ||
| 5046 | |||
| 4992 | /* Schedule clock gating in case of no access to UFS device yet */ | 5047 | /* Schedule clock gating in case of no access to UFS device yet */ |
| 4993 | ufshcd_release(hba); | 5048 | ufshcd_release(hba); |
| 4994 | goto out; | 5049 | goto out; |
| @@ -5172,6 +5227,8 @@ void ufshcd_remove(struct ufs_hba *hba) | |||
| 5172 | scsi_host_put(hba->host); | 5227 | scsi_host_put(hba->host); |
| 5173 | 5228 | ||
| 5174 | ufshcd_exit_clk_gating(hba); | 5229 | ufshcd_exit_clk_gating(hba); |
| 5230 | if (ufshcd_is_clkscaling_enabled(hba)) | ||
| 5231 | devfreq_remove_device(hba->devfreq); | ||
| 5175 | ufshcd_hba_exit(hba); | 5232 | ufshcd_hba_exit(hba); |
| 5176 | } | 5233 | } |
| 5177 | EXPORT_SYMBOL_GPL(ufshcd_remove); | 5234 | EXPORT_SYMBOL_GPL(ufshcd_remove); |
| @@ -5228,6 +5285,112 @@ out_error: | |||
| 5228 | } | 5285 | } |
| 5229 | EXPORT_SYMBOL(ufshcd_alloc_host); | 5286 | EXPORT_SYMBOL(ufshcd_alloc_host); |
| 5230 | 5287 | ||
| 5288 | static int ufshcd_scale_clks(struct ufs_hba *hba, bool scale_up) | ||
| 5289 | { | ||
| 5290 | int ret = 0; | ||
| 5291 | struct ufs_clk_info *clki; | ||
| 5292 | struct list_head *head = &hba->clk_list_head; | ||
| 5293 | |||
| 5294 | if (!head || list_empty(head)) | ||
| 5295 | goto out; | ||
| 5296 | |||
| 5297 | list_for_each_entry(clki, head, list) { | ||
| 5298 | if (!IS_ERR_OR_NULL(clki->clk)) { | ||
| 5299 | if (scale_up && clki->max_freq) { | ||
| 5300 | if (clki->curr_freq == clki->max_freq) | ||
| 5301 | continue; | ||
| 5302 | ret = clk_set_rate(clki->clk, clki->max_freq); | ||
| 5303 | if (ret) { | ||
| 5304 | dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n", | ||
| 5305 | __func__, clki->name, | ||
| 5306 | clki->max_freq, ret); | ||
| 5307 | break; | ||
| 5308 | } | ||
| 5309 | clki->curr_freq = clki->max_freq; | ||
| 5310 | |||
| 5311 | } else if (!scale_up && clki->min_freq) { | ||
| 5312 | if (clki->curr_freq == clki->min_freq) | ||
| 5313 | continue; | ||
| 5314 | ret = clk_set_rate(clki->clk, clki->min_freq); | ||
| 5315 | if (ret) { | ||
| 5316 | dev_err(hba->dev, "%s: %s clk set rate(%dHz) failed, %d\n", | ||
| 5317 | __func__, clki->name, | ||
| 5318 | clki->min_freq, ret); | ||
| 5319 | break; | ||
| 5320 | } | ||
| 5321 | clki->curr_freq = clki->min_freq; | ||
| 5322 | } | ||
| 5323 | } | ||
| 5324 | dev_dbg(hba->dev, "%s: clk: %s, rate: %lu\n", __func__, | ||
| 5325 | clki->name, clk_get_rate(clki->clk)); | ||
| 5326 | } | ||
| 5327 | if (hba->vops->clk_scale_notify) | ||
| 5328 | hba->vops->clk_scale_notify(hba); | ||
| 5329 | out: | ||
| 5330 | return ret; | ||
| 5331 | } | ||
| 5332 | |||
| 5333 | static int ufshcd_devfreq_target(struct device *dev, | ||
| 5334 | unsigned long *freq, u32 flags) | ||
| 5335 | { | ||
| 5336 | int err = 0; | ||
| 5337 | struct ufs_hba *hba = dev_get_drvdata(dev); | ||
| 5338 | |||
| 5339 | if (!ufshcd_is_clkscaling_enabled(hba)) | ||
| 5340 | return -EINVAL; | ||
| 5341 | |||
| 5342 | if (*freq == UINT_MAX) | ||
| 5343 | err = ufshcd_scale_clks(hba, true); | ||
| 5344 | else if (*freq == 0) | ||
| 5345 | err = ufshcd_scale_clks(hba, false); | ||
| 5346 | |||
| 5347 | return err; | ||
| 5348 | } | ||
| 5349 | |||
| 5350 | static int ufshcd_devfreq_get_dev_status(struct device *dev, | ||
| 5351 | struct devfreq_dev_status *stat) | ||
| 5352 | { | ||
| 5353 | struct ufs_hba *hba = dev_get_drvdata(dev); | ||
| 5354 | struct ufs_clk_scaling *scaling = &hba->clk_scaling; | ||
| 5355 | unsigned long flags; | ||
| 5356 | |||
| 5357 | if (!ufshcd_is_clkscaling_enabled(hba)) | ||
| 5358 | return -EINVAL; | ||
| 5359 | |||
| 5360 | memset(stat, 0, sizeof(*stat)); | ||
| 5361 | |||
| 5362 | spin_lock_irqsave(hba->host->host_lock, flags); | ||
| 5363 | if (!scaling->window_start_t) | ||
| 5364 | goto start_window; | ||
| 5365 | |||
| 5366 | if (scaling->is_busy_started) | ||
| 5367 | scaling->tot_busy_t += ktime_to_us(ktime_sub(ktime_get(), | ||
| 5368 | scaling->busy_start_t)); | ||
| 5369 | |||
| 5370 | stat->total_time = jiffies_to_usecs((long)jiffies - | ||
| 5371 | (long)scaling->window_start_t); | ||
| 5372 | stat->busy_time = scaling->tot_busy_t; | ||
| 5373 | start_window: | ||
| 5374 | scaling->window_start_t = jiffies; | ||
| 5375 | scaling->tot_busy_t = 0; | ||
| 5376 | |||
| 5377 | if (hba->outstanding_reqs) { | ||
| 5378 | scaling->busy_start_t = ktime_get(); | ||
| 5379 | scaling->is_busy_started = true; | ||
| 5380 | } else { | ||
| 5381 | scaling->busy_start_t = ktime_set(0, 0); | ||
| 5382 | scaling->is_busy_started = false; | ||
| 5383 | } | ||
| 5384 | spin_unlock_irqrestore(hba->host->host_lock, flags); | ||
| 5385 | return 0; | ||
| 5386 | } | ||
| 5387 | |||
| 5388 | static struct devfreq_dev_profile ufs_devfreq_profile = { | ||
| 5389 | .polling_ms = 100, | ||
| 5390 | .target = ufshcd_devfreq_target, | ||
| 5391 | .get_dev_status = ufshcd_devfreq_get_dev_status, | ||
| 5392 | }; | ||
| 5393 | |||
| 5231 | /** | 5394 | /** |
| 5232 | * ufshcd_init - Driver initialization routine | 5395 | * ufshcd_init - Driver initialization routine |
| 5233 | * @hba: per-adapter instance | 5396 | * @hba: per-adapter instance |
| @@ -5337,6 +5500,19 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) | |||
| 5337 | goto out_remove_scsi_host; | 5500 | goto out_remove_scsi_host; |
| 5338 | } | 5501 | } |
| 5339 | 5502 | ||
| 5503 | if (ufshcd_is_clkscaling_enabled(hba)) { | ||
| 5504 | hba->devfreq = devfreq_add_device(dev, &ufs_devfreq_profile, | ||
| 5505 | "simple_ondemand", NULL); | ||
| 5506 | if (IS_ERR(hba->devfreq)) { | ||
| 5507 | dev_err(hba->dev, "Unable to register with devfreq %ld\n", | ||
| 5508 | PTR_ERR(hba->devfreq)); | ||
| 5509 | goto out_remove_scsi_host; | ||
| 5510 | } | ||
| 5511 | /* Suspend devfreq until the UFS device is detected */ | ||
| 5512 | devfreq_suspend_device(hba->devfreq); | ||
| 5513 | hba->clk_scaling.window_start_t = 0; | ||
| 5514 | } | ||
| 5515 | |||
| 5340 | /* Hold auto suspend until async scan completes */ | 5516 | /* Hold auto suspend until async scan completes */ |
| 5341 | pm_runtime_get_sync(dev); | 5517 | pm_runtime_get_sync(dev); |
| 5342 | 5518 | ||
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index ac72a6daf50e..908db3eb0609 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h | |||
| @@ -210,6 +210,7 @@ struct ufs_dev_cmd { | |||
| 210 | * @name: clock name | 210 | * @name: clock name |
| 211 | * @max_freq: maximum frequency supported by the clock | 211 | * @max_freq: maximum frequency supported by the clock |
| 212 | * @min_freq: min frequency that can be used for clock scaling | 212 | * @min_freq: min frequency that can be used for clock scaling |
| 213 | * @curr_freq: indicates the current frequency that it is set to | ||
| 213 | * @enabled: variable to check against multiple enable/disable | 214 | * @enabled: variable to check against multiple enable/disable |
| 214 | */ | 215 | */ |
| 215 | struct ufs_clk_info { | 216 | struct ufs_clk_info { |
| @@ -218,6 +219,7 @@ struct ufs_clk_info { | |||
| 218 | const char *name; | 219 | const char *name; |
| 219 | u32 max_freq; | 220 | u32 max_freq; |
| 220 | u32 min_freq; | 221 | u32 min_freq; |
| 222 | u32 curr_freq; | ||
| 221 | bool enabled; | 223 | bool enabled; |
| 222 | }; | 224 | }; |
| 223 | 225 | ||
| @@ -244,6 +246,7 @@ struct ufs_pwr_mode_info { | |||
| 244 | * @name: variant name | 246 | * @name: variant name |
| 245 | * @init: called when the driver is initialized | 247 | * @init: called when the driver is initialized |
| 246 | * @exit: called to cleanup everything done in init | 248 | * @exit: called to cleanup everything done in init |
| 249 | * @clk_scale_notify: notifies that clks are scaled up/down | ||
| 247 | * @setup_clocks: called before touching any of the controller registers | 250 | * @setup_clocks: called before touching any of the controller registers |
| 248 | * @setup_regulators: called before accessing the host controller | 251 | * @setup_regulators: called before accessing the host controller |
| 249 | * @hce_enable_notify: called before and after HCE enable bit is set to allow | 252 | * @hce_enable_notify: called before and after HCE enable bit is set to allow |
| @@ -260,6 +263,7 @@ struct ufs_hba_variant_ops { | |||
| 260 | const char *name; | 263 | const char *name; |
| 261 | int (*init)(struct ufs_hba *); | 264 | int (*init)(struct ufs_hba *); |
| 262 | void (*exit)(struct ufs_hba *); | 265 | void (*exit)(struct ufs_hba *); |
| 266 | void (*clk_scale_notify)(struct ufs_hba *); | ||
| 263 | int (*setup_clocks)(struct ufs_hba *, bool); | 267 | int (*setup_clocks)(struct ufs_hba *, bool); |
| 264 | int (*setup_regulators)(struct ufs_hba *, bool); | 268 | int (*setup_regulators)(struct ufs_hba *, bool); |
| 265 | int (*hce_enable_notify)(struct ufs_hba *, bool); | 269 | int (*hce_enable_notify)(struct ufs_hba *, bool); |
| @@ -303,6 +307,13 @@ struct ufs_clk_gating { | |||
| 303 | int active_reqs; | 307 | int active_reqs; |
| 304 | }; | 308 | }; |
| 305 | 309 | ||
| 310 | struct ufs_clk_scaling { | ||
| 311 | ktime_t busy_start_t; | ||
| 312 | bool is_busy_started; | ||
| 313 | unsigned long tot_busy_t; | ||
| 314 | unsigned long window_start_t; | ||
| 315 | }; | ||
| 316 | |||
| 306 | /** | 317 | /** |
| 307 | * struct ufs_init_prefetch - contains data that is pre-fetched once during | 318 | * struct ufs_init_prefetch - contains data that is pre-fetched once during |
| 308 | * initialization | 319 | * initialization |
| @@ -456,6 +467,11 @@ struct ufs_hba { | |||
| 456 | #define UFSHCD_CAP_CLK_GATING (1 << 0) | 467 | #define UFSHCD_CAP_CLK_GATING (1 << 0) |
| 457 | /* Allow hiberb8 with clk gating */ | 468 | /* Allow hiberb8 with clk gating */ |
| 458 | #define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1) | 469 | #define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1) |
| 470 | /* Allow dynamic clk scaling */ | ||
| 471 | #define UFSHCD_CAP_CLK_SCALING (1 << 2) | ||
| 472 | |||
| 473 | struct devfreq *devfreq; | ||
| 474 | struct ufs_clk_scaling clk_scaling; | ||
| 459 | }; | 475 | }; |
| 460 | 476 | ||
| 461 | /* Returns true if clocks can be gated. Otherwise false */ | 477 | /* Returns true if clocks can be gated. Otherwise false */ |
| @@ -467,6 +483,10 @@ static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba) | |||
| 467 | { | 483 | { |
| 468 | return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; | 484 | return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; |
| 469 | } | 485 | } |
| 486 | static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba) | ||
| 487 | { | ||
| 488 | return hba->caps & UFSHCD_CAP_CLK_SCALING; | ||
| 489 | } | ||
| 470 | #define ufshcd_writel(hba, val, reg) \ | 490 | #define ufshcd_writel(hba, val, reg) \ |
| 471 | writel((val), (hba)->mmio_base + (reg)) | 491 | writel((val), (hba)->mmio_base + (reg)) |
| 472 | #define ufshcd_readl(hba, reg) \ | 492 | #define ufshcd_readl(hba, reg) \ |
