Commit 506629c8 authored by Nicolas Boichat's avatar Nicolas Boichat Committed by Rob Herring

drm/panfrost: Add support for multiple power domains

When there is a single power domain per device, the core will
ensure the power domain is switched on (so it is technically
equivalent to having not power domain specified at all).

However, when there are multiple domains, as in MT8183 Bifrost
GPU, we need to handle them in driver code.
Signed-off-by: default avatarNicolas Boichat <drinkcat@chromium.org>
Reviewed-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Reviewed-by: default avatarSteven Price <steven.price@arm.com>
Signed-off-by: default avatarRob Herring <robh@kernel.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20200207052627.130118-6-drinkcat@chromium.org
parent 3e1399bc
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include "panfrost_device.h" #include "panfrost_device.h"
...@@ -120,6 +121,79 @@ static void panfrost_regulator_fini(struct panfrost_device *pfdev) ...@@ -120,6 +121,79 @@ static void panfrost_regulator_fini(struct panfrost_device *pfdev)
pfdev->regulators); pfdev->regulators);
} }
static void panfrost_pm_domain_fini(struct panfrost_device *pfdev)
{
int i;
for (i = 0; i < ARRAY_SIZE(pfdev->pm_domain_devs); i++) {
if (!pfdev->pm_domain_devs[i])
break;
if (pfdev->pm_domain_links[i])
device_link_del(pfdev->pm_domain_links[i]);
dev_pm_domain_detach(pfdev->pm_domain_devs[i], true);
}
}
static int panfrost_pm_domain_init(struct panfrost_device *pfdev)
{
int err;
int i, num_domains;
num_domains = of_count_phandle_with_args(pfdev->dev->of_node,
"power-domains",
"#power-domain-cells");
/*
* Single domain is handled by the core, and, if only a single power
* the power domain is requested, the property is optional.
*/
if (num_domains < 2 && pfdev->comp->num_pm_domains < 2)
return 0;
if (num_domains != pfdev->comp->num_pm_domains) {
dev_err(pfdev->dev,
"Incorrect number of power domains: %d provided, %d needed\n",
num_domains, pfdev->comp->num_pm_domains);
return -EINVAL;
}
if (WARN(num_domains > ARRAY_SIZE(pfdev->pm_domain_devs),
"Too many supplies in compatible structure.\n"))
return -EINVAL;
for (i = 0; i < num_domains; i++) {
pfdev->pm_domain_devs[i] =
dev_pm_domain_attach_by_name(pfdev->dev,
pfdev->comp->pm_domain_names[i]);
if (IS_ERR_OR_NULL(pfdev->pm_domain_devs[i])) {
err = PTR_ERR(pfdev->pm_domain_devs[i]) ? : -ENODATA;
pfdev->pm_domain_devs[i] = NULL;
dev_err(pfdev->dev,
"failed to get pm-domain %s(%d): %d\n",
pfdev->comp->pm_domain_names[i], i, err);
goto err;
}
pfdev->pm_domain_links[i] = device_link_add(pfdev->dev,
pfdev->pm_domain_devs[i], DL_FLAG_PM_RUNTIME |
DL_FLAG_STATELESS | DL_FLAG_RPM_ACTIVE);
if (!pfdev->pm_domain_links[i]) {
dev_err(pfdev->pm_domain_devs[i],
"adding device link failed!\n");
err = -ENODEV;
goto err;
}
}
return 0;
err:
panfrost_pm_domain_fini(pfdev);
return err;
}
int panfrost_device_init(struct panfrost_device *pfdev) int panfrost_device_init(struct panfrost_device *pfdev)
{ {
int err; int err;
...@@ -150,37 +224,43 @@ int panfrost_device_init(struct panfrost_device *pfdev) ...@@ -150,37 +224,43 @@ int panfrost_device_init(struct panfrost_device *pfdev)
goto err_out1; goto err_out1;
} }
err = panfrost_pm_domain_init(pfdev);
if (err)
goto err_out2;
res = platform_get_resource(pfdev->pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pfdev->pdev, IORESOURCE_MEM, 0);
pfdev->iomem = devm_ioremap_resource(pfdev->dev, res); pfdev->iomem = devm_ioremap_resource(pfdev->dev, res);
if (IS_ERR(pfdev->iomem)) { if (IS_ERR(pfdev->iomem)) {
dev_err(pfdev->dev, "failed to ioremap iomem\n"); dev_err(pfdev->dev, "failed to ioremap iomem\n");
err = PTR_ERR(pfdev->iomem); err = PTR_ERR(pfdev->iomem);
goto err_out2; goto err_out3;
} }
err = panfrost_gpu_init(pfdev); err = panfrost_gpu_init(pfdev);
if (err) if (err)
goto err_out2; goto err_out3;
err = panfrost_mmu_init(pfdev); err = panfrost_mmu_init(pfdev);
if (err) if (err)
goto err_out3; goto err_out4;
err = panfrost_job_init(pfdev); err = panfrost_job_init(pfdev);
if (err) if (err)
goto err_out4; goto err_out5;
err = panfrost_perfcnt_init(pfdev); err = panfrost_perfcnt_init(pfdev);
if (err) if (err)
goto err_out5; goto err_out6;
return 0; return 0;
err_out5: err_out6:
panfrost_job_fini(pfdev); panfrost_job_fini(pfdev);
err_out4: err_out5:
panfrost_mmu_fini(pfdev); panfrost_mmu_fini(pfdev);
err_out3: err_out4:
panfrost_gpu_fini(pfdev); panfrost_gpu_fini(pfdev);
err_out3:
panfrost_pm_domain_fini(pfdev);
err_out2: err_out2:
panfrost_reset_fini(pfdev); panfrost_reset_fini(pfdev);
err_out1: err_out1:
...@@ -196,6 +276,7 @@ void panfrost_device_fini(struct panfrost_device *pfdev) ...@@ -196,6 +276,7 @@ void panfrost_device_fini(struct panfrost_device *pfdev)
panfrost_job_fini(pfdev); panfrost_job_fini(pfdev);
panfrost_mmu_fini(pfdev); panfrost_mmu_fini(pfdev);
panfrost_gpu_fini(pfdev); panfrost_gpu_fini(pfdev);
panfrost_pm_domain_fini(pfdev);
panfrost_reset_fini(pfdev); panfrost_reset_fini(pfdev);
panfrost_regulator_fini(pfdev); panfrost_regulator_fini(pfdev);
panfrost_clk_fini(pfdev); panfrost_clk_fini(pfdev);
......
...@@ -21,6 +21,7 @@ struct panfrost_perfcnt; ...@@ -21,6 +21,7 @@ struct panfrost_perfcnt;
#define NUM_JOB_SLOTS 3 #define NUM_JOB_SLOTS 3
#define MAX_REGULATORS 2 #define MAX_REGULATORS 2
#define MAX_PM_DOMAINS 3
struct panfrost_features { struct panfrost_features {
u16 id; u16 id;
...@@ -61,6 +62,13 @@ struct panfrost_compatible { ...@@ -61,6 +62,13 @@ struct panfrost_compatible {
/* Supplies count and names. */ /* Supplies count and names. */
int num_supplies; int num_supplies;
const char * const *supply_names; const char * const *supply_names;
/*
* Number of power domains required, note that values 0 and 1 are
* handled identically, as only values > 1 need special handling.
*/
int num_pm_domains;
/* Only required if num_pm_domains > 1. */
const char * const *pm_domain_names;
}; };
struct panfrost_device { struct panfrost_device {
...@@ -73,6 +81,9 @@ struct panfrost_device { ...@@ -73,6 +81,9 @@ struct panfrost_device {
struct clk *bus_clock; struct clk *bus_clock;
struct regulator_bulk_data regulators[MAX_REGULATORS]; struct regulator_bulk_data regulators[MAX_REGULATORS];
struct reset_control *rstc; struct reset_control *rstc;
/* pm_domains for devices with more than one. */
struct device *pm_domain_devs[MAX_PM_DOMAINS];
struct device_link *pm_domain_links[MAX_PM_DOMAINS];
struct panfrost_features features; struct panfrost_features features;
const struct panfrost_compatible *comp; const struct panfrost_compatible *comp;
......
...@@ -663,6 +663,8 @@ const char * const default_supplies[] = { "mali" }; ...@@ -663,6 +663,8 @@ const char * const default_supplies[] = { "mali" };
static const struct panfrost_compatible default_data = { static const struct panfrost_compatible default_data = {
.num_supplies = ARRAY_SIZE(default_supplies), .num_supplies = ARRAY_SIZE(default_supplies),
.supply_names = default_supplies, .supply_names = default_supplies,
.num_pm_domains = 1, /* optional */
.pm_domain_names = NULL,
}; };
static const struct of_device_id dt_match[] = { static const struct of_device_id dt_match[] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment