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