Skip to content

Commit 898216c

Browse files
committed
firmware: arm_scmi: add device power domain support using genpd
This patch hooks up the support for device power domain provided by SCMI using the Linux generic power domain infrastructure. Cc: Kevin Hilman <[email protected]> Reviewed-by: Ulf Hansson <[email protected]> Signed-off-by: Sudeep Holla <[email protected]>
1 parent 907b6d1 commit 898216c

File tree

3 files changed

+143
-0
lines changed

3 files changed

+143
-0
lines changed

drivers/firmware/Kconfig

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ config ARM_SCMI_PROTOCOL
4040
This protocol library provides interface for all the client drivers
4141
making use of the features offered by the SCMI.
4242

43+
config ARM_SCMI_POWER_DOMAIN
44+
tristate "SCMI power domain driver"
45+
depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
46+
default y
47+
select PM_GENERIC_DOMAINS if PM
48+
help
49+
This enables support for the SCMI power domains which can be
50+
enabled or disabled via the SCP firmware
51+
52+
This driver can also be built as a module. If so, the module
53+
will be called scmi_pm_domain. Note this may needed early in boot
54+
before rootfs may be available.
55+
4356
config ARM_SCPI_PROTOCOL
4457
tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
4558
depends on ARM || ARM64 || COMPILE_TEST

drivers/firmware/arm_scmi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o
22
scmi-bus-y = bus.o
33
scmi-driver-y = driver.o
44
scmi-protocols-y = base.o clock.o perf.o power.o sensors.o
5+
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* SCMI Generic power domain support.
4+
*
5+
* Copyright (C) 2018 ARM Ltd.
6+
*/
7+
8+
#include <linux/err.h>
9+
#include <linux/io.h>
10+
#include <linux/module.h>
11+
#include <linux/pm_domain.h>
12+
#include <linux/scmi_protocol.h>
13+
14+
struct scmi_pm_domain {
15+
struct generic_pm_domain genpd;
16+
const struct scmi_handle *handle;
17+
const char *name;
18+
u32 domain;
19+
};
20+
21+
#define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
22+
23+
static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
24+
{
25+
int ret;
26+
u32 state, ret_state;
27+
struct scmi_pm_domain *pd = to_scmi_pd(domain);
28+
const struct scmi_power_ops *ops = pd->handle->power_ops;
29+
30+
if (power_on)
31+
state = SCMI_POWER_STATE_GENERIC_ON;
32+
else
33+
state = SCMI_POWER_STATE_GENERIC_OFF;
34+
35+
ret = ops->state_set(pd->handle, pd->domain, state);
36+
if (!ret)
37+
ret = ops->state_get(pd->handle, pd->domain, &ret_state);
38+
if (!ret && state != ret_state)
39+
return -EIO;
40+
41+
return ret;
42+
}
43+
44+
static int scmi_pd_power_on(struct generic_pm_domain *domain)
45+
{
46+
return scmi_pd_power(domain, true);
47+
}
48+
49+
static int scmi_pd_power_off(struct generic_pm_domain *domain)
50+
{
51+
return scmi_pd_power(domain, false);
52+
}
53+
54+
static int scmi_pm_domain_probe(struct scmi_device *sdev)
55+
{
56+
int num_domains, i;
57+
struct device *dev = &sdev->dev;
58+
struct device_node *np = dev->of_node;
59+
struct scmi_pm_domain *scmi_pd;
60+
struct genpd_onecell_data *scmi_pd_data;
61+
struct generic_pm_domain **domains;
62+
const struct scmi_handle *handle = sdev->handle;
63+
64+
if (!handle || !handle->power_ops)
65+
return -ENODEV;
66+
67+
num_domains = handle->power_ops->num_domains_get(handle);
68+
if (num_domains < 0) {
69+
dev_err(dev, "number of domains not found\n");
70+
return num_domains;
71+
}
72+
73+
scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
74+
if (!scmi_pd)
75+
return -ENOMEM;
76+
77+
scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
78+
if (!scmi_pd_data)
79+
return -ENOMEM;
80+
81+
domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
82+
if (!domains)
83+
return -ENOMEM;
84+
85+
for (i = 0; i < num_domains; i++, scmi_pd++) {
86+
u32 state;
87+
88+
domains[i] = &scmi_pd->genpd;
89+
90+
scmi_pd->domain = i;
91+
scmi_pd->handle = handle;
92+
scmi_pd->name = handle->power_ops->name_get(handle, i);
93+
scmi_pd->genpd.name = scmi_pd->name;
94+
scmi_pd->genpd.power_off = scmi_pd_power_off;
95+
scmi_pd->genpd.power_on = scmi_pd_power_on;
96+
97+
if (handle->power_ops->state_get(handle, i, &state)) {
98+
dev_warn(dev, "failed to get state for domain %d\n", i);
99+
continue;
100+
}
101+
102+
pm_genpd_init(&scmi_pd->genpd, NULL,
103+
state == SCMI_POWER_STATE_GENERIC_OFF);
104+
}
105+
106+
scmi_pd_data->domains = domains;
107+
scmi_pd_data->num_domains = num_domains;
108+
109+
of_genpd_add_provider_onecell(np, scmi_pd_data);
110+
111+
return 0;
112+
}
113+
114+
static const struct scmi_device_id scmi_id_table[] = {
115+
{ SCMI_PROTOCOL_POWER },
116+
{ },
117+
};
118+
MODULE_DEVICE_TABLE(scmi, scmi_id_table);
119+
120+
static struct scmi_driver scmi_power_domain_driver = {
121+
.name = "scmi-power-domain",
122+
.probe = scmi_pm_domain_probe,
123+
.id_table = scmi_id_table,
124+
};
125+
module_scmi_driver(scmi_power_domain_driver);
126+
127+
MODULE_AUTHOR("Sudeep Holla <[email protected]>");
128+
MODULE_DESCRIPTION("ARM SCMI power domain driver");
129+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)