summaryrefslogtreecommitdiff
path: root/chip/npcx/gpio-npcx9.c
blob: 5de8ea3b0afc99d240ca92b0c0fbad520fde10ba (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
/* Copyright 2020 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* GPIO module for Chrome EC */

#include "clock.h"
#include "common.h"
#include "ec_commands.h"
#include "gpio_chip.h"
#include "hooks.h"
#include "host_command.h"
#include "lct_chip.h"
#include "lpc_chip.h"
#include "registers.h"
#include "task.h"

/*
 * List of GPIO IRQs to enable. Don't automatically enable interrupts for
 * the keyboard input GPIO bank - that's handled separately. Of course the
 * bank is different for different systems.
 */
static void gpio_init(void)
{
	/* Enable IRQs now that pins are set up */
	task_enable_irq(NPCX_IRQ_CR_SIN2_WKINTA_0);
	task_enable_irq(NPCX_IRQ_TWD_WKINTB_0);
	task_enable_irq(NPCX_IRQ_WKINTC_0);
	task_enable_irq(NPCX_IRQ_MTC_WKINTD_0);
	task_enable_irq(NPCX_IRQ_WKINTE_0);
	task_enable_irq(NPCX_IRQ_WKINTF_0);
	task_enable_irq(NPCX_IRQ_WKINTG_0);
	task_enable_irq(NPCX_IRQ_WKINTH_0);
	task_enable_irq(NPCX_IRQ_WKINTA_1);
	task_enable_irq(NPCX_IRQ_WKINTB_1);
#ifdef NPCX_SELECT_KSI_TO_GPIO
	task_enable_irq(NPCX_IRQ_KSI_WKINTC_1);
#endif
	task_enable_irq(NPCX_IRQ_WKINTD_1);
	task_enable_irq(NPCX_IRQ_WKINTE_1);
	task_enable_irq(NPCX_IRQ_WKINTF_1);
	task_enable_irq(NPCX_IRQ_WKINTG_1);
	task_enable_irq(NPCX_IRQ_WKINTH_1);
	task_enable_irq(NPCX_IRQ_LCT_WKINTF_2);
}
DECLARE_HOOK(HOOK_INIT, gpio_init, HOOK_PRIO_DEFAULT);

/**
 * Handlers for each GPIO port.  These read and clear the interrupt bits for
 * the port, then call the master handler above.
 */

#define GPIO_IRQ_FUNC(_irq_func, wui_int) \
	static void _irq_func(void)       \
	{                                 \
		gpio_interrupt(wui_int);  \
	}

/* If we need to handle the other type interrupts except GPIO, add code here */
static void __gpio_host_interrupt(void)
{
	if (IS_ENABLED(CONFIG_HOSTCMD_X86)) {
		/* Pending bit 7 or 6 or 5? */
		if (IS_BIT_SET(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_5), 6) &&
		    IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_0, MIWU_GROUP_5), 6)) {
			/* Disable host wake-up */
			CLEAR_BIT(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_5), 6);
			/* Clear pending bit of WUI */
			SET_BIT(NPCX_WKPCL(MIWU_TABLE_0, MIWU_GROUP_5), 6);
			return;
		}
		if (IS_ENABLED(CONFIG_HOST_INTERFACE_ESPI)) {
			if (IS_BIT_SET(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_5),
				       5) &&
			    IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_0, MIWU_GROUP_5),
				       5)) {
				espi_espirst_handler();
				return;
			}
		} else {
			if (IS_BIT_SET(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_5),
				       7) &&
			    IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_0, MIWU_GROUP_5),
				       7)) {
				lpc_lreset_pltrst_handler();
				return;
			}
		}
	}
	gpio_interrupt(WUI_INT(MIWU_TABLE_0, MIWU_GROUP_5));
}

#ifdef CONFIG_HOSTCMD_RTC
static void set_rtc_host_event(void)
{
	host_set_single_event(EC_HOST_EVENT_RTC);
}
DECLARE_DEFERRED(set_rtc_host_event);
#endif

static void __gpio_rtc_interrupt(void)
{
	/* Check pending bit 7 */
#ifdef CONFIG_HOSTCMD_RTC
	if (NPCX_WKPND(MIWU_TABLE_0, MIWU_GROUP_4) & 0x80) {
		/* Clear pending bit for WUI */
		SET_BIT(NPCX_WKPCL(MIWU_TABLE_0, MIWU_GROUP_4), 7);
		hook_call_deferred(&set_rtc_host_event_data, 0);
		return;
	}
#endif
	gpio_interrupt(WUI_INT(MIWU_TABLE_0, MIWU_GROUP_4));
}
static void __gpio_cr_sin2_interrupt(void)
{
#if defined(CONFIG_LOW_POWER_IDLE) && (CONFIG_CONSOLE_UART == 1)
	/* Handle the interrupt from UART wakeup event */
	if (IS_BIT_SET(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_1), 6) &&
	    IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_0, MIWU_GROUP_1), 6)) {
		/*
		 * Disable WKEN bit to avoid the other unnecessary interrupts
		 * from the coming data bits after the start bit. (Pending bit
		 * of CR_SIN is set when a high-to-low transaction occurs.)
		 */
		CLEAR_BIT(NPCX_WKEN(MIWU_TABLE_0, MIWU_GROUP_1), 6);
		/* Clear pending bit for WUI */
		SET_BIT(NPCX_WKPCL(MIWU_TABLE_0, MIWU_GROUP_1), 6);
		/* Notify the clock module that the console is in use. */
		clock_refresh_console_in_use();
		return;
	}
#endif
	gpio_interrupt(WUI_INT(MIWU_TABLE_0, MIWU_GROUP_1));
}

static void __gpio_wk1h_interrupt(void)
{
#if defined(CONFIG_LOW_POWER_IDLE) && (CONFIG_CONSOLE_UART == 0)
	/* Handle the interrupt from UART wakeup event */
	if (IS_BIT_SET(NPCX_WKEN(MIWU_TABLE_1, MIWU_GROUP_8), 7) &&
	    IS_BIT_SET(NPCX_WKPND(MIWU_TABLE_1, MIWU_GROUP_8), 7)) {
		/*
		 * Disable WKEN bit to avoid the other unnecessary interrupts
		 * from the coming data bits after the start bit. (Pending bit
		 * of CR_SIN is set when a high-to-low transaction occurs.)
		 */
		CLEAR_BIT(NPCX_WKEN(MIWU_TABLE_1, MIWU_GROUP_8), 7);
		/* Clear pending bit for WUI */
		SET_BIT(NPCX_WKPCL(MIWU_TABLE_1, MIWU_GROUP_8), 7);
		/* Notify the clock module that the console is in use. */
		clock_refresh_console_in_use();
	} else
#endif
		gpio_interrupt(WUI_INT(MIWU_TABLE_1, MIWU_GROUP_8));
}

static void __gpio_lct_interrupt(void)
{
	if (NPCX_WKPND(MIWU_TABLE_2, MIWU_GROUP_6) & LCT_WUI_MASK) {
		NPCX_WKPCL(MIWU_TABLE_2, MIWU_GROUP_6) |= LCT_WUI_MASK;
		npcx_lct_clear_event();
		return;
	}
	gpio_interrupt(WUI_INT(MIWU_TABLE_2, MIWU_GROUP_6));
}

GPIO_IRQ_FUNC(__gpio_wk0b_interrupt, WUI_INT(MIWU_TABLE_0, MIWU_GROUP_2));
GPIO_IRQ_FUNC(__gpio_wk0c_interrupt, WUI_INT(MIWU_TABLE_0, MIWU_GROUP_3));
GPIO_IRQ_FUNC(__gpio_wk0f_interrupt, WUI_INT(MIWU_TABLE_0, MIWU_GROUP_6));
GPIO_IRQ_FUNC(__gpio_wk0g_interrupt, WUI_INT(MIWU_TABLE_0, MIWU_GROUP_7));
GPIO_IRQ_FUNC(__gpio_wk0h_interrupt, WUI_INT(MIWU_TABLE_0, MIWU_GROUP_8));
GPIO_IRQ_FUNC(__gpio_wk1a_interrupt, WUI_INT(MIWU_TABLE_1, MIWU_GROUP_1));
GPIO_IRQ_FUNC(__gpio_wk1b_interrupt, WUI_INT(MIWU_TABLE_1, MIWU_GROUP_2));
#ifdef NPCX_SELECT_KSI_TO_GPIO
/* Declare GPIO irq functions for KSI pins if there's no keyboard scan task, */
GPIO_IRQ_FUNC(__gpio_wk1c_interrupt, WUI_INT(MIWU_TABLE_1, MIWU_GROUP_3));
#endif
GPIO_IRQ_FUNC(__gpio_wk1d_interrupt, WUI_INT(MIWU_TABLE_1, MIWU_GROUP_4));
GPIO_IRQ_FUNC(__gpio_wk1e_interrupt, WUI_INT(MIWU_TABLE_1, MIWU_GROUP_5));
GPIO_IRQ_FUNC(__gpio_wk1f_interrupt, WUI_INT(MIWU_TABLE_1, MIWU_GROUP_6));
GPIO_IRQ_FUNC(__gpio_wk1g_interrupt, WUI_INT(MIWU_TABLE_1, MIWU_GROUP_7));

DECLARE_IRQ(NPCX_IRQ_CR_SIN2_WKINTA_0, __gpio_cr_sin2_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_TWD_WKINTB_0, __gpio_wk0b_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTC_0, __gpio_wk0c_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_MTC_WKINTD_0, __gpio_rtc_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTE_0, __gpio_host_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTF_0, __gpio_wk0f_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTG_0, __gpio_wk0g_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTH_0, __gpio_wk0h_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTA_1, __gpio_wk1a_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTB_1, __gpio_wk1b_interrupt, 3);
#ifdef NPCX_SELECT_KSI_TO_GPIO
DECLARE_IRQ(NPCX_IRQ_KSI_WKINTC_1, __gpio_wk1c_interrupt, 3);
#endif
DECLARE_IRQ(NPCX_IRQ_WKINTD_1, __gpio_wk1d_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTE_1, __gpio_wk1e_interrupt, 3);
#ifdef CONFIG_HOST_INTERFACE_SHI
/*
 * HACK: Make CS GPIO P2 to improve SHI reliability.
 * TODO: Increase CS-assertion-to-transaction-start delay on host to
 * accommodate P3 CS interrupt.
 */
DECLARE_IRQ(NPCX_IRQ_WKINTF_1, __gpio_wk1f_interrupt, 2);
#else
DECLARE_IRQ(NPCX_IRQ_WKINTF_1, __gpio_wk1f_interrupt, 3);
#endif
DECLARE_IRQ(NPCX_IRQ_WKINTG_1, __gpio_wk1g_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_WKINTH_1, __gpio_wk1h_interrupt, 3);
DECLARE_IRQ(NPCX_IRQ_LCT_WKINTF_2, __gpio_lct_interrupt, 3);

#undef GPIO_IRQ_FUNC