Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+37
View File
@@ -0,0 +1,37 @@
# SPDX-License-Identifier: GPL-2.0-only
config ARM_SCMI_PERF_DOMAIN
tristate "SCMI performance domain driver"
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
default ARM_SCMI_PROTOCOL
select PM_GENERIC_DOMAINS if PM
help
This enables support for the SCMI performance domains which can be
enabled or disabled via the SCP firmware.
This driver can also be built as a module. If so, the module will be
called scmi_perf_domain.
config ARM_SCMI_POWER_DOMAIN
tristate "SCMI power domain driver"
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
default ARM_SCMI_PROTOCOL
select PM_GENERIC_DOMAINS if PM
help
This enables support for the SCMI power domains which can be
enabled or disabled via the SCP firmware
This driver can also be built as a module. If so, the module
will be called scmi_pm_domain. Note this may needed early in boot
before rootfs may be available.
config ARM_SCPI_POWER_DOMAIN
tristate "SCPI power domain driver"
depends on ARM_SCPI_PROTOCOL || (COMPILE_TEST && OF)
default ARM_SCPI_PROTOCOL
select PM_GENERIC_DOMAINS if PM
help
This enables support for the SCPI power domains which can be
enabled or disabled via the SCP firmware
This driver can also be built as a module. If so, the module will be
called scpi_pm_domain.
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_ARM_SCMI_PERF_DOMAIN) += scmi_perf_domain.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o
@@ -0,0 +1,188 @@
// SPDX-License-Identifier: GPL-2.0
/*
* SCMI performance domain support.
*
* Copyright (C) 2023 Linaro Ltd.
*/
#include <linux/err.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/scmi_protocol.h>
#include <linux/slab.h>
struct scmi_perf_domain {
struct generic_pm_domain genpd;
const struct scmi_perf_proto_ops *perf_ops;
const struct scmi_protocol_handle *ph;
const struct scmi_perf_domain_info *info;
u32 domain_id;
};
#define to_scmi_pd(pd) container_of(pd, struct scmi_perf_domain, genpd)
static int
scmi_pd_set_perf_state(struct generic_pm_domain *genpd, unsigned int state)
{
struct scmi_perf_domain *pd = to_scmi_pd(genpd);
int ret;
if (!pd->info->set_perf)
return 0;
if (!state)
return 0;
ret = pd->perf_ops->level_set(pd->ph, pd->domain_id, state, false);
if (ret)
dev_warn(&genpd->dev, "Failed with %d when trying to set %d perf level",
ret, state);
return ret;
}
static int
scmi_pd_attach_dev(struct generic_pm_domain *genpd, struct device *dev)
{
struct scmi_perf_domain *pd = to_scmi_pd(genpd);
int ret;
/*
* Allow the device to be attached, but don't add the OPP table unless
* the performance level can be changed.
*/
if (!pd->info->set_perf)
return 0;
ret = pd->perf_ops->device_opps_add(pd->ph, dev, pd->domain_id);
if (ret)
dev_warn(dev, "failed to add OPPs for the device\n");
return ret;
}
static void
scmi_pd_detach_dev(struct generic_pm_domain *genpd, struct device *dev)
{
struct scmi_perf_domain *pd = to_scmi_pd(genpd);
if (!pd->info->set_perf)
return;
dev_pm_opp_remove_all_dynamic(dev);
}
static int scmi_perf_domain_probe(struct scmi_device *sdev)
{
struct device *dev = &sdev->dev;
const struct scmi_handle *handle = sdev->handle;
const struct scmi_perf_proto_ops *perf_ops;
struct scmi_protocol_handle *ph;
struct scmi_perf_domain *scmi_pd;
struct genpd_onecell_data *scmi_pd_data;
struct generic_pm_domain **domains;
int num_domains, i, ret = 0;
if (!handle)
return -ENODEV;
/* The OF node must specify us as a power-domain provider. */
if (!of_find_property(dev->of_node, "#power-domain-cells", NULL))
return 0;
perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph);
if (IS_ERR(perf_ops))
return PTR_ERR(perf_ops);
num_domains = perf_ops->num_domains_get(ph);
if (num_domains < 0) {
dev_warn(dev, "Failed with %d when getting num perf domains\n",
num_domains);
return num_domains;
} else if (!num_domains) {
return 0;
}
scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
if (!scmi_pd)
return -ENOMEM;
scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
if (!scmi_pd_data)
return -ENOMEM;
domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
if (!domains)
return -ENOMEM;
for (i = 0; i < num_domains; i++, scmi_pd++) {
scmi_pd->info = perf_ops->info_get(ph, i);
scmi_pd->domain_id = i;
scmi_pd->perf_ops = perf_ops;
scmi_pd->ph = ph;
scmi_pd->genpd.name = scmi_pd->info->name;
scmi_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON |
GENPD_FLAG_OPP_TABLE_FW |
GENPD_FLAG_DEV_NAME_FW;
scmi_pd->genpd.set_performance_state = scmi_pd_set_perf_state;
scmi_pd->genpd.attach_dev = scmi_pd_attach_dev;
scmi_pd->genpd.detach_dev = scmi_pd_detach_dev;
ret = pm_genpd_init(&scmi_pd->genpd, NULL, false);
if (ret)
goto err;
domains[i] = &scmi_pd->genpd;
}
scmi_pd_data->domains = domains;
scmi_pd_data->num_domains = num_domains;
ret = of_genpd_add_provider_onecell(dev->of_node, scmi_pd_data);
if (ret)
goto err;
dev_set_drvdata(dev, scmi_pd_data);
dev_info(dev, "Initialized %d performance domains", num_domains);
return 0;
err:
for (i--; i >= 0; i--)
pm_genpd_remove(domains[i]);
return ret;
}
static void scmi_perf_domain_remove(struct scmi_device *sdev)
{
struct device *dev = &sdev->dev;
struct genpd_onecell_data *scmi_pd_data = dev_get_drvdata(dev);
int i;
if (!scmi_pd_data)
return;
of_genpd_del_provider(dev->of_node);
for (i = 0; i < scmi_pd_data->num_domains; i++)
pm_genpd_remove(scmi_pd_data->domains[i]);
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_PERF, "perf" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_perf_domain_driver = {
.name = "scmi-perf-domain",
.probe = scmi_perf_domain_probe,
.remove = scmi_perf_domain_remove,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_perf_domain_driver);
MODULE_AUTHOR("Ulf Hansson <ulf.hansson@linaro.org>");
MODULE_DESCRIPTION("ARM SCMI perf domain driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,172 @@
// SPDX-License-Identifier: GPL-2.0
/*
* SCMI Generic power domain support.
*
* Copyright (C) 2018-2021 ARM Ltd.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_domain.h>
#include <linux/scmi_protocol.h>
static const struct scmi_power_proto_ops *power_ops;
struct scmi_pm_domain {
struct generic_pm_domain genpd;
const struct scmi_protocol_handle *ph;
const char *name;
u32 domain;
};
#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
static int scmi_pd_power(struct generic_pm_domain *domain, u32 state)
{
struct scmi_pm_domain *pd = to_scmi_pd(domain);
return power_ops->state_set(pd->ph, pd->domain, state);
}
static int scmi_pd_power_on(struct generic_pm_domain *domain)
{
return scmi_pd_power(domain, SCMI_POWER_STATE_GENERIC_ON);
}
static int scmi_pd_power_off(struct generic_pm_domain *domain)
{
return scmi_pd_power(domain, SCMI_POWER_STATE_GENERIC_OFF);
}
static int scmi_pm_domain_probe(struct scmi_device *sdev)
{
int num_domains, i, ret;
struct device *dev = &sdev->dev;
struct device_node *np = dev->of_node;
struct scmi_pm_domain *scmi_pd;
struct genpd_onecell_data *scmi_pd_data;
struct generic_pm_domain **domains;
const struct scmi_handle *handle = sdev->handle;
struct scmi_protocol_handle *ph;
if (!handle)
return -ENODEV;
power_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_POWER, &ph);
if (IS_ERR(power_ops))
return PTR_ERR(power_ops);
num_domains = power_ops->num_domains_get(ph);
if (num_domains < 0) {
dev_err(dev, "number of domains not found\n");
return num_domains;
}
scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
if (!scmi_pd)
return -ENOMEM;
scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
if (!scmi_pd_data)
return -ENOMEM;
domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
if (!domains)
return -ENOMEM;
for (i = 0; i < num_domains; i++, scmi_pd++) {
u32 state;
if (power_ops->state_get(ph, i, &state)) {
dev_warn(dev, "failed to get state for domain %d\n", i);
continue;
}
/*
* Register the explicit power on request to the firmware so
* that it is tracked as used by OSPM agent and not
* accidentally turned off with OSPM's knowledge
*/
if (state == SCMI_POWER_STATE_GENERIC_ON)
power_ops->state_set(ph, i, state);
scmi_pd->domain = i;
scmi_pd->ph = ph;
scmi_pd->name = power_ops->name_get(ph, i);
scmi_pd->genpd.name = scmi_pd->name;
scmi_pd->genpd.power_off = scmi_pd_power_off;
scmi_pd->genpd.power_on = scmi_pd_power_on;
scmi_pd->genpd.flags = GENPD_FLAG_ACTIVE_WAKEUP;
pm_genpd_init(&scmi_pd->genpd, NULL,
state == SCMI_POWER_STATE_GENERIC_OFF);
domains[i] = &scmi_pd->genpd;
}
scmi_pd_data->domains = domains;
scmi_pd_data->num_domains = num_domains;
ret = of_genpd_add_provider_onecell(np, scmi_pd_data);
if (ret)
goto err_rm_genpds;
dev_set_drvdata(dev, scmi_pd_data);
/*
* Parse (optional) power-domains-child-ids property to establish
* parent-child relationships.
*/
ret = of_genpd_add_child_ids(np, scmi_pd_data);
if (ret < 0)
dev_err(dev, "Failed to add child domain hierarchy: %d\n", ret);
dev_info(dev, "Initialized %d power domains", num_domains);
return 0;
err_rm_genpds:
for (i = num_domains - 1; i >= 0; i--)
pm_genpd_remove(domains[i]);
return ret;
}
static void scmi_pm_domain_remove(struct scmi_device *sdev)
{
int i;
struct genpd_onecell_data *scmi_pd_data;
struct device *dev = &sdev->dev;
struct device_node *np = dev->of_node;
scmi_pd_data = dev_get_drvdata(dev);
/* Remove any parent-child relationships established at probe time */
of_genpd_remove_child_ids(np, scmi_pd_data);
of_genpd_del_provider(np);
for (i = 0; i < scmi_pd_data->num_domains; i++) {
if (!scmi_pd_data->domains[i])
continue;
pm_genpd_remove(scmi_pd_data->domains[i]);
}
}
static const struct scmi_device_id scmi_id_table[] = {
{ SCMI_PROTOCOL_POWER, "genpd" },
{ },
};
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
static struct scmi_driver scmi_power_domain_driver = {
.name = "scmi-power-domain",
.probe = scmi_pm_domain_probe,
.remove = scmi_pm_domain_remove,
.id_table = scmi_id_table,
};
module_scmi_driver(scmi_power_domain_driver);
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCMI power domain driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,157 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* SCPI Generic power domain support.
*
* Copyright (C) 2016 ARM Ltd.
*/
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/scpi_protocol.h>
struct scpi_pm_domain {
struct generic_pm_domain genpd;
struct scpi_ops *ops;
u32 domain;
};
/*
* These device power state values are not well-defined in the specification.
* In case, different implementations use different values, we can make these
* specific to compatibles rather than getting these values from device tree.
*/
enum scpi_power_domain_state {
SCPI_PD_STATE_ON = 0,
SCPI_PD_STATE_OFF = 3,
};
#define to_scpi_pd(gpd) container_of(gpd, struct scpi_pm_domain, genpd)
static int scpi_pd_power(struct scpi_pm_domain *pd, bool power_on)
{
int ret;
enum scpi_power_domain_state state;
if (power_on)
state = SCPI_PD_STATE_ON;
else
state = SCPI_PD_STATE_OFF;
ret = pd->ops->device_set_power_state(pd->domain, state);
if (ret)
return ret;
return !(state == pd->ops->device_get_power_state(pd->domain));
}
static int scpi_pd_power_on(struct generic_pm_domain *domain)
{
struct scpi_pm_domain *pd = to_scpi_pd(domain);
return scpi_pd_power(pd, true);
}
static int scpi_pd_power_off(struct generic_pm_domain *domain)
{
struct scpi_pm_domain *pd = to_scpi_pd(domain);
return scpi_pd_power(pd, false);
}
static int scpi_pm_domain_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct scpi_pm_domain *scpi_pd;
struct genpd_onecell_data *scpi_pd_data;
struct generic_pm_domain **domains;
struct scpi_ops *scpi_ops;
int ret, num_domains, i;
scpi_ops = get_scpi_ops();
if (!scpi_ops)
return -EPROBE_DEFER;
if (!np) {
dev_err(dev, "device tree node not found\n");
return -ENODEV;
}
if (!scpi_ops->device_set_power_state ||
!scpi_ops->device_get_power_state) {
dev_err(dev, "power domains not supported in the firmware\n");
return -ENODEV;
}
ret = of_property_read_u32(np, "num-domains", &num_domains);
if (ret) {
dev_err(dev, "number of domains not found\n");
return -EINVAL;
}
scpi_pd = devm_kcalloc(dev, num_domains, sizeof(*scpi_pd), GFP_KERNEL);
if (!scpi_pd)
return -ENOMEM;
scpi_pd_data = devm_kzalloc(dev, sizeof(*scpi_pd_data), GFP_KERNEL);
if (!scpi_pd_data)
return -ENOMEM;
domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
if (!domains)
return -ENOMEM;
for (i = 0; i < num_domains; i++, scpi_pd++) {
domains[i] = &scpi_pd->genpd;
scpi_pd->domain = i;
scpi_pd->ops = scpi_ops;
scpi_pd->genpd.name = devm_kasprintf(dev, GFP_KERNEL,
"%pOFn.%d", np, i);
if (!scpi_pd->genpd.name) {
dev_err(dev, "Failed to allocate genpd name:%pOFn.%d\n",
np, i);
continue;
}
scpi_pd->genpd.power_off = scpi_pd_power_off;
scpi_pd->genpd.power_on = scpi_pd_power_on;
/*
* Treat all power domains as off at boot.
*
* The SCP firmware itself may have switched on some domains,
* but for reference counting purpose, keep it this way.
*/
pm_genpd_init(&scpi_pd->genpd, NULL, true);
}
scpi_pd_data->domains = domains;
scpi_pd_data->num_domains = num_domains;
of_genpd_add_provider_onecell(np, scpi_pd_data);
return 0;
}
static const struct of_device_id scpi_power_domain_ids[] = {
{ .compatible = "arm,scpi-power-domains", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, scpi_power_domain_ids);
static struct platform_driver scpi_power_domain_driver = {
.driver = {
.name = "scpi_power_domain",
.of_match_table = scpi_power_domain_ids,
},
.probe = scpi_pm_domain_probe,
};
module_platform_driver(scpi_power_domain_driver);
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCPI power domain driver");
MODULE_LICENSE("GPL v2");