GNU Linux-libre 6.8.7-gnu
[releases.git] / drivers / pmdomain / arm / scmi_pm_domain.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * SCMI Generic power domain support.
4  *
5  * Copyright (C) 2018-2021 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 static const struct scmi_power_proto_ops *power_ops;
15
16 struct scmi_pm_domain {
17         struct generic_pm_domain genpd;
18         const struct scmi_protocol_handle *ph;
19         const char *name;
20         u32 domain;
21 };
22
23 #define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
24
25 static int scmi_pd_power(struct generic_pm_domain *domain, bool power_on)
26 {
27         int ret;
28         u32 state, ret_state;
29         struct scmi_pm_domain *pd = to_scmi_pd(domain);
30
31         if (power_on)
32                 state = SCMI_POWER_STATE_GENERIC_ON;
33         else
34                 state = SCMI_POWER_STATE_GENERIC_OFF;
35
36         ret = power_ops->state_set(pd->ph, pd->domain, state);
37         if (!ret)
38                 ret = power_ops->state_get(pd->ph, pd->domain, &ret_state);
39         if (!ret && state != ret_state)
40                 return -EIO;
41
42         return ret;
43 }
44
45 static int scmi_pd_power_on(struct generic_pm_domain *domain)
46 {
47         return scmi_pd_power(domain, true);
48 }
49
50 static int scmi_pd_power_off(struct generic_pm_domain *domain)
51 {
52         return scmi_pd_power(domain, false);
53 }
54
55 static int scmi_pm_domain_probe(struct scmi_device *sdev)
56 {
57         int num_domains, i;
58         struct device *dev = &sdev->dev;
59         struct device_node *np = dev->of_node;
60         struct scmi_pm_domain *scmi_pd;
61         struct genpd_onecell_data *scmi_pd_data;
62         struct generic_pm_domain **domains;
63         const struct scmi_handle *handle = sdev->handle;
64         struct scmi_protocol_handle *ph;
65
66         if (!handle)
67                 return -ENODEV;
68
69         power_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_POWER, &ph);
70         if (IS_ERR(power_ops))
71                 return PTR_ERR(power_ops);
72
73         num_domains = power_ops->num_domains_get(ph);
74         if (num_domains < 0) {
75                 dev_err(dev, "number of domains not found\n");
76                 return num_domains;
77         }
78
79         scmi_pd = devm_kcalloc(dev, num_domains, sizeof(*scmi_pd), GFP_KERNEL);
80         if (!scmi_pd)
81                 return -ENOMEM;
82
83         scmi_pd_data = devm_kzalloc(dev, sizeof(*scmi_pd_data), GFP_KERNEL);
84         if (!scmi_pd_data)
85                 return -ENOMEM;
86
87         domains = devm_kcalloc(dev, num_domains, sizeof(*domains), GFP_KERNEL);
88         if (!domains)
89                 return -ENOMEM;
90
91         for (i = 0; i < num_domains; i++, scmi_pd++) {
92                 u32 state;
93
94                 if (power_ops->state_get(ph, i, &state)) {
95                         dev_warn(dev, "failed to get state for domain %d\n", i);
96                         continue;
97                 }
98
99                 scmi_pd->domain = i;
100                 scmi_pd->ph = ph;
101                 scmi_pd->name = power_ops->name_get(ph, i);
102                 scmi_pd->genpd.name = scmi_pd->name;
103                 scmi_pd->genpd.power_off = scmi_pd_power_off;
104                 scmi_pd->genpd.power_on = scmi_pd_power_on;
105
106                 pm_genpd_init(&scmi_pd->genpd, NULL,
107                               state == SCMI_POWER_STATE_GENERIC_OFF);
108
109                 domains[i] = &scmi_pd->genpd;
110         }
111
112         scmi_pd_data->domains = domains;
113         scmi_pd_data->num_domains = num_domains;
114
115         dev_set_drvdata(dev, scmi_pd_data);
116
117         return of_genpd_add_provider_onecell(np, scmi_pd_data);
118 }
119
120 static void scmi_pm_domain_remove(struct scmi_device *sdev)
121 {
122         int i;
123         struct genpd_onecell_data *scmi_pd_data;
124         struct device *dev = &sdev->dev;
125         struct device_node *np = dev->of_node;
126
127         of_genpd_del_provider(np);
128
129         scmi_pd_data = dev_get_drvdata(dev);
130         for (i = 0; i < scmi_pd_data->num_domains; i++) {
131                 if (!scmi_pd_data->domains[i])
132                         continue;
133                 pm_genpd_remove(scmi_pd_data->domains[i]);
134         }
135 }
136
137 static const struct scmi_device_id scmi_id_table[] = {
138         { SCMI_PROTOCOL_POWER, "genpd" },
139         { },
140 };
141 MODULE_DEVICE_TABLE(scmi, scmi_id_table);
142
143 static struct scmi_driver scmi_power_domain_driver = {
144         .name = "scmi-power-domain",
145         .probe = scmi_pm_domain_probe,
146         .remove = scmi_pm_domain_remove,
147         .id_table = scmi_id_table,
148 };
149 module_scmi_driver(scmi_power_domain_driver);
150
151 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
152 MODULE_DESCRIPTION("ARM SCMI power domain driver");
153 MODULE_LICENSE("GPL v2");