summaryrefslogtreecommitdiff
path: root/common/throttle_ap.c
blob: 39223f9829529805b23ca679fd8d91992310c83b (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
/* Copyright 2013 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Common chipset throttling code for Chrome EC */

#include "builtin/assert.h"
#include "chipset.h"
#include "common.h"
#include "console.h"
#include "dptf.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "task.h"
#include "throttle_ap.h"
#include "timer.h"
#include "util.h"

/* Console output macros */
#define CPUTS(outstr) cputs(CC_THERMAL, outstr)
#define CPRINTS(format, args...) cprints(CC_THERMAL, format, ##args)

/*
 * When C10 deasserts, PROCHOT may also change state when the corresponding
 * power rail is turned back on. Recheck PROCHOT directly from the C10 exit
 * using a shorter debounce than the PROCHOT interrupt.
 */
#define PROCHOT_IN_DEBOUNCE_US (100 * MSEC)
#define C10_IN_DEBOUNCE_US (10 * MSEC)

/*****************************************************************************/
/* This enforces the virtual OR of all throttling sources. */
K_MUTEX_DEFINE(throttle_mutex);
static uint32_t throttle_request[NUM_THROTTLE_TYPES];
static int debounced_prochot_in;
static const struct prochot_cfg *prochot_cfg;

void throttle_ap(enum throttle_level level, enum throttle_type type,
		 enum throttle_sources source)
{
	uint32_t tmpval, bitmask;

	mutex_lock(&throttle_mutex);

	bitmask = BIT(source);

	switch (level) {
	case THROTTLE_ON:
		throttle_request[type] |= bitmask;
		break;
	case THROTTLE_OFF:
		throttle_request[type] &= ~bitmask;
		break;
	}

	tmpval = throttle_request[type]; /* save for printing */

	switch (type) {
	case THROTTLE_SOFT:
#ifdef HAS_TASK_HOSTCMD
		host_throttle_cpu(tmpval);
#endif
		break;
	case THROTTLE_HARD:
#ifdef CONFIG_CHIPSET_CAN_THROTTLE
		chipset_throttle_cpu(tmpval);
#endif
		break;

	case NUM_THROTTLE_TYPES:
		/* Make the compiler shut up. Don't use 'default', because
		 * we still want to catch any new types.
		 */
		break;
	}

	mutex_unlock(&throttle_mutex);

	/* print outside the mutex */
	CPRINTS("set AP throttling type %d to %s (0x%08x)", type,
		tmpval ? "on" : "off", tmpval);
}

void throttle_ap_config_prochot(const struct prochot_cfg *cfg)
{
	prochot_cfg = cfg;
}

__maybe_unused static bool prochot_is_gated_by_c10(int prochot_in)
{
#ifdef CONFIG_CPU_PROCHOT_GATE_ON_C10
	int c10_in = gpio_get_level(prochot_cfg->gpio_c10_in);

	if (!prochot_cfg->c10_active_high)
		c10_in = !c10_in;

	if (c10_in && prochot_in) {
		return true;
	}
#endif
	return false;
}

static void prochot_input_deferred(void)
{
	int prochot_in;

	/*
	 * Validate board called throttle_ap_config_prochot().
	 */
	ASSERT(prochot_cfg);

	prochot_in = gpio_get_level(prochot_cfg->gpio_prochot_in);

	if (IS_ENABLED(CONFIG_CPU_PROCHOT_ACTIVE_LOW))
		prochot_in = !prochot_in;

	if (prochot_in == debounced_prochot_in)
		return;

	/*
	 * b/173180788 Confirmed from Intel internal that SLP_S3# asserts low
	 * about 10us before PROCHOT# asserts low, which means that
	 * the CPU is already in reset and therefore the PROCHOT#
	 * asserting low is normal behavior and not a concern
	 * for PROCHOT# event.  Ignore all PROCHOT changes while the AP is off
	 */
	if (chipset_in_state(CHIPSET_STATE_ANY_OFF | CHIPSET_STATE_ANY_SUSPEND))
		return;

	/*
	 * b/185810479 When the AP enters C10, the PROCHOT signal may not be
	 * valid. Refer to the CONFIG_CPU_PROCHOT_GATE_ON_C10 documentation
	 * for details.
	 */
	if (prochot_is_gated_by_c10(prochot_in))
		return;

	debounced_prochot_in = prochot_in;

	if (debounced_prochot_in) {
		CPRINTS("External PROCHOT assertion detected");
#ifdef CONFIG_FANS
		dptf_set_fan_duty_target(100);
#endif
	} else {
		CPRINTS("External PROCHOT condition cleared");
#ifdef CONFIG_FANS
		/* Revert to automatic control of the fan */
		dptf_set_fan_duty_target(-1);
#endif
	}
}
DECLARE_DEFERRED(prochot_input_deferred);

void throttle_ap_prochot_input_interrupt(enum gpio_signal signal)
{
	/*
	 * Trigger deferred notification of PROCHOT change so we can ignore
	 * any pulses that are too short.
	 */
	hook_call_deferred(&prochot_input_deferred_data,
			   PROCHOT_IN_DEBOUNCE_US);
}

#ifdef CONFIG_CPU_PROCHOT_GATE_ON_C10
void throttle_ap_c10_input_interrupt(enum gpio_signal signal)
{
	/*
	 * This interrupt is configured to fire only when the AP exits C10
	 * and de-asserts the C10 signal. Recheck the PROCHOT signal in case
	 * another PROCHOT source is active when the AP exits C10.
	 */
	hook_call_deferred(&prochot_input_deferred_data, C10_IN_DEBOUNCE_US);
}
#endif

/*****************************************************************************/
/* Console commands */
#ifdef CONFIG_CMD_APTHROTTLE
static int command_apthrottle(int argc, const char **argv)
{
	int i;
	uint32_t tmpval;

	for (i = 0; i < NUM_THROTTLE_TYPES; i++) {
		mutex_lock(&throttle_mutex);
		tmpval = throttle_request[i];
		mutex_unlock(&throttle_mutex);

		ccprintf("AP throttling type %d is %s (0x%08x)\n", i,
			 tmpval ? "on" : "off", tmpval);
	}

	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(apthrottle, command_apthrottle, NULL,
			"Display the AP throttling state");
#endif