diff options
Diffstat (limited to 'drivers/gpu/msm/kgsl_pwrctrl.c')
| -rw-r--r-- | drivers/gpu/msm/kgsl_pwrctrl.c | 408 |
1 files changed, 365 insertions, 43 deletions
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c index 2eb213db..ccb37f99 100644 --- a/drivers/gpu/msm/kgsl_pwrctrl.c +++ b/drivers/gpu/msm/kgsl_pwrctrl.c @@ -21,11 +21,13 @@ #include <linux/delay.h> #include <linux/msm_adreno_devfreq.h> #include <linux/of_device.h> +#include <linux/thermal.h> #include "kgsl.h" #include "kgsl_pwrscale.h" #include "kgsl_device.h" #include "kgsl_trace.h" +#include "adreno.h" #include <soc/qcom/devfreq_devbw.h> #define KGSL_PWRFLAGS_POWER_ON 0 @@ -70,6 +72,7 @@ static const char * const clocks[] = { static unsigned int ib_votes[KGSL_MAX_BUSLEVELS]; static int last_vote_buslevel; static int max_vote_buslevel; +static int limit_pwrlevel; static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state, int requested_state); @@ -574,26 +577,14 @@ static ssize_t kgsl_pwrctrl_max_pwrlevel_show(struct device *dev, return snprintf(buf, PAGE_SIZE, "%u\n", pwr->max_pwrlevel); } -static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ struct kgsl_device *device = kgsl_device_from_dev(dev); - struct kgsl_pwrctrl *pwr; - int ret; - unsigned int level = 0; - - if (device == NULL) - return 0; - - pwr = &device->pwrctrl; - - ret = kgsl_sysfs_store(buf, &level); - if (ret) - return ret; +static void kgsl_pwrctrl_min_pwrlevel_set(struct kgsl_device *device, + int level) +{ + struct kgsl_pwrctrl *pwr = &device->pwrctrl; mutex_lock(&device->mutex); - if (level > pwr->num_pwrlevels - 2) - level = pwr->num_pwrlevels - 2; + if (level > (pwr->num_pwrlevels - 2 - limit_pwrlevel)) + level = pwr->num_pwrlevels - 2 - limit_pwrlevel; /* You can't set a minimum power level lower than the maximum */ if (level < pwr->max_pwrlevel) @@ -605,6 +596,24 @@ static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev, kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel); mutex_unlock(&device->mutex); +} + +static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + int ret; + unsigned int level = 0; + + if (device == NULL) + return 0; + + ret = kgsl_sysfs_store(buf, &level); + if (ret) + return ret; + + kgsl_pwrctrl_min_pwrlevel_set(device, level); return count; } @@ -648,24 +657,13 @@ static int _get_nearest_pwrlevel(struct kgsl_pwrctrl *pwr, unsigned int clock) return -ERANGE; } -static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static void kgsl_pwrctrl_max_clock_set(struct kgsl_device *device, int val) { - struct kgsl_device *device = kgsl_device_from_dev(dev); struct kgsl_pwrctrl *pwr; - unsigned int val = 0; - int level, ret; - - if (device == NULL) - return 0; + int level; pwr = &device->pwrctrl; - ret = kgsl_sysfs_store(buf, &val); - if (ret) - return ret; - mutex_lock(&device->mutex); level = _get_nearest_pwrlevel(pwr, val); /* If the requested power level is not supported by hw, try cycling */ @@ -699,21 +697,37 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, if (pwr->sysfs_pwr_limit) kgsl_pwr_limits_set_freq(pwr->sysfs_pwr_limit, pwr->pwrlevels[level].gpu_freq); - return count; + return; err: mutex_unlock(&device->mutex); - return count; } -static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct kgsl_device *device = kgsl_device_from_dev(dev); + unsigned int val = 0; + int ret; + + if (device == NULL) + return 0; + + ret = kgsl_sysfs_store(buf, &val); + if (ret) + return ret; + + kgsl_pwrctrl_max_clock_set(device, val); + + return count; +} + +static unsigned int kgsl_pwrctrl_max_clock_get(struct kgsl_device *device) +{ struct kgsl_pwrctrl *pwr; unsigned int freq; + if (device == NULL) return 0; pwr = &device->pwrctrl; @@ -727,7 +741,17 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev, (TH_HZ - pwr->thermal_timeout) * (hfreq / TH_HZ); } - return snprintf(buf, PAGE_SIZE, "%d\n", freq); + return freq; +} + +static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + kgsl_pwrctrl_max_clock_get(device)); } static ssize_t kgsl_pwrctrl_gpuclk_store(struct device *dev, @@ -910,9 +934,14 @@ static ssize_t kgsl_pwrctrl_gpu_available_frequencies_show( if (device == NULL) return 0; pwr = &device->pwrctrl; - for (index = 0; index < pwr->num_pwrlevels - 1; index++) - num_chars += snprintf(buf + num_chars, PAGE_SIZE, "%d ", - pwr->pwrlevels[index].gpu_freq); + for (index = 0; index < pwr->num_pwrlevels - 1; index++) { + num_chars += scnprintf(buf + num_chars, + PAGE_SIZE - num_chars - 1, + "%d ", pwr->pwrlevels[index].gpu_freq); + /* One space for trailing null and another for the newline */ + if (num_chars >= PAGE_SIZE - 2) + break; + } buf[num_chars++] = '\n'; return num_chars; } @@ -1181,6 +1210,237 @@ static ssize_t kgsl_popp_show(struct device *dev, test_bit(POPP_ON, &device->pwrscale.popp_state)); } +static ssize_t kgsl_pwrctrl_pwrscale_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + int ret; + unsigned int enable = 0; + + if (device == NULL) + return 0; + + ret = kgsl_sysfs_store(buf, &enable); + if (ret) + return ret; + + mutex_lock(&device->mutex); + + if (enable) + kgsl_pwrscale_enable(device); + else + kgsl_pwrscale_disable(device, false); + + mutex_unlock(&device->mutex); + + return count; +} + +static ssize_t kgsl_pwrctrl_pwrscale_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrscale *psc; + + if (device == NULL) + return 0; + psc = &device->pwrscale; + + return snprintf(buf, PAGE_SIZE, "%u\n", psc->enabled); +} + +static ssize_t kgsl_pwrctrl_gpu_model_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + char model_str[32] = {0}; + + if (device == NULL) + return 0; + + device->ftbl->gpu_model(device, model_str, sizeof(model_str)); + + return snprintf(buf, PAGE_SIZE, "%s\n", model_str); +} + +static ssize_t kgsl_pwrctrl_gpu_busy_percentage_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_clk_stats *stats; + unsigned int busy_percent = 0; + + if (device == NULL) + return 0; + stats = &device->pwrctrl.clk_stats; + + if (stats->total_old != 0) + busy_percent = (stats->busy_old * 100) / stats->total_old; + + ret = snprintf(buf, PAGE_SIZE, "%d %%\n", busy_percent); + + /* Reset the stats if GPU is OFF */ + if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) { + stats->busy_old = 0; + stats->total_old = 0; + } + return ret; +} + +static ssize_t kgsl_pwrctrl_min_clock_mhz_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr; + + if (device == NULL) + return 0; + pwr = &device->pwrctrl; + + return snprintf(buf, PAGE_SIZE, "%d\n", + pwr->pwrlevels[pwr->min_pwrlevel].gpu_freq / 1000000); +} + +static ssize_t kgsl_pwrctrl_min_clock_mhz_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + int level, ret; + unsigned int freq; + struct kgsl_pwrctrl *pwr; + + if (device == NULL) + return 0; + + pwr = &device->pwrctrl; + + ret = kgsl_sysfs_store(buf, &freq); + if (ret) + return ret; + + freq *= 1000000; + level = _get_nearest_pwrlevel(pwr, freq); + + if (level >= 0) + kgsl_pwrctrl_min_pwrlevel_set(device, level); + + return count; +} + +static ssize_t kgsl_pwrctrl_max_clock_mhz_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + unsigned int freq; + + if (device == NULL) + return 0; + + freq = kgsl_pwrctrl_max_clock_get(device); + + return snprintf(buf, PAGE_SIZE, "%d\n", freq / 1000000); +} + +static ssize_t kgsl_pwrctrl_max_clock_mhz_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + unsigned int val = 0; + int ret; + + if (device == NULL) + return 0; + + ret = kgsl_sysfs_store(buf, &val); + if (ret) + return ret; + + val *= 1000000; + kgsl_pwrctrl_max_clock_set(device, val); + + return count; +} + +static ssize_t kgsl_pwrctrl_clock_mhz_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + + if (device == NULL) + return 0; + + return snprintf(buf, PAGE_SIZE, "%ld\n", + kgsl_pwrctrl_active_freq(&device->pwrctrl) / 1000000); +} + +static ssize_t kgsl_pwrctrl_freq_table_mhz_show( + struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr; + int index, num_chars = 0; + + if (device == NULL) + return 0; + + pwr = &device->pwrctrl; + for (index = 0; index < pwr->num_pwrlevels - 1; index++) { + num_chars += scnprintf(buf + num_chars, + PAGE_SIZE - num_chars - 1, + "%d ", pwr->pwrlevels[index].gpu_freq / 1000000); + /* One space for trailing null and another for the newline */ + if (num_chars >= PAGE_SIZE - 2) + break; + } + + buf[num_chars++] = '\n'; + + return num_chars; +} + +static ssize_t kgsl_pwrctrl_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct kgsl_device *device = kgsl_device_from_dev(dev); + struct kgsl_pwrctrl *pwr; + int ret, id = 0; + long temperature = 0; + + if (device == NULL) + goto done; + + pwr = &device->pwrctrl; + + if (!pwr->tsens_name) + goto done; + + id = sensor_get_id((char *)pwr->tsens_name); + if (id < 0) + goto done; + + ret = sensor_get_temp(id, &temperature); + if (ret) + goto done; + + return snprintf(buf, PAGE_SIZE, "%ld\n", + temperature); +done: + return 0; +} + static DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store); static DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show, @@ -1234,6 +1494,20 @@ static DEVICE_ATTR(popp, 0644, kgsl_popp_show, kgsl_popp_store); static DEVICE_ATTR(force_non_retention_on, 0644, kgsl_pwrctrl_force_non_retention_on_show, kgsl_pwrctrl_force_non_retention_on_store); +static DEVICE_ATTR(pwrscale, 0644, + kgsl_pwrctrl_pwrscale_show, + kgsl_pwrctrl_pwrscale_store); +static DEVICE_ATTR(gpu_model, 0444, kgsl_pwrctrl_gpu_model_show, NULL); +static DEVICE_ATTR(gpu_busy_percentage, 0444, + kgsl_pwrctrl_gpu_busy_percentage_show, NULL); +static DEVICE_ATTR(min_clock_mhz, 0644, kgsl_pwrctrl_min_clock_mhz_show, + kgsl_pwrctrl_min_clock_mhz_store); +static DEVICE_ATTR(max_clock_mhz, 0644, kgsl_pwrctrl_max_clock_mhz_show, + kgsl_pwrctrl_max_clock_mhz_store); +static DEVICE_ATTR(clock_mhz, 0444, kgsl_pwrctrl_clock_mhz_show, NULL); +static DEVICE_ATTR(freq_table_mhz, 0444, + kgsl_pwrctrl_freq_table_mhz_show, NULL); +static DEVICE_ATTR(temp, 0444, kgsl_pwrctrl_temp_show, NULL); static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_gpuclk, @@ -1256,12 +1530,51 @@ static const struct device_attribute *pwrctrl_attr_list[] = { &dev_attr_bus_split, &dev_attr_default_pwrlevel, &dev_attr_popp, + &dev_attr_pwrscale, + &dev_attr_gpu_model, + &dev_attr_gpu_busy_percentage, + &dev_attr_min_clock_mhz, + &dev_attr_max_clock_mhz, + &dev_attr_clock_mhz, + &dev_attr_freq_table_mhz, + &dev_attr_temp, NULL }; +struct sysfs_link { + const char *src; + const char *dst; +}; + +static struct sysfs_link link_names[] = { + { "gpu_model", "gpu_model",}, + { "gpu_busy_percentage", "gpu_busy",}, + { "min_clock_mhz", "gpu_min_clock",}, + { "max_clock_mhz", "gpu_max_clock",}, + { "clock_mhz", "gpu_clock",}, + { "freq_table_mhz", "gpu_freq_table",}, + { "temp", "gpu_tmu",}, +}; + int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device) { - return kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list); + int i, ret; + + ret = kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list); + if (ret) + return ret; + + device->gpu_sysfs_kobj = kobject_create_and_add("gpu", kernel_kobj); + if (IS_ERR_OR_NULL(device->gpu_sysfs_kobj)) + return (device->gpu_sysfs_kobj == NULL) ? + -ENOMEM : PTR_ERR(device->gpu_sysfs_kobj); + + for (i = 0; i < ARRAY_SIZE(link_names); i++) + kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj, + &device->dev->kobj, link_names[i].src, + link_names[i].dst); + + return 0; } void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device) @@ -1701,6 +2014,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) struct msm_bus_scale_pdata *bus_scale_table; struct device_node *gpubw_dev_node = NULL; struct platform_device *p2dev; + struct adreno_device *adreno_dev = + container_of(device, struct adreno_device, dev); bus_scale_table = msm_bus_cl_get_pdata(device->pdev); if (bus_scale_table == NULL) @@ -1743,10 +2058,13 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) return -EINVAL; } + if (adreno_is_a506(adreno_dev)) + limit_pwrlevel = 1; + /* Initialize the user and thermal clock constraints */ pwr->max_pwrlevel = 0; - pwr->min_pwrlevel = pwr->num_pwrlevels - 2; + pwr->min_pwrlevel = pwr->num_pwrlevels - 2 - limit_pwrlevel; pwr->thermal_pwrlevel = 0; pwr->wakeup_maxpwrlevel = 0; @@ -1881,6 +2199,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device) (unsigned long) device); devfreq_vbif_register_callback(kgsl_get_bw); + /* temperature sensor name */ + of_property_read_string(pdev->dev.of_node, "qcom,tsens-name", + &pwr->tsens_name); + return result; } |
