From 0649d01df70a8b2a8a4f236f5b870f083e8df400 Mon Sep 17 00:00:00 2001 From: YouMin Chen Date: Fri, 28 May 2021 20:14:46 +0800 Subject: [PATCH] PM / devfreq: rockchip_dmc: rk3568: get available frequencies from ATF Add the function of rockchip_get_freq_info to get available ddr frequencies info via SMC call. The frequency info include available frequencies count and frequencies table(sort by rate from low to high). Afterwards it will update the dmc_opp_table base on frequencies info. If have "system-status-level" property in dmc, the function of rockchip_get_system_status_level will get the frequency for system-status base on frequency level and available frequencies table. Change-Id: I73d0f999e718ba9426ad75e48ac2a4ec3fe5f496 Signed-off-by: YouMin Chen --- drivers/devfreq/rockchip_dmc.c | 267 ++++++++++++++++++++++++++++++++- 1 file changed, 259 insertions(+), 8 deletions(-) diff --git a/drivers/devfreq/rockchip_dmc.c b/drivers/devfreq/rockchip_dmc.c index cb2421dea429..27635de19143 100644 --- a/drivers/devfreq/rockchip_dmc.c +++ b/drivers/devfreq/rockchip_dmc.c @@ -88,6 +88,11 @@ struct rl_map_table { unsigned int rl; /* readlatency */ }; +struct dmc_freq_table { + unsigned long freq; + unsigned long volt; +}; + struct share_params { u32 hz; u32 lcdc_type; @@ -108,6 +113,9 @@ struct share_params { u32 complt_hwirq; u32 update_drv_odt_cfg; u32 update_deskew_cfg; + + u32 freq_count; + u32 freq_info_mhz[6]; /* if need, add parameter after */ }; @@ -151,6 +159,13 @@ struct rockchip_dmcfreq { unsigned long low_power_rate; unsigned long vop_req_rate; + unsigned long freq_count; + unsigned long freq_info_rate[6]; + unsigned long rate_low; + unsigned long rate_mid_low; + unsigned long rate_mid_high; + unsigned long rate_high; + unsigned int min_cpu_freq; unsigned int auto_freq_en; unsigned int system_status_en; @@ -1191,6 +1206,85 @@ int rockchip_dmcfreq_wait_complete(void) return 0; } +static __maybe_unused int rockchip_get_freq_info(struct rockchip_dmcfreq *dmcfreq) +{ + struct arm_smccc_res res; + struct dev_pm_opp *opp; + struct dmc_freq_table *freq_table; + unsigned long rate; + int i, j, count, ret = 0; + + res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0, + ROCKCHIP_SIP_CONFIG_DRAM_GET_FREQ_INFO); + if (res.a0) { + dev_err(dmcfreq->dev, "rockchip_sip_config_dram_get_freq_info error:%lx\n", + res.a0); + return -ENOMEM; + } + + if (ddr_psci_param->freq_count == 0 || ddr_psci_param->freq_count > 6) { + dev_err(dmcfreq->dev, "it is no available frequencies!\n"); + return -EPERM; + } + + for (i = 0; i < ddr_psci_param->freq_count; i++) + dmcfreq->freq_info_rate[i] = ddr_psci_param->freq_info_mhz[i] * 1000000; + dmcfreq->freq_count = ddr_psci_param->freq_count; + + /* update dmc_opp_table */ + count = dev_pm_opp_get_opp_count(dmcfreq->dev); + if (count <= 0) { + ret = count ? count : -ENODATA; + return ret; + } + + freq_table = kmalloc(sizeof(struct dmc_freq_table) * count, GFP_KERNEL); + for (i = 0, rate = 0; i < count; i++, rate++) { + /* find next rate */ + opp = dev_pm_opp_find_freq_ceil(dmcfreq->dev, &rate); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + dev_err(dmcfreq->dev, "failed to find OPP for freq %lu.\n", rate); + goto out; + } + freq_table[i].freq = rate; + freq_table[i].volt = dev_pm_opp_get_voltage(opp); + dev_pm_opp_put(opp); + + for (j = 0; j < dmcfreq->freq_count; j++) { + if (rate == dmcfreq->freq_info_rate[j]) + break; + } + if (j == dmcfreq->freq_count) + dev_pm_opp_remove(dmcfreq->dev, rate); + } + + for (i = 0; i < dmcfreq->freq_count; i++) { + for (j = 0; j < count; j++) { + if (dmcfreq->freq_info_rate[i] == freq_table[j].freq) { + break; + } else if (dmcfreq->freq_info_rate[i] < freq_table[j].freq) { + dev_pm_opp_add(dmcfreq->dev, dmcfreq->freq_info_rate[i], + freq_table[j].volt); + break; + } + } + if (j == count) { + dev_err(dmcfreq->dev, "failed to match dmc_opp_table for %ld\n", + dmcfreq->freq_info_rate[i]); + if (i == 0) + ret = -EPERM; + else + dmcfreq->freq_count = i; + goto out; + } + } + +out: + kfree(freq_table); + return ret; +} + static __maybe_unused int px30_dmc_init(struct platform_device *pdev, struct rockchip_dmcfreq *dmcfreq) { @@ -1690,9 +1784,8 @@ static __maybe_unused int rk3568_dmc_init(struct platform_device *pdev, res = sip_smc_dram(0, 0, ROCKCHIP_SIP_CONFIG_DRAM_GET_VERSION); dev_notice(&pdev->dev, "current ATF version 0x%lx\n", res.a1); - if (res.a0 || res.a1 < 0x100) { - dev_err(&pdev->dev, - "trusted firmware need to update or is invalid!\n"); + if (res.a0 || res.a1 < 0x101) { + dev_err(&pdev->dev, "trusted firmware need update to V1.01 and above.\n"); return -ENXIO; } @@ -1750,6 +1843,12 @@ static __maybe_unused int rk3568_dmc_init(struct platform_device *pdev, return -ENOMEM; } + ret = rockchip_get_freq_info(dmcfreq); + if (ret < 0) { + dev_err(&pdev->dev, "cannot get frequency info\n"); + return ret; + } + dmcfreq->set_auto_self_refresh = rockchip_ddr_set_auto_self_refresh; return 0; @@ -2035,6 +2134,156 @@ static int rockchip_get_system_status_rate(struct device_node *np, return 0; } +static unsigned long rockchip_freq_level_2_rate(struct rockchip_dmcfreq *dmcfreq, + unsigned int level) +{ + unsigned long rate = 0; + + switch (level) { + case DMC_FREQ_LEVEL_LOW: + rate = dmcfreq->rate_low; + break; + case DMC_FREQ_LEVEL_MID_LOW: + rate = dmcfreq->rate_mid_low; + break; + case DMC_FREQ_LEVEL_MID_HIGH: + rate = dmcfreq->rate_mid_high; + break; + case DMC_FREQ_LEVEL_HIGH: + rate = dmcfreq->rate_high; + break; + default: + break; + } + + return rate; +} + +static int rockchip_get_system_status_level(struct device_node *np, + char *porp_name, + struct rockchip_dmcfreq *dmcfreq) +{ + const struct property *prop; + unsigned int status = 0, level = 0; + unsigned long temp_rate = 0; + int count, i; + + prop = of_find_property(np, porp_name, NULL); + if (!prop) + return -ENODEV; + + if (!prop->value) + return -ENODATA; + + count = of_property_count_u32_elems(np, porp_name); + if (count < 0) + return -EINVAL; + + if (count % 2) + return -EINVAL; + + if (dmcfreq->freq_count == 1) { + dmcfreq->rate_low = dmcfreq->freq_info_rate[0]; + dmcfreq->rate_mid_low = dmcfreq->freq_info_rate[0]; + dmcfreq->rate_mid_high = dmcfreq->freq_info_rate[0]; + dmcfreq->rate_high = dmcfreq->freq_info_rate[0]; + } else if (dmcfreq->freq_count == 2) { + dmcfreq->rate_low = dmcfreq->freq_info_rate[0]; + dmcfreq->rate_mid_low = dmcfreq->freq_info_rate[0]; + dmcfreq->rate_mid_high = dmcfreq->freq_info_rate[1]; + dmcfreq->rate_high = dmcfreq->freq_info_rate[1]; + } else if (dmcfreq->freq_count == 3) { + dmcfreq->rate_low = dmcfreq->freq_info_rate[0]; + dmcfreq->rate_mid_low = dmcfreq->freq_info_rate[1]; + dmcfreq->rate_mid_high = dmcfreq->freq_info_rate[1]; + dmcfreq->rate_high = dmcfreq->freq_info_rate[2]; + } else if (dmcfreq->freq_count == 4) { + dmcfreq->rate_low = dmcfreq->freq_info_rate[0]; + dmcfreq->rate_mid_low = dmcfreq->freq_info_rate[1]; + dmcfreq->rate_mid_high = dmcfreq->freq_info_rate[2]; + dmcfreq->rate_high = dmcfreq->freq_info_rate[3]; + } else if (dmcfreq->freq_count == 5 || dmcfreq->freq_count == 6) { + dmcfreq->rate_low = dmcfreq->freq_info_rate[0]; + dmcfreq->rate_mid_low = dmcfreq->freq_info_rate[1]; + dmcfreq->rate_mid_high = dmcfreq->freq_info_rate[dmcfreq->freq_count - 2]; + dmcfreq->rate_high = dmcfreq->freq_info_rate[dmcfreq->freq_count - 1]; + } else { + return -EINVAL; + } + + for (i = 0; i < count / 2; i++) { + of_property_read_u32_index(np, porp_name, 2 * i, + &status); + of_property_read_u32_index(np, porp_name, 2 * i + 1, + &level); + switch (status) { + case SYS_STATUS_NORMAL: + dmcfreq->normal_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "normal_rate = %ld\n", dmcfreq->normal_rate); + break; + case SYS_STATUS_SUSPEND: + dmcfreq->suspend_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "suspend_rate = %ld\n", dmcfreq->suspend_rate); + break; + case SYS_STATUS_VIDEO_1080P: + dmcfreq->video_1080p_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "video_1080p_rate = %ld\n", + dmcfreq->video_1080p_rate); + break; + case SYS_STATUS_VIDEO_4K: + dmcfreq->video_4k_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "video_4k_rate = %ld\n", dmcfreq->video_4k_rate); + break; + case SYS_STATUS_VIDEO_4K_10B: + dmcfreq->video_4k_10b_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "video_4k_10b_rate = %ld\n", + dmcfreq->video_4k_10b_rate); + break; + case SYS_STATUS_PERFORMANCE: + dmcfreq->performance_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "performance_rate = %ld\n", + dmcfreq->performance_rate); + break; + case SYS_STATUS_HDMI: + dmcfreq->hdmi_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "hdmi_rate = %ld\n", dmcfreq->hdmi_rate); + break; + case SYS_STATUS_IDLE: + dmcfreq->idle_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "idle_rate = %ld\n", dmcfreq->idle_rate); + break; + case SYS_STATUS_REBOOT: + dmcfreq->reboot_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "reboot_rate = %ld\n", dmcfreq->reboot_rate); + break; + case SYS_STATUS_BOOST: + dmcfreq->boost_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "boost_rate = %ld\n", dmcfreq->boost_rate); + break; + case SYS_STATUS_ISP: + case SYS_STATUS_CIF0: + case SYS_STATUS_CIF1: + case SYS_STATUS_DUALVIEW: + temp_rate = rockchip_freq_level_2_rate(dmcfreq, level); + if (dmcfreq->fixed_rate < temp_rate) { + dmcfreq->fixed_rate = temp_rate; + dev_info(dmcfreq->dev, + "fixed_rate(isp|cif0|cif1|dualview) = %ld\n", + dmcfreq->fixed_rate); + } + break; + case SYS_STATUS_LOW_POWER: + dmcfreq->low_power_rate = rockchip_freq_level_2_rate(dmcfreq, level); + dev_info(dmcfreq->dev, "low_power_rate = %ld\n", dmcfreq->low_power_rate); + break; + default: + break; + } + } + + return 0; +} + static void rockchip_dmcfreq_update_target(struct rockchip_dmcfreq *dmcfreq) { struct devfreq *df = dmcfreq->devfreq; @@ -2535,7 +2784,7 @@ static int rockchip_dmcfreq_power_control(struct rockchip_dmcfreq *dmcfreq) dmcfreq->dmc_clk = devm_clk_get(dev, "dmc_clk"); if (IS_ERR(dmcfreq->dmc_clk)) { - dev_err(dev, "Cannot get the clk dmc_clk\n"); + dev_err(dev, "Cannot get the clk dmc_clk. If using SCMI, trusted firmware need update to V1.01 and above.\n"); return PTR_ERR(dmcfreq->dmc_clk); } dmcfreq->rate = clk_get_rate(dmcfreq->dmc_clk); @@ -2569,9 +2818,11 @@ static void rockchip_dmcfreq_parse_dt(struct rockchip_dmcfreq *dmcfreq) struct device *dev = dmcfreq->dev; struct device_node *np = dev->of_node; - if (!rockchip_get_system_status_rate(np, "system-status-freq", - dmcfreq)) + if (!rockchip_get_system_status_rate(np, "system-status-freq", dmcfreq)) dmcfreq->system_status_en = true; + else if (!rockchip_get_system_status_level(np, "system-status-level", dmcfreq)) + dmcfreq->system_status_en = true; + of_property_read_u32(np, "min-cpu-freq", &dmcfreq->min_cpu_freq); of_property_read_u32(np, "upthreshold", @@ -2939,11 +3190,11 @@ static int rockchip_dmcfreq_probe(struct platform_device *pdev) if (ret) return ret; - ret = rockchip_dmcfreq_dmc_init(pdev, data); + ret = rockchip_init_opp_table(dev, NULL, "ddr_leakage", "center"); if (ret) return ret; - ret = rockchip_init_opp_table(dev, NULL, "ddr_leakage", "center"); + ret = rockchip_dmcfreq_dmc_init(pdev, data); if (ret) return ret;