summaryrefslogtreecommitdiff
path: root/common/gaia_power.c
blob: 1683ecb006ad37e966a8eb9bc12fedb849afc468 (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
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* GAIA SoC power sequencing module for Chrome EC */

#include "board.h"
#include "chipset.h"  /* This module implements chipset functions too */
#include "console.h"
#include "gpio.h"
#include "task.h"
#include "timer.h"
#include "util.h"

/* Console output macros */
#define CPUTS(outstr) cputs(CC_CHIPSET, outstr)
#define CPRINTF(format, args...) cprintf(CC_CHIPSET, format, ## args)

/* Time necessary for the 5v regulator output to stabilize */
#define DELAY_5V_SETUP        1000  /* 1ms */

/* Delay between 1.35v and 3.3v rails startup */
#define DELAY_RAIL_STAGGERING 100  /* 100us */

/* Long power key press to force shutdown */
#define DELAY_FORCE_SHUTDOWN  8000000 /* 8s */

/* debounce time to prevent accidental power-on after keyboard power off */
#define KB_PWR_ON_DEBOUNCE    250    /* 250us */

/* PMIC fails to set the LDO2 output */
#define PMIC_TIMEOUT          100000  /* 100ms */

/* Default timeout for input transition */
#define FAIL_TIMEOUT          500000 /* 500ms */


/* Application processor power state */
static int ap_on;

/* simulated event state */
static int force_signal = -1;
static int force_value;

/* Wait for GPIO "signal" to reach level "value".
 * Returns EC_ERROR_TIMEOUT if timeout before reaching the desired state.
 */
static int wait_in_signal(enum gpio_signal signal, int value, int timeout)
{
	timestamp_t deadline;
	timestamp_t now = get_time();

	if (timeout < 0)
		deadline.le.hi = 0xffffffff;
	else
		deadline.val = now.val + timeout;

	while (((force_signal != signal) || (force_value != value)) &&
		gpio_get_level(signal) != value) {
		now = get_time();
		if ((now.val >= deadline.val) ||
			(task_wait_event(deadline.val - now.val) ==
			 TASK_EVENT_TIMER)) {
			CPRINTF("Timeout waiting for GPIO %d\n", signal);
			return EC_ERROR_TIMEOUT;
		}
	}

	return EC_SUCCESS;
}

/* Wait for some event triggering the shutdown.
 *
 * It can be either a long power button press or a shutdown triggered from the
 * AP and detected by reading XPSHOLD.
 */
static void wait_for_power_off(void)
{
	timestamp_t deadline, now;

	while (1) {
		/* wait for power button press or XPSHOLD falling edge */
		while ((gpio_get_level(GPIO_KB_PWR_ON_L) == 1) &&
			(gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 1)) {
				task_wait_event(-1);
		}

		/* XPSHOLD released by AP : shutdown immediatly */
		if (gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0)
			return;

		/* relay to PMIC */
		gpio_set_level(GPIO_PMIC_PWRON_L, 0);

		/* check if power button is pressed for 8s */
		deadline.val = get_time().val + DELAY_FORCE_SHUTDOWN;
		while ((gpio_get_level(GPIO_KB_PWR_ON_L) == 0) &&
			(gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 1)) {
			now = get_time();
			if ((now.val >= deadline.val) ||
				(task_wait_event(deadline.val - now.val) ==
				 TASK_EVENT_TIMER)) {
					gpio_set_level(GPIO_PMIC_PWRON_L, 1);
					return;
			}
		}

		gpio_set_level(GPIO_PMIC_PWRON_L, 1);

		/*
		 * Holding down the power button causes this loop to spin
		 * endlessly, triggering the watchdog. So add a wait here.
		 */
		task_wait_event(-1);
	}
}

void gaia_power_event(enum gpio_signal signal)
{
	/* Wake up the task */
	task_wake(TASK_ID_GAIAPOWER);
}

int gaia_power_init(void)
{
	/* Enable interrupts for our GPIOs */
	gpio_enable_interrupt(GPIO_KB_PWR_ON_L);
	gpio_enable_interrupt(GPIO_PP1800_LDO2);
	gpio_enable_interrupt(GPIO_SOC1V8_XPSHOLD);

	return EC_SUCCESS;
}


/*****************************************************************************/
/* Chipset interface */

/* Returns non-zero if the chipset is in the specified state. */
int chipset_in_state(enum chipset_state in_state)
{
	switch (in_state) {
	case CHIPSET_STATE_SOFT_OFF:
		return ap_on == 0;
	case CHIPSET_STATE_SUSPEND:
		/* TODO: implement */
		return 0;
	case CHIPSET_STATE_ON:
		return ap_on;
	}

	/* Should never get here since we list all states above, but compiler
	 * doesn't seem to understand that. */
	return 0;
}

/*****************************************************************************/

void gaia_power_task(void)
{
	gaia_power_init();

	while (1) {
		/* Power OFF state */
		ap_on = 0;

		/* wait for Power button press */
		wait_in_signal(GPIO_KB_PWR_ON_L, 0, -1);

		usleep(KB_PWR_ON_DEBOUNCE);
		if (gpio_get_level(GPIO_KB_PWR_ON_L) == 1)
			continue;

		/* Enable 5v power rail */
		gpio_set_level(GPIO_EN_PP5000, 1);
		/* wait to have stable power */
		usleep(DELAY_5V_SETUP);

		/* Startup PMIC */
		gpio_set_level(GPIO_PMIC_PWRON_L, 0);
		/* wait for all PMIC regulators to be ready */
		wait_in_signal(GPIO_PP1800_LDO2, 1, PMIC_TIMEOUT);

		/* if PP1800_LDO2 did not come up (e.g. PMIC_TIMEOUT was
		 * reached), turn off 5v rail and start over */
		if (gpio_get_level(GPIO_PP1800_LDO2) == 0) {
			gpio_set_level(GPIO_EN_PP5000, 0);
			usleep(DELAY_5V_SETUP);
			continue;
		}

		/* Enable DDR 1.35v power rail */
		gpio_set_level(GPIO_EN_PP1350, 1);
		/* wait to avoid large inrush current */
		usleep(DELAY_RAIL_STAGGERING);
		/* Enable 3.3v power rail */
		gpio_set_level(GPIO_EN_PP3300, 1);

		/* wait for the Application Processor to take control of the
		 * PMIC.
		 */
		wait_in_signal(GPIO_SOC1V8_XPSHOLD, 1, FAIL_TIMEOUT);
		/* release PMIC startup signal */
		gpio_set_level(GPIO_PMIC_PWRON_L, 1);

		/* Power ON state */
		ap_on = 1;
		CPUTS("AP running ...\n");

		/* Wait for power off from AP or long power button press */
		wait_for_power_off();
		/* switch off all rails */
		gpio_set_level(GPIO_EN_PP3300, 0);
		gpio_set_level(GPIO_EN_PP1350, 0);
		gpio_set_level(GPIO_EN_PP5000, 0);
		CPUTS("Shutdown complete.\n");

		/* Ensure the power button is released */
		wait_in_signal(GPIO_KB_PWR_ON_L, 1, -1);
	}
}

/*****************************************************************************/
/* Console debug command */

static int command_force_power(int argc, char **argv)
{
	/* simulate power button pressed */
	force_signal = GPIO_KB_PWR_ON_L;
	force_value = 1;
	/* Wake up the task */
	task_wake(TASK_ID_GAIAPOWER);
	/* wait 100 ms */
	usleep(100000);
	/* release power button */
	force_signal = -1;
	force_value = 0;

	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(forcepower, command_force_power);