diff --git a/Documentation/devicetree/bindings/gpu/adreno.txt b/Documentation/devicetree/bindings/gpu/adreno.txt index 453223dc195a..1ca89b587077 100644 --- a/Documentation/devicetree/bindings/gpu/adreno.txt +++ b/Documentation/devicetree/bindings/gpu/adreno.txt @@ -110,6 +110,17 @@ Optional Properties: - qcom,l2pc-cpu-mask-latency: The CPU mask latency in microseconds to avoid L2PC on masked CPUs. + +- qcom,gpu-cx-ipeak: + To handle Cx peak current limit. + + phandle - phandle of cx ipeak device node + bit - bit number of client in relevant register +- qcom,gpu-cx-ipeak-clk: + GPU clock threshold for Cx Ipeak voting. KGSL votes + to Cx Ipeak driver when GPU clock crosses this threshold. + Cx Ipeak can limit peak current based on voting from other clients. + - qcom,force-32bit: Force the GPU to use 32 bit data sizes even if it is capable of doing 64 bit. diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index fe6aa45901d0..e639e197de93 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -361,6 +361,26 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, if (new_level == old_level) return; + if (pwr->gpu_cx_ipeak) { + unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq; + unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq; + + /* + * Set Cx ipeak vote for GPU if it tries to cross + * threshold frequency. + */ + if (old_freq < pwr->gpu_cx_ipeak_clk && + new_freq >= pwr->gpu_cx_ipeak_clk) { + int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, true); + + if (ret) { + KGSL_PWR_ERR(device, + "cx_ipeak_update failed %d\n", ret); + return; + } + } + } + kgsl_pwrscale_update_stats(device); /* @@ -422,6 +442,24 @@ void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device, /* Timestamp the frequency change */ device->pwrscale.freq_change_time = ktime_to_ms(ktime_get()); + + if (pwr->gpu_cx_ipeak) { + unsigned int old_freq = pwr->pwrlevels[old_level].gpu_freq; + unsigned int new_freq = pwr->pwrlevels[new_level].gpu_freq; + + /* + * Reset Cx ipeak vote for GPU if it goes below + * threshold frequency. + */ + if (old_freq >= pwr->gpu_cx_ipeak_clk && + new_freq < pwr->gpu_cx_ipeak_clk) { + int ret = cx_ipeak_update(pwr->gpu_cx_ipeak, false); + + if (ret) + KGSL_PWR_ERR(device, + "cx_ipeak_update failed %d\n", ret); + } + } } EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change); @@ -2217,8 +2255,37 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) of_property_read_string(pdev->dev.of_node, "qcom,tsens-name", &pwr->tsens_name); + /* Cx ipeak client support */ + if (of_find_property(pdev->dev.of_node, "qcom,gpu-cx-ipeak", NULL)) { + if (!of_property_read_u32(pdev->dev.of_node, + "qcom,gpu-cx-ipeak-clk", &pwr->gpu_cx_ipeak_clk)) { + pwr->gpu_cx_ipeak = cx_ipeak_register(pdev->dev.of_node, + "qcom,gpu-cx-ipeak"); + } else { + KGSL_PWR_ERR(device, "failed to get gpu cxip clk\n"); + result = -EINVAL; + goto error_cleanup_pwr_limit; + } + + if (IS_ERR(pwr->gpu_cx_ipeak)) { + result = PTR_ERR(pwr->gpu_cx_ipeak); + KGSL_PWR_ERR(device, + "Failed to register Cx ipeak client %d\n", + result); + goto error_cleanup_pwr_limit; + } + } return result; +error_cleanup_pwr_limit: + pwr->power_flags = 0; + + if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) { + list_del(&pwr->sysfs_pwr_limit->node); + kfree(pwr->sysfs_pwr_limit); + pwr->sysfs_pwr_limit = NULL; + } + kfree(pwr->bus_ib); error_cleanup_pcl: _close_pcl(pwr); error_cleanup_ocmem_pcl: @@ -2238,6 +2305,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device) KGSL_PWR_INFO(device, "close device %d\n", device->id); + cx_ipeak_unregister(pwr->gpu_cx_ipeak); + pwr->power_flags = 0; if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) { diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h index 2de42d87bcbe..42f918b80fcd 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.h +++ b/drivers/gpu/msm/kgsl_pwrctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -14,6 +14,7 @@ #define __KGSL_PWRCTRL_H #include +#include /***************************************************************************** ** power flags @@ -153,6 +154,8 @@ struct kgsl_regulator { * isense_clk_indx - index of isense clock, 0 if no isense * isense_clk_on_level - isense clock rate is XO rate below this level. * tsens_name - pointer to temperature sensor name of GPU temperature sensor + * gpu_cx_ipeak - pointer to cx ipeak client used by GPU + * gpu_cx_ipeak_clk - GPU threshold frequency to call cx ipeak driver API */ struct kgsl_pwrctrl { @@ -206,6 +209,8 @@ struct kgsl_pwrctrl { unsigned int gpu_bimc_int_clk_freq; bool gpu_bimc_interface_enabled; const char *tsens_name; + struct cx_ipeak_client *gpu_cx_ipeak; + unsigned int gpu_cx_ipeak_clk; }; int kgsl_pwrctrl_init(struct kgsl_device *device);