File: css.c

package info (click to toggle)
crust-firmware 0.6-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,900 kB
  • sloc: ansic: 19,341; yacc: 596; lex: 479; makefile: 334; asm: 215; sh: 136; python: 42
file content (145 lines) | stat: -rw-r--r-- 3,875 bytes parent folder | download | duplicates (2)
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
/*
 * Copyright © 2017-2022 The Crust Firmware Authors.
 * SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only
 */

#include <css.h>
#include <debug.h>
#include <scpi_protocol.h>
#include <stdint.h>
#include <steps.h>
#include <system.h>
#include <util.h>

#include "css.h"

/* The register layout assumes a maximum of two clusters. */
#define FIQ_BIT BIT(2 * MAX_CORES_PER_CLUSTER)
#define IRQ_BIT BIT(0)

static uint8_t lead_cluster, lead_core;

int
css_get_power_state(uint32_t cluster, uint32_t *cluster_state,
                    uint32_t *online_cores)
{
	uint32_t mask = 0;

	if (cluster >= css_get_cluster_count())
		return SCPI_E_PARAM;

	*cluster_state = power_state.cluster[cluster];

	for (uint32_t core = 0; core < MAX_CORES_PER_CLUSTER; ++core) {
		if (power_state.core[cluster][core] != SCPI_CSS_OFF)
			mask |= BIT(core);
	}
	*online_cores = mask;

	return SCPI_OK;
}

int
css_set_power_state(uint32_t cluster, uint32_t core, uint32_t core_state,
                    uint32_t cluster_state, uint32_t css_state)
{
	uint8_t *core_ps    = &power_state.core[cluster][core];
	uint8_t *cluster_ps = &power_state.cluster[cluster];
	uint8_t *css_ps     = &power_state.css;

	if (cluster >= css_get_cluster_count())
		return SCPI_E_PARAM;
	if (core >= css_get_core_count(cluster))
		return SCPI_E_PARAM;

	/*
	 * This implementation takes advantage of two restrictions on power
	 * state requests:
	 *
	 *   1. A request to suspend a core may only be sent from that core.
	 *      Therefore, at the time any such request is received, it is safe
	 *      to assume that the core and all of its ancestor power domains
	 *      are in the "on" state.
	 *
	 *   2. No power domain may be in a deeper power state than any of its
	 *      children. Therefore, any request to turn on a core must also
	 *      turn on all of its ancestor power domains, regardless of their
	 *      previous or requested states.
	 */
	if (core_state != SCPI_CSS_ON) {
		uint8_t *cluster_cores = power_state.core[cluster];
		uint8_t *css_clusters  = power_state.cluster;

		record_step(STEP_SUSPEND_CORE);
		css_suspend_core(cluster, core, core_state);
		*core_ps = core_state;

		/* A cluster must be on if any of its cores is on. */
		for (uint32_t i = 0; i < css_get_core_count(cluster); ++i) {
			if (cluster_cores[i] < cluster_state)
				cluster_state = cluster_cores[i];
		}
		record_step(STEP_SUSPEND_CLUSTER);
		css_suspend_cluster(cluster, cluster_state);
		*cluster_ps = cluster_state;

		/* The CSS must be on if any of its clusters is on. */
		for (uint32_t i = 0; i < css_get_cluster_count(); ++i) {
			if (css_clusters[i] < css_state)
				css_state = css_clusters[i];
		}
		record_step(STEP_SUSPEND_CSS);
		css_suspend_css(css_state);
		*css_ps = css_state;

		/* Suspend the system when powering off the CSS. */
		if (css_state == SCPI_CSS_OFF) {
			system_suspend();

			/* Remember the last active core. */
			lead_cluster = cluster;
			lead_core    = core;
		}
	} else {
		css_resume_css(*css_ps);
		*css_ps = SCPI_CSS_ON;

		css_resume_cluster(cluster, *cluster_ps);
		*cluster_ps = SCPI_CSS_ON;

		css_resume_core(cluster, core, *core_ps);
		*core_ps = SCPI_CSS_ON;
	}

	return SCPI_OK;
}

static void
css_wake_one_cpu(uint32_t cluster, uint32_t core)
{
	css_set_power_state(cluster, core,
	                    SCPI_CSS_ON, SCPI_CSS_ON, SCPI_CSS_ON);
}

void
css_resume(void)
{
	css_wake_one_cpu(lead_cluster, lead_core);
}

void
css_poll(void)
{
	uint32_t status = css_get_irq_status();

	for (uint32_t i = 0; i < css_get_cluster_count(); ++i) {
		/* Assume each cluster is allocated the same number of bits. */
		for (uint32_t j = 0; j < MAX_CORES_PER_CLUSTER; ++j) {
			if ((status & (FIQ_BIT | IRQ_BIT)) &&
			    (power_state.core[i][j] == SCPI_CSS_OFF))
				css_wake_one_cpu(i, j);
			/* Shift the next core status on top of the mask. */
			status >>= 1;
		}
	}
}