Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scsi: ufs: ufs-mediatek: Support clk-scaling to optimize power consumption

Provide clk-scaling feature in MediaTek UFS platforms.

MediaTek platform supports clk-scaling by switching parent clock mux of
UFSHCI main clocks: ufs_sel.

The driver needs to prevent changing the rate of ufs_sel because its parent
PLL clock may be shared between multiple IPs. In order to achieve this
goal, the maximum and minimum clock rates of ufs_sel defined in dts should
match the rate of "ufs_sel_max_src" and "ufs_sel_min_src" respectively.

Link: https://lore.kernel.org/r/20220802235437.4547-6-stanley.chu@mediatek.com
Reviewed-by: Stanley Chu <stanley.chu@mediatek.com>
Signed-off-by: Po-Wen Kao <powen.kao@mediatek.com>
Signed-off-by: Stanley Chu <stanley.chu@mediatek.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Po-Wen Kao and committed by
Martin K. Petersen
b7dbc686 2873e045

+153 -2
+25 -2
drivers/ufs/host/ufs-mediatek-trace.h
··· 24 24 __entry->data = data; 25 25 ), 26 26 27 - TP_printk("ufs:event=%u data=%u", 27 + TP_printk("ufs: event=%u data=%u", 28 28 __entry->type, __entry->data) 29 - ); 29 + ); 30 + 31 + TRACE_EVENT(ufs_mtk_clk_scale, 32 + TP_PROTO(const char *name, bool scale_up, unsigned long clk_rate), 33 + TP_ARGS(name, scale_up, clk_rate), 34 + 35 + TP_STRUCT__entry( 36 + __field(const char*, name) 37 + __field(bool, scale_up) 38 + __field(unsigned long, clk_rate) 39 + ), 40 + 41 + TP_fast_assign( 42 + __entry->name = name; 43 + __entry->scale_up = scale_up; 44 + __entry->clk_rate = clk_rate; 45 + ), 46 + 47 + TP_printk("ufs: clk (%s) scaled %s @ %lu", 48 + __entry->name, 49 + __entry->scale_up ? "up" : "down", 50 + __entry->clk_rate) 51 + ); 52 + 30 53 #endif 31 54 32 55 #undef TRACE_INCLUDE_PATH
+121
drivers/ufs/host/ufs-mediatek.c
··· 738 738 return hba->ufs_version; 739 739 } 740 740 741 + /** 742 + * ufs_mtk_init_clocks - Init mtk driver private clocks 743 + * 744 + * @hba: per adapter instance 745 + */ 746 + static void ufs_mtk_init_clocks(struct ufs_hba *hba) 747 + { 748 + struct ufs_mtk_host *host = ufshcd_get_variant(hba); 749 + struct list_head *head = &hba->clk_list_head; 750 + struct ufs_mtk_clk *mclk = &host->mclk; 751 + struct ufs_clk_info *clki, *clki_tmp; 752 + 753 + /* 754 + * Find private clocks and store them in struct ufs_mtk_clk. 755 + * Remove "ufs_sel_min_src" and "ufs_sel_min_src" from list to avoid 756 + * being switched on/off in clock gating. 757 + */ 758 + list_for_each_entry_safe(clki, clki_tmp, head, list) { 759 + if (!strcmp(clki->name, "ufs_sel")) { 760 + host->mclk.ufs_sel_clki = clki; 761 + } else if (!strcmp(clki->name, "ufs_sel_max_src")) { 762 + host->mclk.ufs_sel_max_clki = clki; 763 + clk_disable_unprepare(clki->clk); 764 + list_del(&clki->list); 765 + } else if (!strcmp(clki->name, "ufs_sel_min_src")) { 766 + host->mclk.ufs_sel_min_clki = clki; 767 + clk_disable_unprepare(clki->clk); 768 + list_del(&clki->list); 769 + } 770 + } 771 + 772 + if (!mclk->ufs_sel_clki || !mclk->ufs_sel_max_clki || 773 + !mclk->ufs_sel_min_clki) { 774 + hba->caps &= ~UFSHCD_CAP_CLK_SCALING; 775 + dev_info(hba->dev, 776 + "%s: Clk-scaling not ready. Feature disabled.", 777 + __func__); 778 + } 779 + } 780 + 741 781 #define MAX_VCC_NAME 30 742 782 static int ufs_mtk_vreg_fix_vcc(struct ufs_hba *hba) 743 783 { ··· 898 858 899 859 /* Enable WriteBooster */ 900 860 hba->caps |= UFSHCD_CAP_WB_EN; 861 + 862 + /* Enable clk scaling*/ 863 + hba->caps |= UFSHCD_CAP_CLK_SCALING; 864 + 901 865 hba->quirks |= UFSHCI_QUIRK_SKIP_MANUAL_WB_FLUSH_CTRL; 902 866 hba->vps->wb_flush_threshold = UFS_WB_BUF_REMAIN_PERCENT(80); 903 867 904 868 if (host->caps & UFS_MTK_CAP_DISABLE_AH8) 905 869 hba->caps |= UFSHCD_CAP_HIBERN8_WITH_CLK_GATING; 870 + 871 + ufs_mtk_init_clocks(hba); 906 872 907 873 /* 908 874 * ufshcd_vops_init() is invoked after ··· 1430 1384 } 1431 1385 } 1432 1386 1387 + static void ufs_mtk_config_scaling_param(struct ufs_hba *hba, 1388 + struct devfreq_dev_profile *profile, 1389 + struct devfreq_simple_ondemand_data *data) 1390 + { 1391 + /* Customize min gear in clk scaling */ 1392 + hba->clk_scaling.min_gear = UFS_HS_G4; 1393 + 1394 + hba->vps->devfreq_profile.polling_ms = 200; 1395 + hba->vps->ondemand_data.upthreshold = 50; 1396 + hba->vps->ondemand_data.downdifferential = 20; 1397 + } 1398 + 1399 + /** 1400 + * ufs_mtk_clk_scale - Internal clk scaling operation 1401 + * 1402 + * MTK platform supports clk scaling by switching parent of ufs_sel(mux). 1403 + * The ufs_sel downstream to ufs_ck which feeds directly to UFS hardware. 1404 + * Max and min clocks rate of ufs_sel defined in dts should match rate of 1405 + * "ufs_sel_max_src" and "ufs_sel_min_src" respectively. 1406 + * This prevent changing rate of pll clock that is shared between modules. 1407 + * 1408 + * @hba: per adapter instance 1409 + * @scale_up: True for scaling up and false for scaling down 1410 + */ 1411 + static void ufs_mtk_clk_scale(struct ufs_hba *hba, bool scale_up) 1412 + { 1413 + struct ufs_mtk_host *host = ufshcd_get_variant(hba); 1414 + struct ufs_mtk_clk *mclk = &host->mclk; 1415 + struct ufs_clk_info *clki = mclk->ufs_sel_clki; 1416 + int ret = 0; 1417 + 1418 + ret = clk_prepare_enable(clki->clk); 1419 + if (ret) { 1420 + dev_info(hba->dev, 1421 + "clk_prepare_enable() fail, ret: %d\n", ret); 1422 + return; 1423 + } 1424 + 1425 + if (scale_up) { 1426 + ret = clk_set_parent(clki->clk, mclk->ufs_sel_max_clki->clk); 1427 + clki->curr_freq = clki->max_freq; 1428 + } else { 1429 + ret = clk_set_parent(clki->clk, mclk->ufs_sel_min_clki->clk); 1430 + clki->curr_freq = clki->min_freq; 1431 + } 1432 + 1433 + if (ret) { 1434 + dev_info(hba->dev, 1435 + "Failed to set ufs_sel_clki, ret: %d\n", ret); 1436 + } 1437 + 1438 + clk_disable_unprepare(clki->clk); 1439 + 1440 + trace_ufs_mtk_clk_scale(clki->name, scale_up, clk_get_rate(clki->clk)); 1441 + } 1442 + 1443 + static int ufs_mtk_clk_scale_notify(struct ufs_hba *hba, bool scale_up, 1444 + enum ufs_notify_change_status status) 1445 + { 1446 + if (!ufshcd_is_clkscaling_supported(hba)) 1447 + return 0; 1448 + 1449 + if (status == PRE_CHANGE) { 1450 + /* Switch parent before clk_set_rate() */ 1451 + ufs_mtk_clk_scale(hba, scale_up); 1452 + } else { 1453 + /* Request interrupt latency QoS accordingly */ 1454 + ufs_mtk_scale_perf(hba, scale_up); 1455 + } 1456 + 1457 + return 0; 1458 + } 1459 + 1433 1460 /* 1434 1461 * struct ufs_hba_mtk_vops - UFS MTK specific variant operations 1435 1462 * ··· 1524 1405 .dbg_register_dump = ufs_mtk_dbg_register_dump, 1525 1406 .device_reset = ufs_mtk_device_reset, 1526 1407 .event_notify = ufs_mtk_event_notify, 1408 + .config_scaling_param = ufs_mtk_config_scaling_param, 1409 + .clk_scale_notify = ufs_mtk_clk_scale_notify, 1527 1410 }; 1528 1411 1529 1412 /**
+7
drivers/ufs/host/ufs-mediatek.h
··· 124 124 int vcore_volt; 125 125 }; 126 126 127 + struct ufs_mtk_clk { 128 + struct ufs_clk_info *ufs_sel_clki; /* Mux */ 129 + struct ufs_clk_info *ufs_sel_max_clki; /* Max src */ 130 + struct ufs_clk_info *ufs_sel_min_clki; /* Min src */ 131 + }; 132 + 127 133 struct ufs_mtk_hw_ver { 128 134 u8 step; 129 135 u8 minor; ··· 145 139 struct reset_control *crypto_reset; 146 140 struct ufs_hba *hba; 147 141 struct ufs_mtk_crypt_cfg *crypt; 142 + struct ufs_mtk_clk mclk; 148 143 struct ufs_mtk_hw_ver hw_ver; 149 144 enum ufs_mtk_host_caps caps; 150 145 bool mphy_powered_on;