1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2019-2021, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Description:
8 * N1SDP Remote Power Domain (PD) management driver.
9 */
10
11 #include "n1sdp_core.h"
12
13 #include <mod_n1sdp_c2c_i2c.h>
14 #include <mod_n1sdp_remote_pd.h>
15 #include <mod_power_domain.h>
16
17 #include <fwk_assert.h>
18 #include <fwk_id.h>
19 #include <fwk_log.h>
20 #include <fwk_mm.h>
21 #include <fwk_module.h>
22 #include <fwk_module_idx.h>
23 #include <fwk_status.h>
24
25 #include <stddef.h>
26 #include <stdint.h>
27
28 /* N1SDP remote PD driver device context */
29 struct n1sdp_remote_pd_device_ctx {
30 /* Pointer to the device configuration */
31 const struct mod_n1sdp_remote_pd_config *config;
32
33 /* Identifier of the entity bound to the power domain driver API */
34 fwk_id_t bound_id;
35
36 /* Power module driver input API */
37 struct mod_pd_driver_input_api *pd_driver_input_api;
38 };
39
40 /* N1SDP remote PD driver module context */
41 struct n1sdp_remote_pd_ctx {
42 /* Table of device contexts */
43 struct n1sdp_remote_pd_device_ctx *dev_ctx_table;
44
45 /* Total power domains */
46 unsigned int pd_count;
47
48 /* C2C power domain API */
49 struct n1sdp_c2c_pd_api *c2c_pd_api;
50
51 /* Logical SYSTOP PD state */
52 unsigned int logical_systop_state;
53 };
54
55 static struct n1sdp_remote_pd_ctx remote_pd_ctx;
56
57 /*
58 * Remote PD API
59 */
remote_pd_get_state(fwk_id_t pd_id,unsigned int * state)60 static int remote_pd_get_state(fwk_id_t pd_id, unsigned int *state)
61 {
62 unsigned int element_id;
63
64 element_id = fwk_id_get_element_idx(pd_id);
65 fwk_assert(element_id < remote_pd_ctx.pd_count);
66
67 /*
68 * The last element is logical systop so report current state from
69 * context variable.
70 */
71 if (element_id == (remote_pd_ctx.pd_count - 1)) {
72 *state = remote_pd_ctx.logical_systop_state;
73 return FWK_SUCCESS;
74 }
75
76 return remote_pd_ctx.c2c_pd_api->get_state(
77 N1SDP_C2C_CMD_POWER_DOMAIN_GET_STATE, (uint8_t)element_id,
78 state);
79 }
80
remote_pd_set_state(fwk_id_t pd_id,unsigned int state)81 static int remote_pd_set_state(fwk_id_t pd_id, unsigned int state)
82 {
83 int status;
84 unsigned int element_id;
85 struct n1sdp_remote_pd_device_ctx *dev_ctx;
86
87 element_id = fwk_id_get_element_idx(pd_id);
88 fwk_assert(element_id < remote_pd_ctx.pd_count);
89
90 dev_ctx = &remote_pd_ctx.dev_ctx_table[element_id];
91
92 /*
93 * The last element is logical systop so no action is required
94 * except reporting the state transition.
95 */
96 if (element_id == (remote_pd_ctx.pd_count - 1)) {
97 remote_pd_ctx.logical_systop_state = state;
98 return dev_ctx->pd_driver_input_api->report_power_state_transition(
99 dev_ctx->bound_id, state);
100 }
101
102 switch (state) {
103 case MOD_PD_STATE_OFF:
104 status = remote_pd_ctx.c2c_pd_api->set_state(
105 N1SDP_C2C_CMD_POWER_DOMAIN_OFF, (uint8_t)element_id,
106 (uint8_t)dev_ctx->config->pd_type);
107 if (status != FWK_SUCCESS) {
108 return status;
109 }
110
111 status = dev_ctx->pd_driver_input_api->report_power_state_transition(
112 dev_ctx->bound_id, MOD_PD_STATE_OFF);
113 fwk_assert(status == FWK_SUCCESS);
114 break;
115
116 case MOD_PD_STATE_ON:
117 status = remote_pd_ctx.c2c_pd_api->set_state(
118 N1SDP_C2C_CMD_POWER_DOMAIN_ON, (uint8_t)element_id,
119 (uint8_t)dev_ctx->config->pd_type);
120 if (status != FWK_SUCCESS) {
121 return status;
122 }
123
124 status = dev_ctx->pd_driver_input_api->report_power_state_transition(
125 dev_ctx->bound_id, MOD_PD_STATE_ON);
126 fwk_assert(status == FWK_SUCCESS);
127 break;
128
129 default:
130 FWK_LOG_ERR(
131 "[C2C] Requested CPU power state (%i) is not supported!", state);
132 return FWK_E_PARAM;
133 }
134
135 return FWK_SUCCESS;
136 }
137
remote_pd_reset(fwk_id_t pd_id)138 static int remote_pd_reset(fwk_id_t pd_id)
139 {
140 int status;
141
142 status = remote_pd_set_state(pd_id, MOD_PD_STATE_OFF);
143 if (status == FWK_SUCCESS) {
144 status = remote_pd_set_state(pd_id, MOD_PD_STATE_ON);
145 }
146
147 return status;
148 }
149
remote_pd_prepare_for_system_suspend(fwk_id_t pd_id)150 static int remote_pd_prepare_for_system_suspend(fwk_id_t pd_id)
151 {
152 return remote_pd_set_state(pd_id, MOD_PD_STATE_OFF);
153 }
154
remote_pd_shutdown(fwk_id_t pd_id,enum mod_pd_system_shutdown system_shutdown)155 static int remote_pd_shutdown(fwk_id_t pd_id,
156 enum mod_pd_system_shutdown system_shutdown)
157 {
158 return FWK_SUCCESS;
159 }
160
161 static const struct mod_pd_driver_api remote_pd_driver = {
162 .set_state = remote_pd_set_state,
163 .get_state = remote_pd_get_state,
164 .reset = remote_pd_reset,
165 .prepare_core_for_system_suspend =
166 remote_pd_prepare_for_system_suspend,
167 .shutdown = remote_pd_shutdown,
168 };
169
170 /*
171 * Framework handlers
172 */
173
remote_pd_init(fwk_id_t module_id,unsigned int device_count,const void * unused)174 static int remote_pd_init(fwk_id_t module_id, unsigned int device_count,
175 const void *unused)
176 {
177 if (!n1sdp_is_multichip_enabled() || (n1sdp_get_chipid() != 0x0)) {
178 return FWK_SUCCESS;
179 }
180
181 if (device_count == 0) {
182 return FWK_E_PARAM;
183 }
184
185 remote_pd_ctx.dev_ctx_table = fwk_mm_calloc(device_count,
186 sizeof(remote_pd_ctx.dev_ctx_table[0]));
187
188 remote_pd_ctx.pd_count = device_count;
189
190 return FWK_SUCCESS;
191 }
192
remote_pd_element_init(fwk_id_t device_id,unsigned int slot_count,const void * data)193 static int remote_pd_element_init(fwk_id_t device_id, unsigned int slot_count,
194 const void *data)
195 {
196 struct mod_n1sdp_remote_pd_config *config =
197 (struct mod_n1sdp_remote_pd_config *)data;
198 struct n1sdp_remote_pd_device_ctx *dev_ctx;
199
200 if (!n1sdp_is_multichip_enabled() || (n1sdp_get_chipid() != 0x0)) {
201 return FWK_SUCCESS;
202 }
203
204 dev_ctx = &remote_pd_ctx.dev_ctx_table[fwk_id_get_element_idx(device_id)];
205 dev_ctx->config = config;
206
207 return FWK_SUCCESS;
208 }
209
remote_pd_bind(fwk_id_t id,unsigned int round)210 static int remote_pd_bind(fwk_id_t id, unsigned int round)
211 {
212 int status;
213 struct n1sdp_remote_pd_device_ctx *dev_ctx;
214
215 if (!n1sdp_is_multichip_enabled() || (n1sdp_get_chipid() != 0x0)) {
216 return FWK_SUCCESS;
217 }
218
219 if ((round == 0) && fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) {
220 status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_N1SDP_C2C),
221 FWK_ID_API(FWK_MODULE_IDX_N1SDP_C2C,
222 N1SDP_C2C_API_IDX_PD),
223 &remote_pd_ctx.c2c_pd_api);
224 if (status != FWK_SUCCESS) {
225 return status;
226 }
227 }
228
229 if ((round == 1) && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
230 dev_ctx = &remote_pd_ctx.dev_ctx_table[fwk_id_get_element_idx(id)];
231 status = fwk_module_bind(dev_ctx->bound_id,
232 mod_pd_api_id_driver_input,
233 &dev_ctx->pd_driver_input_api);
234 if (status != FWK_SUCCESS) {
235 return status;
236 }
237 }
238
239 return FWK_SUCCESS;
240 }
241
remote_pd_process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)242 static int remote_pd_process_bind_request(fwk_id_t source_id,
243 fwk_id_t target_id,
244 fwk_id_t api_id,
245 const void **api)
246 {
247 struct n1sdp_remote_pd_device_ctx *dev_ctx;
248
249 if (!n1sdp_is_multichip_enabled() || (n1sdp_get_chipid() != 0x0)) {
250 return FWK_E_ACCESS;
251 }
252
253 if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) {
254 return FWK_E_ACCESS;
255 }
256
257 dev_ctx = &remote_pd_ctx.dev_ctx_table[fwk_id_get_element_idx(target_id)];
258
259 *api = &remote_pd_driver;
260 dev_ctx->bound_id = source_id;
261
262 return FWK_SUCCESS;
263 }
264
265 const struct fwk_module module_n1sdp_remote_pd = {
266 .type = FWK_MODULE_TYPE_DRIVER,
267 .api_count = N1SDP_REMOTE_PD_API_COUNT,
268 .init = remote_pd_init,
269 .element_init = remote_pd_element_init,
270 .bind = remote_pd_bind,
271 .process_bind_request = remote_pd_process_bind_request,
272 };
273