summaryrefslogtreecommitdiff
path: root/board/cr50/ap_uart_state.c
blob: 42e2feeab4cca290db4f2e49bb5c8cd579d47fb2 (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
/* Copyright 2018 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.
 *
 * AP UART state machine
 */
#include "ccd_config.h"
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "uart_bitbang.h"
#include "uartn.h"

#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)

static enum device_state state = DEVICE_STATE_INIT;

void print_ap_uart_state(void)
{
	ccprintf("AP UART: %s\n", device_state_name(state));
}

int ap_uart_is_on(void)
{
	/* Debouncing and on are both still on */
	return (state == DEVICE_STATE_DEBOUNCING || state == DEVICE_STATE_ON);
}

/**
 * Set the AP UART state.
 *
 * Done as a function to make it easier to debug state transitions.  Note that
 * this ONLY sets the state (and possibly prints debug info), and doesn't do
 * all the additional transition work that set_ap_uart_on(), etc. do.
 *
 * @param new_state	State to set.
 */
static void set_state(enum device_state new_state)
{
#ifdef CR50_DEBUG_AP_UART_STATE
	/* Print all state transitions.  May spam the console. */
	if (state != new_state)
		CPRINTS("AP UART %s -> %s",
			device_state_name(state), device_state_name(new_state));
#endif
	state = new_state;
}

/* Move the AP UART to the OFF state. */
static void set_ap_uart_off(void)
{
	CPRINTS("AP UART off");
	set_state(DEVICE_STATE_OFF);

	ccd_update_state();
}

/**
 * Move the AP UART to the ON state.
 *
 * This can be deferred from the interrupt handler, or called from the state
 * machine which also runs in HOOK task, so it needs to check the current state
 * to determine whether we're already on.
 */
static void set_ap_uart_on_deferred(void)
{
	/* If we were debouncing ON->OFF, cancel it because we're still on */
	if (state == DEVICE_STATE_DEBOUNCING)
		set_state(DEVICE_STATE_ON);

	/* If we're already on, done */
	if (state == DEVICE_STATE_ON)
		return;

	/* We were previously off */
	CPRINTS("AP UART on");
	set_state(DEVICE_STATE_ON);

	ccd_update_state();
}
DECLARE_DEFERRED(set_ap_uart_on_deferred);

/**
 * Interrupt handler for AP detect asserted
 */
void ap_detect_asserted(enum gpio_signal signal)
{
	gpio_disable_interrupt(GPIO_DETECT_AP);
	hook_call_deferred(&set_ap_uart_on_deferred_data, 0);
}

/**
 * Detect state machine
 */
static void ap_uart_detect(void)
{
	/* Disable interrupts if we had them on for debouncing */
	gpio_disable_interrupt(GPIO_DETECT_AP);

	/* If the AP UART signal is high, make sure it's on */
	if (gpio_get_level(GPIO_DETECT_AP)) {
		hook_call_deferred(&set_ap_uart_on_deferred_data, 0);
		return;
	}

	/*
	 * Make sure the interrupt is enabled. We will need to detect the on
	 * transition if we enter the off or debouncing state
	 */
	gpio_enable_interrupt(GPIO_DETECT_AP);

	/* AP UART wasn't detected.  If we're already off, done. */
	if (state == DEVICE_STATE_OFF)
		return;

	/* If we were debouncing, we're now sure we're off */
	if (state == DEVICE_STATE_DEBOUNCING ||
	    state == DEVICE_STATE_INIT_DEBOUNCING) {
		set_ap_uart_off();
		return;
	}

	/*
	 * Otherwise, we were on or initializing, and we're not sure if the AP
	 * UART is actually off or just sending a 0-bit.  So start debouncing.
	 */
	if (state == DEVICE_STATE_INIT)
		set_state(DEVICE_STATE_INIT_DEBOUNCING);
	else
		set_state(DEVICE_STATE_DEBOUNCING);
}
DECLARE_HOOK(HOOK_SECOND, ap_uart_detect, HOOK_PRIO_DEFAULT);