msm: kgsl: Add support for device tree in kgsl
Populate the platform_data structure by reading from the Device Tree
Source file.
Change-Id: Ic8c83d3918510448b76559b095ad414d2f22c7bd
Signed-off-by: Lokesh Batra <lbatra@codeaurora.org>
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 3bfec17..dac9f47 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -15,8 +15,14 @@
#include <linux/vmalloc.h>
#include <linux/ioctl.h>
#include <linux/sched.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <mach/socinfo.h>
+#include <mach/msm_bus_board.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_dcvs.h>
+#include <mach/msm_dcvs_scm.h>
#include "kgsl.h"
#include "kgsl_pwrscale.h"
@@ -647,12 +653,473 @@
adreno_dev->gmem_size = adreno_gpulist[i].gmem_size;
}
+static struct platform_device_id adreno_id_table[] = {
+ { DEVICE_3D0_NAME, (kernel_ulong_t)&device_3d0.dev, },
+ {},
+};
+
+MODULE_DEVICE_TABLE(platform, adreno_id_table);
+
+static struct of_device_id adreno_match_table[] = {
+ { .compatible = "qcom,kgsl-3d0", },
+ {}
+};
+
+static inline int adreno_of_read_property(struct device_node *node,
+ const char *prop, unsigned int *ptr)
+{
+ int ret = of_property_read_u32(node, prop, ptr);
+ if (ret)
+ KGSL_CORE_ERR("Unable to read '%s'\n", prop);
+ return ret;
+}
+
+static struct device_node *adreno_of_find_subnode(struct device_node *parent,
+ const char *name)
+{
+ struct device_node *child;
+
+ for_each_child_of_node(parent, child) {
+ if (of_device_is_compatible(child, name))
+ return child;
+ }
+
+ return NULL;
+}
+
+static int adreno_of_get_pwrlevels(struct device_node *parent,
+ struct kgsl_device_platform_data *pdata)
+{
+ struct device_node *node, *child;
+ int ret = -EINVAL;
+
+ node = adreno_of_find_subnode(parent, "qcom,gpu-pwrlevels");
+
+ if (node == NULL) {
+ KGSL_CORE_ERR("Unable to find 'qcom,gpu-pwrlevels'\n");
+ return -EINVAL;
+ }
+
+ pdata->num_levels = 0;
+
+ for_each_child_of_node(node, child) {
+ unsigned int index;
+ struct kgsl_pwrlevel *level;
+
+ if (adreno_of_read_property(child, "reg", &index))
+ goto done;
+
+ if (index >= KGSL_MAX_PWRLEVELS) {
+ KGSL_CORE_ERR("Pwrlevel index %d is out of range\n",
+ index);
+ continue;
+ }
+
+ if (index >= pdata->num_levels)
+ pdata->num_levels = index + 1;
+
+ level = &pdata->pwrlevel[index];
+
+ if (adreno_of_read_property(child, "qcom,gpu-freq",
+ &level->gpu_freq))
+ goto done;
+
+ if (adreno_of_read_property(child, "qcom,bus-freq",
+ &level->bus_freq))
+ goto done;
+
+ if (adreno_of_read_property(child, "qcom,io-fraction",
+ &level->io_fraction))
+ level->io_fraction = 0;
+ }
+
+ if (adreno_of_read_property(parent, "qcom,initial-pwrlevel",
+ &pdata->init_level))
+ pdata->init_level = 1;
+
+ if (pdata->init_level < 0 || pdata->init_level > pdata->num_levels) {
+ KGSL_CORE_ERR("Initial power level out of range\n");
+ pdata->init_level = 1;
+ }
+
+ ret = 0;
+done:
+ return ret;
+
+}
+static void adreno_of_free_bus_scale_info(struct msm_bus_scale_pdata *pdata)
+{
+ int i;
+
+ if (pdata == NULL)
+ return;
+
+ for (i = 0; pdata->usecase && i < pdata->num_usecases; i++)
+ kfree(pdata->usecase[i].vectors);
+
+ kfree(pdata->usecase);
+ kfree(pdata);
+}
+
+struct msm_bus_scale_pdata *adreno_of_get_bus_scale(struct device_node *node)
+{
+ static int bus_vectors_src[3] = {MSM_BUS_MASTER_GRAPHICS_3D,
+ MSM_BUS_MASTER_GRAPHICS_3D_PORT1, MSM_BUS_MASTER_V_OCMEM_GFX3D};
+ static int bus_vectors_dst[2] = {MSM_BUS_SLAVE_EBI_CH0,
+ MSM_BUS_SLAVE_OCMEM};
+ const unsigned int *vectors;
+ struct msm_bus_scale_pdata *pdata;
+ int i, j, len, num_paths;
+ int ret = -EINVAL;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+
+ if (!pdata) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*pdata));
+ return ERR_PTR(-ENOMEM);
+ }
+
+ if (adreno_of_read_property(node, "qcom,grp3d-num-bus-scale-usecases",
+ &pdata->num_usecases)) {
+ pdata->num_usecases = 0;
+ goto err;
+ }
+
+ pdata->usecase = kzalloc(pdata->num_usecases *
+ sizeof(struct msm_bus_paths), GFP_KERNEL);
+
+ if (pdata->usecase == NULL) {
+ KGSL_CORE_ERR("kzalloc (%d) failed\n",
+ pdata->num_usecases * sizeof(struct msm_bus_paths));
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (adreno_of_read_property(node, "qcom,grp3d-num-vectors-per-usecase",
+ &num_paths))
+ goto err;
+
+ vectors = of_get_property(node, "qcom,grp3d-vectors", &len);
+
+ if (len != pdata->num_usecases * num_paths *
+ sizeof(struct msm_bus_vectors)) {
+ KGSL_CORE_ERR("Invalid size for the bus scale vectors\n");
+ goto err;
+ }
+
+ for (i = 0; i < pdata->num_usecases; i++) {
+ pdata->usecase[i].num_paths = num_paths;
+ pdata->usecase[i].vectors = kzalloc(num_paths *
+ sizeof(struct msm_bus_vectors),
+ GFP_KERNEL);
+ if (!pdata->usecase[i].vectors) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n",
+ num_paths * sizeof(struct msm_bus_vectors));
+ ret = -ENOMEM;
+ goto err;
+ }
+ for (j = 0; j < num_paths; j++) {
+ int index = (i * num_paths + j) * 4;
+ pdata->usecase[i].vectors[j].src =
+ bus_vectors_src[be32_to_cpu(vectors[index])];
+ pdata->usecase[i].vectors[j].dst =
+ bus_vectors_dst[
+ be32_to_cpu(vectors[index + 1])];
+ pdata->usecase[i].vectors[j].ab =
+ be32_to_cpu(vectors[index + 2]);
+ pdata->usecase[i].vectors[j].ib =
+ KGSL_CONVERT_TO_MBPS(
+ be32_to_cpu(vectors[index + 3]));
+ }
+ }
+
+ pdata->name = "grp3d";
+
+ return pdata;
+
+err:
+ adreno_of_free_bus_scale_info(pdata);
+
+ return ERR_PTR(ret);
+}
+
+static struct msm_dcvs_core_info *adreno_of_get_dcvs(struct device_node *parent)
+{
+ struct device_node *node, *child;
+ struct msm_dcvs_core_info *info = NULL;
+ int count = 0;
+ int ret = -EINVAL;
+
+ node = adreno_of_find_subnode(parent, "qcom,dcvs-core-info");
+ if (node == NULL)
+ return ERR_PTR(-EINVAL);
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+
+ if (info == NULL) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*info));
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for_each_child_of_node(node, child)
+ count++;
+
+ info->core_param.num_freq = count;
+
+ info->freq_tbl = kzalloc(info->core_param.num_freq *
+ sizeof(struct msm_dcvs_freq_entry),
+ GFP_KERNEL);
+
+ if (info->freq_tbl == NULL) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n",
+ info->core_param.num_freq *
+ sizeof(struct msm_dcvs_freq_entry));
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ for_each_child_of_node(node, child) {
+ unsigned int index;
+
+ if (adreno_of_read_property(child, "reg", &index))
+ goto err;
+
+ if (index >= info->core_param.num_freq) {
+ KGSL_CORE_ERR("DCVS freq entry %d is out of range\n",
+ index);
+ continue;
+ }
+
+ if (adreno_of_read_property(child, "qcom,freq",
+ &info->freq_tbl[index].freq))
+ goto err;
+
+ if (adreno_of_read_property(child, "qcom,idle-energy",
+ &info->freq_tbl[index].idle_energy))
+ info->freq_tbl[index].idle_energy = 0;
+
+ if (adreno_of_read_property(child, "qcom,active-energy",
+ &info->freq_tbl[index].active_energy))
+ info->freq_tbl[index].active_energy = 0;
+ }
+
+ if (adreno_of_read_property(node, "qcom,core-max-time-us",
+ &info->core_param.max_time_us))
+ goto err;
+
+ if (adreno_of_read_property(node, "qcom,algo-slack-time-us",
+ &info->algo_param.slack_time_us))
+ goto err;
+
+ if (adreno_of_read_property(node, "qcom,algo-disable-pc-threshold",
+ &info->algo_param.disable_pc_threshold))
+ goto err;
+
+ if (adreno_of_read_property(node, "qcom,algo-ss-window-size",
+ &info->algo_param.ss_window_size))
+ goto err;
+
+ if (adreno_of_read_property(node, "qcom,algo-ss-util-pct",
+ &info->algo_param.ss_util_pct))
+ goto err;
+
+ if (adreno_of_read_property(node, "qcom,algo-em-max-util-pct",
+ &info->algo_param.em_max_util_pct))
+ goto err;
+
+ if (adreno_of_read_property(node, "qcom,algo-ss-iobusy-conv",
+ &info->algo_param.ss_iobusy_conv))
+ goto err;
+
+ return info;
+
+err:
+ if (info)
+ kfree(info->freq_tbl);
+
+ kfree(info);
+
+ return ERR_PTR(ret);
+}
+
+static int adreno_of_get_iommu(struct device_node *parent,
+ struct kgsl_device_platform_data *pdata)
+{
+ struct device_node *node, *child;
+ struct kgsl_device_iommu_data *data = NULL;
+ struct kgsl_iommu_ctx *ctxs = NULL;
+ u32 reg_val[2];
+ int ctx_index = 0;
+
+ node = of_parse_phandle(parent, "iommu", 0);
+ if (node == NULL)
+ return -EINVAL;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (data == NULL) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*data));
+ goto err;
+ }
+
+ if (of_property_read_u32_array(node, "reg", reg_val, 2))
+ goto err;
+
+ data->physstart = reg_val[0];
+ data->physend = data->physstart + reg_val[1] - 1;
+
+ data->iommu_ctx_count = 0;
+
+ for_each_child_of_node(node, child)
+ data->iommu_ctx_count++;
+
+ ctxs = kzalloc(data->iommu_ctx_count * sizeof(struct kgsl_iommu_ctx),
+ GFP_KERNEL);
+
+ if (ctxs == NULL) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n",
+ data->iommu_ctx_count * sizeof(struct kgsl_iommu_ctx));
+ goto err;
+ }
+
+ for_each_child_of_node(node, child) {
+ int ret = of_property_read_string(child, "label",
+ &ctxs[ctx_index].iommu_ctx_name);
+
+ if (ret) {
+ KGSL_CORE_ERR("Unable to read KGSL IOMMU 'label'\n");
+ goto err;
+ }
+
+ if (adreno_of_read_property(child, "qcom,iommu-ctx-sids",
+ &ctxs[ctx_index].ctx_id))
+ goto err;
+
+ ctx_index++;
+ }
+
+ data->iommu_ctxs = ctxs;
+
+ pdata->iommu_data = data;
+ pdata->iommu_count = 1;
+
+ return 0;
+
+err:
+ kfree(ctxs);
+ kfree(data);
+
+ return -EINVAL;
+}
+
+static int adreno_of_get_pdata(struct platform_device *pdev)
+{
+ struct kgsl_device_platform_data *pdata = NULL;
+ struct kgsl_device *device;
+ int ret = -EINVAL;
+
+ pdev->id_entry = adreno_id_table;
+
+ pdata = pdev->dev.platform_data;
+ if (pdata)
+ return 0;
+
+ if (of_property_read_string(pdev->dev.of_node, "label", &pdev->name)) {
+ KGSL_CORE_ERR("Unable to read 'label'\n");
+ goto err;
+ }
+
+ if (adreno_of_read_property(pdev->dev.of_node, "qcom,id", &pdev->id))
+ goto err;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ KGSL_CORE_ERR("kzalloc(%d) failed\n", sizeof(*pdata));
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (adreno_of_read_property(pdev->dev.of_node, "qcom,chipid",
+ &pdata->chipid))
+ goto err;
+
+ /* pwrlevel Data */
+ ret = adreno_of_get_pwrlevels(pdev->dev.of_node, pdata);
+ if (ret)
+ goto err;
+
+ /* Default value is 83, if not found in DT */
+ if (adreno_of_read_property(pdev->dev.of_node, "qcom,idle-timeout",
+ &pdata->idle_timeout))
+ pdata->idle_timeout = 83;
+
+ if (adreno_of_read_property(pdev->dev.of_node, "qcom,nap-allowed",
+ &pdata->nap_allowed))
+ pdata->nap_allowed = 1;
+
+ if (adreno_of_read_property(pdev->dev.of_node, "qcom,clk-map",
+ &pdata->clk_map))
+ goto err;
+
+ device = (struct kgsl_device *)pdev->id_entry->driver_data;
+
+ if (device->id != KGSL_DEVICE_3D0)
+ goto err;
+
+ /* Bus Scale Data */
+
+ pdata->bus_scale_table = adreno_of_get_bus_scale(pdev->dev.of_node);
+ if (IS_ERR_OR_NULL(pdata->bus_scale_table)) {
+ ret = PTR_ERR(pdata->bus_scale_table);
+ goto err;
+ }
+
+ pdata->core_info = adreno_of_get_dcvs(pdev->dev.of_node);
+ if (IS_ERR_OR_NULL(pdata->core_info)) {
+ ret = PTR_ERR(pdata->core_info);
+ goto err;
+ }
+
+ ret = adreno_of_get_iommu(pdev->dev.of_node, pdata);
+ if (ret)
+ goto err;
+
+ pdev->dev.platform_data = pdata;
+ return 0;
+
+err:
+ if (pdata) {
+ adreno_of_free_bus_scale_info(pdata->bus_scale_table);
+ if (pdata->core_info)
+ kfree(pdata->core_info->freq_tbl);
+ kfree(pdata->core_info);
+
+ if (pdata->iommu_data)
+ kfree(pdata->iommu_data->iommu_ctxs);
+
+ kfree(pdata->iommu_data);
+ }
+
+ kfree(pdata);
+
+ return ret;
+}
+
static int __devinit
adreno_probe(struct platform_device *pdev)
{
struct kgsl_device *device;
struct adreno_device *adreno_dev;
int status = -EINVAL;
+ bool is_dt;
+
+ is_dt = of_match_device(adreno_match_table, &pdev->dev);
+
+ if (is_dt && pdev->dev.of_node) {
+ status = adreno_of_get_pdata(pdev);
+ if (status)
+ goto error_return;
+ }
device = (struct kgsl_device *)pdev->id_entry->driver_data;
adreno_dev = ADRENO_DEVICE(device);
@@ -678,6 +1145,7 @@
adreno_ringbuffer_close(&adreno_dev->ringbuffer);
error:
device->parentdev = NULL;
+error_return:
return status;
}
@@ -1926,12 +2394,6 @@
.setproperty = adreno_setproperty,
};
-static struct platform_device_id adreno_id_table[] = {
- { DEVICE_3D0_NAME, (kernel_ulong_t)&device_3d0.dev, },
- { },
-};
-MODULE_DEVICE_TABLE(platform, adreno_id_table);
-
static struct platform_driver adreno_platform_driver = {
.probe = adreno_probe,
.remove = __devexit_p(adreno_remove),
@@ -1942,6 +2404,7 @@
.owner = THIS_MODULE,
.name = DEVICE_3D_NAME,
.pm = &kgsl_pm_ops,
+ .of_match_table = adreno_match_table,
}
};