aboutsummaryrefslogtreecommitdiff
path: root/module/system_power/src/mod_system_power.c
blob: 21f618cfa940f6d0eb4e05cad6be54471988f90a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
/*
 * Arm SCP/MCP Software
 * Copyright (c) 2015-2019, Arm Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Description:
 *     System Power Support.
 */

#include <stdint.h>
#include <fwk_assert.h>
#include <fwk_id.h>
#include <fwk_interrupt.h>
#include <fwk_macros.h>
#include <fwk_mm.h>
#include <fwk_module.h>
#include <fwk_module_idx.h>
#include <mod_log.h>
#include <mod_system_power.h>
#include <mod_power_domain.h>

/* Module context */
struct system_power_ctx {
    /* Log API pointer */
    const struct mod_log_api *log_api;

    /* SYS0 power domain driver API pointer */
    const struct mod_pd_driver_api *sys0_api;

    /* SYS1 power domain driver API pointer*/
    const struct mod_pd_driver_api *sys1_api;

    /* Pointer to array of extended PPU power domain driver APIs */
    const struct mod_pd_driver_api *ext_ppu_apis;

    /* Power domain module restricted API pointer */
    const struct mod_pd_restricted_api *mod_pd_restricted_api;

    /* Power domain module driver input API pointer */
    const struct mod_pd_driver_input_api *mod_pd_driver_input_api;

    /* Driver API pointer */
    const struct mod_system_power_driver_api *driver_api;

    /* Power domain module identifier of the system power domain */
    fwk_id_t mod_pd_system_id;

    /* Current system-level power state */
    unsigned int state;

    /* Pointer to module config */
    const struct mod_system_power_config *config;
};

struct system_power_isr {
    unsigned int interrupt;
    void (*handler)(void);
};

static struct system_power_ctx system_power_ctx;

static void ext_ppus_set_state(enum mod_pd_state state)
{
    unsigned int i;

    for (i = 0; i < system_power_ctx.config->ext_ppus_count; i++) {
        system_power_ctx.ext_ppu_apis[i].set_state(
            system_power_ctx.config->ext_ppus[i].ppu_id,
            state);
    }
}

/*
 * Functions fulfilling the Power Domain module's driver API
 */

static int system_power_set_state(fwk_id_t pd_id, unsigned int state)
{
    int status;

    status = fwk_module_check_call(pd_id);
    if (status != FWK_SUCCESS)
        return status;

    switch (state) {
    case MOD_PD_STATE_ON:
        fwk_interrupt_disable(system_power_ctx.config->soc_wakeup_irq);

        system_power_ctx.sys0_api->set_state(
            system_power_ctx.config->ppu_sys0_id, MOD_PD_STATE_ON);
        system_power_ctx.sys0_api->set_state(
            system_power_ctx.config->ppu_sys1_id, MOD_PD_STATE_ON);

        ext_ppus_set_state(MOD_PD_STATE_ON);

        break;

    case MOD_SYSTEM_POWER_POWER_STATE_SLEEP0:
        ext_ppus_set_state(MOD_PD_STATE_OFF);

        system_power_ctx.sys0_api->set_state(
            system_power_ctx.config->ppu_sys0_id, MOD_PD_STATE_OFF);
        system_power_ctx.sys0_api->set_state(
            system_power_ctx.config->ppu_sys1_id, MOD_PD_STATE_ON);

        fwk_interrupt_enable(system_power_ctx.config->soc_wakeup_irq);

        break;

    case MOD_PD_STATE_OFF:
        fwk_interrupt_disable(system_power_ctx.config->soc_wakeup_irq);
        ext_ppus_set_state(MOD_PD_STATE_OFF);

        system_power_ctx.sys0_api->set_state(
            system_power_ctx.config->ppu_sys0_id, MOD_PD_STATE_OFF);
        system_power_ctx.sys0_api->set_state(
            system_power_ctx.config->ppu_sys1_id, MOD_PD_STATE_OFF);

        break;

    default:
        return FWK_E_SUPPORT;
    }

    return FWK_SUCCESS;
}

static int system_power_get_state(fwk_id_t pd_id, unsigned int *state)
{
    int status;

    status = fwk_module_check_call(pd_id);
    if (status != FWK_SUCCESS)
        return status;

    *state = system_power_ctx.state;

    return FWK_SUCCESS;
}

static int system_power_reset(fwk_id_t pd_id)
{
    return FWK_E_SUPPORT;
}

static int system_power_shutdown(fwk_id_t pd_id,
                                enum mod_pd_system_shutdown system_shutdown)
{
    int status;

    status = system_power_set_state(pd_id, MOD_PD_STATE_OFF);
    if (status != FWK_SUCCESS)
        return status;

    return system_power_ctx.driver_api->system_shutdown(system_shutdown);
}

static void soc_wakeup_handler(void)
{
    int status;
    fwk_id_t pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, 0);
    uint32_t state = MOD_PD_COMPOSITE_STATE(MOD_PD_LEVEL_2,
                                            0,
                                            MOD_PD_STATE_ON,
                                            MOD_PD_STATE_ON,
                                            MOD_PD_STATE_ON);

    status =
        system_power_ctx.mod_pd_restricted_api->set_composite_state_async(
            pd_id, false, state);
    assert(status == FWK_SUCCESS);
    (void)status;
}

static const struct mod_pd_driver_api system_power_power_domain_driver_api = {
    .set_state = system_power_set_state,
    .get_state = system_power_get_state,
    .reset = system_power_reset,
    .shutdown = system_power_shutdown
};

/*
 * Functions fulfilling the Power Domain module's driver input API
 */

static int system_power_report_power_state_transition(fwk_id_t module_id,
    unsigned int state)
{
    int status;
    unsigned int sys0_state, sys1_state;

    system_power_ctx.sys0_api->get_state(system_power_ctx.config->ppu_sys0_id,
                                        &sys0_state);
    system_power_ctx.sys1_api->get_state(system_power_ctx.config->ppu_sys1_id,
                                        &sys1_state);

    if ((sys0_state == MOD_PD_STATE_ON) && (sys1_state == MOD_PD_STATE_ON))
        system_power_ctx.state = MOD_PD_STATE_ON;
    else if ((sys0_state == MOD_PD_STATE_OFF) &&
             (sys1_state == MOD_PD_STATE_ON))
        system_power_ctx.state = MOD_SYSTEM_POWER_POWER_STATE_SLEEP0;
    else
        system_power_ctx.state = MOD_PD_STATE_OFF;

    status =
        system_power_ctx.mod_pd_driver_input_api->report_power_state_transition(
            system_power_ctx.mod_pd_system_id, system_power_ctx.state);
    assert(status == FWK_SUCCESS);
    (void)status;

    return FWK_SUCCESS;
}

static const struct mod_pd_driver_input_api
    system_power_power_domain_driver_input_api = {
    .report_power_state_transition = system_power_report_power_state_transition
};

/*
 * Functions fulfilling the framework's module interface
 */

static int system_power_mod_init(fwk_id_t module_id,
                                unsigned int unused,
                                const void *data)
{
    assert(data != NULL);

    system_power_ctx.config = data;
    system_power_ctx.mod_pd_system_id = FWK_ID_NONE;

    if (system_power_ctx.config->ext_ppus_count > 0) {
        system_power_ctx.ext_ppu_apis = fwk_mm_calloc(
            system_power_ctx.config->ext_ppus_count,
            sizeof(system_power_ctx.ext_ppu_apis[0]));
        if (system_power_ctx.ext_ppu_apis == NULL)
            return FWK_E_NOMEM;
    }

    if (system_power_ctx.config->soc_wakeup_irq != FWK_INTERRUPT_NONE) {
        return fwk_interrupt_set_isr(system_power_ctx.config->soc_wakeup_irq,
                                     soc_wakeup_handler);
    } else
        return FWK_SUCCESS;
}

static int system_power_bind(fwk_id_t id, unsigned int round)
{
    int status;
    unsigned int i;

    if (round == 1) {
        /*
         * During the first round of binding, the power domain module should
         * have bound to the power domain driver API provided by the present
         * module. Bind back to the power domain driver input API provided by
         * the system_power_ctx.mod_pd_system_id power domain module element to
         * report power state transitions of the system power domains.
         */
        return fwk_module_bind(system_power_ctx.mod_pd_system_id,
                               mod_pd_api_id_driver_input,
                               &system_power_ctx.mod_pd_driver_input_api);
    }

    status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG),
        FWK_ID_API(FWK_MODULE_IDX_LOG, 0), &system_power_ctx.log_api);
    if (status != FWK_SUCCESS)
        return status;

    status = fwk_module_bind(system_power_ctx.config->ppu_sys0_id,
                             system_power_ctx.config->ppu_sys_api_id,
                             &system_power_ctx.sys0_api);
    if (status != FWK_SUCCESS)
        return status;

    status = fwk_module_bind(system_power_ctx.config->ppu_sys1_id,
                             system_power_ctx.config->ppu_sys_api_id,
                             &system_power_ctx.sys1_api);
    if (status != FWK_SUCCESS)
        return status;

    for (i = 0; i < system_power_ctx.config->ext_ppus_count; i++) {
        status = fwk_module_bind(
            system_power_ctx.config->ext_ppus[i].ppu_id,
            system_power_ctx.config->ext_ppus[i].api_id,
            &system_power_ctx.ext_ppu_apis[i]);
        if (status != FWK_SUCCESS)
            return status;
    }

    status = fwk_module_bind(system_power_ctx.config->driver_id,
        system_power_ctx.config->driver_api_id, &system_power_ctx.driver_api);
    if (status != FWK_SUCCESS)
        return status;

    status = fwk_module_bind(fwk_module_id_power_domain,
        mod_pd_api_id_restricted,
        &system_power_ctx.mod_pd_restricted_api);
    if (status != FWK_SUCCESS)
        return status;

    return FWK_SUCCESS;
}

static int system_power_process_bind_request(fwk_id_t requester_id,
                                            fwk_id_t pd_id, fwk_id_t api_id,
                                            const void **api)
{
    if (fwk_id_is_equal(api_id, mod_system_power_api_id_pd_driver)) {

        if (!fwk_id_is_equal(fwk_id_build_module_id(requester_id),
                             fwk_module_id_power_domain))
            return FWK_E_ACCESS;

        *api = &system_power_power_domain_driver_api;
         system_power_ctx.mod_pd_system_id = requester_id;
    } else {
         if (!fwk_id_is_equal(requester_id,
                              system_power_ctx.config->ppu_sys0_id) &&
             !fwk_id_is_equal(requester_id,
                              system_power_ctx.config->ppu_sys1_id))
             return FWK_E_ACCESS;
        *api = &system_power_power_domain_driver_input_api;
    }

    return FWK_SUCCESS;
}

static int system_power_start(fwk_id_t id)
{
    int status;
    unsigned int state;

    status = system_power_ctx.sys1_api->get_state
        (system_power_ctx.config->ppu_sys1_id, &state);
    if (status != FWK_SUCCESS)
        return status;

    if (state == MOD_PD_STATE_OFF) {
        system_power_ctx.state = MOD_PD_STATE_OFF;
        return FWK_SUCCESS;
    }

    status = system_power_ctx.sys0_api->get_state
        (system_power_ctx.config->ppu_sys0_id, &state);
    if (status != FWK_SUCCESS)
        return status;

    system_power_ctx.state = (state == MOD_PD_STATE_ON) ?
                            MOD_PD_STATE_ON :
                            MOD_SYSTEM_POWER_POWER_STATE_SLEEP0;

    return FWK_SUCCESS;
}

const struct fwk_module module_system_power = {
    .name = "SYSTEM_POWER",
    .type = FWK_MODULE_TYPE_DRIVER,
    .api_count = MOD_SYSTEM_POWER_API_COUNT,
    .init = system_power_mod_init,
    .bind = system_power_bind,
    .start = system_power_start,
    .process_bind_request = system_power_process_bind_request,
};