summaryrefslogtreecommitdiff
path: root/common/gaia_power.c
blob: bd532acc604be9fb23c2dfb391d35c04893984ef (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
/* 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 "uart.h"
#include "util.h"

/* 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 */

/* PMIC fails to set the LDO2 output */
#define PMIC_TIMEOUT          15000  /* 15ms */

/* 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_msg(deadline.val - now.val) ==
			 (1 << TASK_ID_TIMER))) {
			uart_printf("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_EC_PWRON) == 0) &&
			(gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 1)) {
				task_wait_msg(-1);
		}
		/* XPSHOLD released by AP : shutdown immediatly */
		if (gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 0)
			return;

		/* check if power button is pressed for 8s */
		deadline.val = get_time().val + DELAY_FORCE_SHUTDOWN;
		while ((gpio_get_level(GPIO_EC_PWRON) == 1) &&
			(gpio_get_level(GPIO_SOC1V8_XPSHOLD) == 1)) {
			now = get_time();
			if ((now.val >= deadline.val) ||
				(task_wait_msg(deadline.val - now.val) ==
				 (1 << TASK_ID_TIMER)))
					return;
		}
	}
}

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

int gaia_power_init(void)
{
	/* Enable interrupts for our GPIOs */
	gpio_enable_interrupt(GPIO_EC_PWRON);
	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_EC_PWRON, 1, -1);

		/* 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_ACOK, 0);
		/* wait for all PMIC regulators to be ready */
		wait_in_signal(GPIO_PP1800_LDO2, 1, PMIC_TIMEOUT);

		/* 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_ACOK, 1);

		/* Power ON state */
		ap_on = 1;
		uart_printf("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);
		uart_printf("Shutdown complete.\n");

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

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

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

	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(forcepower, command_force_power);