1 // SPDX-License-Identifier: GPL-2.0
3 * SCMI Generic power domain support.
5 * Copyright (C) 2018-2021 ARM Ltd.
10 #include <linux/module.h>
11 #include <linux/pm_domain.h>
12 #include <linux/scmi_protocol.h>
14 static const struct scmi_power_proto_ops
*power_ops
;
16 struct scmi_pm_domain
{
17 struct generic_pm_domain genpd
;
18 const struct scmi_protocol_handle
*ph
;
23 #define to_scmi_pd(gpd) container_of(gpd, struct scmi_pm_domain, genpd)
25 static int scmi_pd_power(struct generic_pm_domain
*domain
, bool power_on
)
29 struct scmi_pm_domain
*pd
= to_scmi_pd(domain
);
32 state
= SCMI_POWER_STATE_GENERIC_ON
;
34 state
= SCMI_POWER_STATE_GENERIC_OFF
;
36 ret
= power_ops
->state_set(pd
->ph
, pd
->domain
, state
);
38 ret
= power_ops
->state_get(pd
->ph
, pd
->domain
, &ret_state
);
39 if (!ret
&& state
!= ret_state
)
45 static int scmi_pd_power_on(struct generic_pm_domain
*domain
)
47 return scmi_pd_power(domain
, true);
50 static int scmi_pd_power_off(struct generic_pm_domain
*domain
)
52 return scmi_pd_power(domain
, false);
55 static int scmi_pm_domain_probe(struct scmi_device
*sdev
)
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
;
69 power_ops
= handle
->devm_protocol_get(sdev
, SCMI_PROTOCOL_POWER
, &ph
);
70 if (IS_ERR(power_ops
))
71 return PTR_ERR(power_ops
);
73 num_domains
= power_ops
->num_domains_get(ph
);
74 if (num_domains
< 0) {
75 dev_err(dev
, "number of domains not found\n");
79 scmi_pd
= devm_kcalloc(dev
, num_domains
, sizeof(*scmi_pd
), GFP_KERNEL
);
83 scmi_pd_data
= devm_kzalloc(dev
, sizeof(*scmi_pd_data
), GFP_KERNEL
);
87 domains
= devm_kcalloc(dev
, num_domains
, sizeof(*domains
), GFP_KERNEL
);
91 for (i
= 0; i
< num_domains
; i
++, scmi_pd
++) {
94 if (power_ops
->state_get(ph
, i
, &state
)) {
95 dev_warn(dev
, "failed to get state for domain %d\n", i
);
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 scmi_pd
->genpd
.flags
= GENPD_FLAG_ACTIVE_WAKEUP
;
107 pm_genpd_init(&scmi_pd
->genpd
, NULL
,
108 state
== SCMI_POWER_STATE_GENERIC_OFF
);
110 domains
[i
] = &scmi_pd
->genpd
;
113 scmi_pd_data
->domains
= domains
;
114 scmi_pd_data
->num_domains
= num_domains
;
116 dev_set_drvdata(dev
, scmi_pd_data
);
118 return of_genpd_add_provider_onecell(np
, scmi_pd_data
);
121 static void scmi_pm_domain_remove(struct scmi_device
*sdev
)
124 struct genpd_onecell_data
*scmi_pd_data
;
125 struct device
*dev
= &sdev
->dev
;
126 struct device_node
*np
= dev
->of_node
;
128 of_genpd_del_provider(np
);
130 scmi_pd_data
= dev_get_drvdata(dev
);
131 for (i
= 0; i
< scmi_pd_data
->num_domains
; i
++) {
132 if (!scmi_pd_data
->domains
[i
])
134 pm_genpd_remove(scmi_pd_data
->domains
[i
]);
138 static const struct scmi_device_id scmi_id_table
[] = {
139 { SCMI_PROTOCOL_POWER
, "genpd" },
142 MODULE_DEVICE_TABLE(scmi
, scmi_id_table
);
144 static struct scmi_driver scmi_power_domain_driver
= {
145 .name
= "scmi-power-domain",
146 .probe
= scmi_pm_domain_probe
,
147 .remove
= scmi_pm_domain_remove
,
148 .id_table
= scmi_id_table
,
150 module_scmi_driver(scmi_power_domain_driver
);
152 MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
153 MODULE_DESCRIPTION("ARM SCMI power domain driver");
154 MODULE_LICENSE("GPL v2");