summaryrefslogtreecommitdiff
path: root/board/cr50/ec_state.c
blob: 5958ea3b79443484c2f1a524e7160af559c11553 (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
/* Copyright 2017 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.
 *
 * EC detect state machine.
 */
#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;

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

/**
 * 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 (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");
		if (!uart_bitbang_is_enabled(UART_EC))
			uartn_enable(UART_EC);
		state = DEVICE_STATE_INIT_RX_ONLY;
		return;
	}

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

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

	/* We were previously off */
	CPRINTS("EC on");
	state = DEVICE_STATE_ON;

	/* Enable UART RX if we're not bit-banging */
	if (!uart_bitbang_is_enabled(UART_EC))
		enable_ccd_uart(UART_EC);
}
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);
	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);

	/* If we detect the EC, make sure it's on */
	if (gpio_get_level(GPIO_DETECT_EC)) {
		set_ec_on();
		return;
	}

	/* 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");
		state = DEVICE_STATE_OFF;
		disable_ccd_uart(UART_EC);
		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)
		state = DEVICE_STATE_INIT_DEBOUNCING;
	else
		state = DEVICE_STATE_DEBOUNCING;
	gpio_enable_interrupt(GPIO_DETECT_EC);
}
DECLARE_HOOK(HOOK_SECOND, ec_detect, HOOK_PRIO_DEFAULT);