OSDN Git Service

msm: kgsl: Add and link gpu sysfs nodes
authorHarshdeep Dhatt <hdhatt@codeaurora.org>
Fri, 2 Sep 2016 16:48:58 +0000 (10:48 -0600)
committerHarshdeep Dhatt <hdhatt@codeaurora.org>
Wed, 5 Oct 2016 17:05:07 +0000 (11:05 -0600)
Add new sysfs nodes which satisfy a generic format requested
by customer. Also add a new node to track GPU temperature.
Create links to these nodes at a generic location:

/sys/kernel/gpu/

CRs-Fixed: 1064728
Change-Id: I414a07ff4f9ee14b8f882d15644b06a73d5fcf76
Signed-off-by: Harshdeep Dhatt <hdhatt@codeaurora.org>
Documentation/devicetree/bindings/gpu/adreno.txt
drivers/gpu/msm/adreno.c
drivers/gpu/msm/kgsl.h
drivers/gpu/msm/kgsl_device.h
drivers/gpu/msm/kgsl_pwrctrl.c
drivers/gpu/msm/kgsl_pwrctrl.h
drivers/gpu/msm/kgsl_pwrscale.c

index fffb8cc..ca58f0d 100644 (file)
@@ -139,6 +139,10 @@ Optional Properties:
                                baseAddr - base address of the gpu channels in the qdss stm memory region
                                size     - size of the gpu stm region
 
+- qcom,tsens-name:
+                               Specify the name of GPU temperature sensor. This name will be used
+                               to get the temperature from the thermal driver API.
+
 GPU Quirks:
 - qcom,gpu-quirk-two-pass-use-wfi:
                                Signal the GPU to set Set TWOPASSUSEWFI bit in
index 4983869..1356835 100644 (file)
@@ -2799,6 +2799,18 @@ static void adreno_regulator_disable_poll(struct kgsl_device *device)
        adreno_iommu_sync(device, false);
 }
 
+static void adreno_gpu_model(struct kgsl_device *device, char *str,
+                               size_t bufsz)
+{
+       struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+       snprintf(str, bufsz, "Adreno%d%d%dv%d",
+                       ADRENO_CHIPID_CORE(adreno_dev->chipid),
+                        ADRENO_CHIPID_MAJOR(adreno_dev->chipid),
+                        ADRENO_CHIPID_MINOR(adreno_dev->chipid),
+                        ADRENO_CHIPID_PATCH(adreno_dev->chipid) + 1);
+}
+
 static const struct kgsl_functable adreno_functable = {
        /* Mandatory functions */
        .regread = adreno_regread,
@@ -2835,7 +2847,8 @@ static const struct kgsl_functable adreno_functable = {
        .regulator_disable = adreno_regulator_disable,
        .pwrlevel_change_settings = adreno_pwrlevel_change_settings,
        .regulator_disable_poll = adreno_regulator_disable_poll,
-       .clk_set_options = adreno_clk_set_options
+       .clk_set_options = adreno_clk_set_options,
+       .gpu_model = adreno_gpu_model,
 };
 
 static struct platform_driver adreno_platform_driver = {
index 25f5de6..7ac84b7 100644 (file)
@@ -579,4 +579,19 @@ static inline void __user *to_user_ptr(uint64_t address)
        return (void __user *)(uintptr_t)address;
 }
 
+static inline void kgsl_gpu_sysfs_add_link(struct kobject *dst,
+                       struct kobject *src, const char *src_name,
+                       const char *dst_name)
+{
+       struct kernfs_node *old;
+
+       if (dst == NULL || src == NULL)
+               return;
+
+       old = sysfs_get_dirent(src->sd, src_name);
+       if (IS_ERR_OR_NULL(old))
+               return;
+
+       kernfs_create_link(dst->sd, dst_name, old);
+}
 #endif /* __KGSL_H */
index 0df6dd8..81974d6 100644 (file)
@@ -167,6 +167,8 @@ struct kgsl_functable {
        void (*regulator_disable_poll)(struct kgsl_device *device);
        void (*clk_set_options)(struct kgsl_device *device,
                const char *name, struct clk *clk);
+       void (*gpu_model)(struct kgsl_device *device, char *str,
+               size_t bufsz);
 };
 
 struct kgsl_ioctl {
@@ -281,6 +283,7 @@ struct kgsl_device {
 
        /* Number of active contexts seen globally for this device */
        int active_context_count;
+       struct kobject *gpu_sysfs_kobj;
 };
 
 #define KGSL_MMU_DEVICE(_mmu) \
index 1f21788..4f80539 100644 (file)
@@ -21,6 +21,7 @@
 #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"
@@ -590,22 +591,10 @@ 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)
@@ -621,6 +610,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;
 }
@@ -664,24 +671,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 */
@@ -715,21 +711,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;
@@ -743,7 +755,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,
@@ -903,9 +925,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;
 }
@@ -1171,6 +1198,195 @@ static ssize_t kgsl_popp_show(struct device *dev,
                test_bit(POPP_ON, &device->pwrscale.popp_state));
 }
 
+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, 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, "%d\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,
@@ -1222,6 +1438,17 @@ static DEVICE_ATTR(popp, 0644, kgsl_popp_show, kgsl_popp_store);
 static DEVICE_ATTR(force_no_nap, 0644,
        kgsl_pwrctrl_force_no_nap_show,
        kgsl_pwrctrl_force_no_nap_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,
@@ -1243,12 +1470,50 @@ static const struct device_attribute *pwrctrl_attr_list[] = {
        &dev_attr_bus_split,
        &dev_attr_default_pwrlevel,
        &dev_attr_popp,
+       &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)
@@ -1860,6 +2125,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
 
        kgsl_pwrctrl_vbif_init();
 
+       /* temperature sensor name */
+       of_property_read_string(pdev->dev.of_node, "qcom,tsens-name",
+               &pwr->tsens_name);
+
        return result;
 }
 
index ae21a27..2de42d8 100644 (file)
@@ -152,6 +152,7 @@ struct kgsl_regulator {
  * @sysfs_pwr_limit - pointer to the sysfs limits node
  * 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
  */
 
 struct kgsl_pwrctrl {
@@ -204,6 +205,7 @@ struct kgsl_pwrctrl {
        struct kgsl_pwr_limit *sysfs_pwr_limit;
        unsigned int gpu_bimc_int_clk_freq;
        bool gpu_bimc_interface_enabled;
+       const char *tsens_name;
 };
 
 int kgsl_pwrctrl_init(struct kgsl_device *device);
index d90aec4..01d3b74 100644 (file)
@@ -910,6 +910,14 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor)
                pwrscale->history[i].type = i;
        }
 
+       /* Add links to the devfreq sysfs nodes */
+       kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
+                        &pwrscale->devfreqptr->dev.kobj, "governor",
+                       "gpu_governor");
+       kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
+                        &pwrscale->devfreqptr->dev.kobj,
+                       "available_governors", "gpu_available_governor");
+
        return 0;
 }
 EXPORT_SYMBOL(kgsl_pwrscale_init);