summaryrefslogtreecommitdiff
path: root/board/cr50/ec_state.c
blob: 978ffa029a4b36e53f034ebacfd98831c886e914 (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
/* Copyright 2017 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * EC 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_ec_state(void)
{
	ccprintf("EC:      %s\n", device_state_name(state));
}

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

int ec_is_rx_allowed(void)
{
	return ec_is_on() || state == DEVICE_STATE_INIT_RX_ONLY;
}

/**
 * Set the EC 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_ec_on(), etc. do.
 *
 * @param new_state	State to set.
 */
static void set_state(enum device_state new_state)
{
#ifdef CR50_DEBUG_EC_STATE
	/* Print all state transitions.  May spam the console. */
	if (state != new_state)
		CPRINTS("EC %s -> %s",
			device_state_name(state), device_state_name(new_state));
#endif
	state = new_state;
}

/**
 * Move the EC 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_ec_on(void)
{
	/* If we're already on, done */
	if (state == DEVICE_STATE_ON)
		return;

	/* If we were debouncing ON->OFF, cancel it because we're still on */
	if (state == DEVICE_STATE_DEBOUNCING) {
		set_state(DEVICE_STATE_ON);
		return;
	}

	if (state == DEVICE_STATE_INIT ||
	    state == DEVICE_STATE_INIT_DEBOUNCING) {
		/*
		 * Enable the UART peripheral so we start receiving on EC RX,
		 * but do not call uartn_tx_connect() to connect EC TX yet.  We
		 * need to be able to use EC TX to detect servo, so if we drive
		 * it right away that blocks us from detecting servo.
		 */
		CPRINTS("EC RX only");
		set_state(DEVICE_STATE_INIT_RX_ONLY);
		ccd_update_state();
		return;
	}

	/* We were previously off */
	CPRINTS("EC on");
	set_state(DEVICE_STATE_ON);
	ccd_update_state();
}
DECLARE_DEFERRED(set_ec_on);

/**
 * Interrupt handler for EC detect asserted.
 */
void ec_detect_asserted(enum gpio_signal signal)
{
	gpio_disable_interrupt(GPIO_DETECT_EC_UART);
	hook_call_deferred(&set_ec_on_data, 0);
}

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

	if (uart_bitbang_is_enabled())
		return;

	/* If we detect the EC, make sure it's on */
	if (gpio_get_level(GPIO_DETECT_EC_UART)) {
		set_ec_on();
		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_EC_UART);

	/* EC 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) {
		CPRINTS("EC off");
		set_state(DEVICE_STATE_OFF);
		ccd_update_state();
		return;
	}

	/*
	 * Otherwise, we were on or initializing, and we're not sure if the EC
	 * 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, ec_detect, HOOK_PRIO_DEFAULT);