summaryrefslogtreecommitdiff
path: root/chip/it83xx/espi.c
blob: bef877449e87373e3367a71894ba83abf2c844fd (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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
/* 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.
 */

/* ESPI module for Chrome EC */

#include "console.h"
#include "espi.h"
#include "gpio.h"
#include "hooks.h"
#include "port80.h"
#include "power.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "uart.h"
#include "util.h"

/* Console output macros */
#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args)

struct vw_channel_t {
	uint8_t  index;         /* VW index of signal */
	uint8_t  level_mask;    /* level bit of signal */
	uint8_t  valid_mask;    /* valid bit of signal */
};

/* VW settings after the controller enables the VW channel. */
static const struct vw_channel_t en_vw_setting[] = {
	/* EC sends SUS_ACK# = 1 VW to PCH. That does not apply to GLK SoC. */
#ifndef CONFIG_CHIPSET_GEMINILAKE
	{ESPI_SYSTEM_EVENT_VW_IDX_40,
		VW_LEVEL_FIELD(0),
		VW_VALID_FIELD(VW_IDX_40_SUS_ACK)},
#endif
};

/* VW settings after the controller enables the OOB channel. */
static const struct vw_channel_t en_oob_setting[] = {
	{ESPI_SYSTEM_EVENT_VW_IDX_4,
		VW_LEVEL_FIELD(0),
		VW_VALID_FIELD(VW_IDX_4_OOB_RST_ACK)},
};

/* VW settings after the controller enables the flash channel. */
static const struct vw_channel_t en_flash_setting[] = {
	{ESPI_SYSTEM_EVENT_VW_IDX_5,
		VW_LEVEL_FIELD(VW_IDX_5_BTLD_STATUS_DONE),
		VW_VALID_FIELD(VW_IDX_5_BTLD_STATUS_DONE)},
};

/* VW settings at host startup */
static const struct vw_channel_t vw_host_startup_setting[] = {
	{ESPI_SYSTEM_EVENT_VW_IDX_6,
		VW_LEVEL_FIELD(VW_IDX_6_SCI | VW_IDX_6_SMI |
				VW_IDX_6_RCIN | VW_IDX_6_HOST_RST_ACK),
		VW_VALID_FIELD(VW_IDX_6_SCI | VW_IDX_6_SMI |
				VW_IDX_6_RCIN | VW_IDX_6_HOST_RST_ACK)},
};

#define VW_CHAN(name, idx, level, valid) \
	[(name - VW_SIGNAL_START)] = {idx, level, valid}

/* VW signals used in eSPI (NOTE: must match order of enum espi_vw_signal). */
static const struct vw_channel_t vw_channel_list[] = {
	/* index 02h: controller to peripheral. */
	VW_CHAN(VW_SLP_S3_L,
		ESPI_SYSTEM_EVENT_VW_IDX_2,
		VW_LEVEL_FIELD(VW_IDX_2_SLP_S3),
		VW_VALID_FIELD(VW_IDX_2_SLP_S3)),
	VW_CHAN(VW_SLP_S4_L,
		ESPI_SYSTEM_EVENT_VW_IDX_2,
		VW_LEVEL_FIELD(VW_IDX_2_SLP_S4),
		VW_VALID_FIELD(VW_IDX_2_SLP_S4)),
	VW_CHAN(VW_SLP_S5_L,
		ESPI_SYSTEM_EVENT_VW_IDX_2,
		VW_LEVEL_FIELD(VW_IDX_2_SLP_S5),
		VW_VALID_FIELD(VW_IDX_2_SLP_S5)),
	/* index 03h: controller to peripheral. */
	VW_CHAN(VW_SUS_STAT_L,
		ESPI_SYSTEM_EVENT_VW_IDX_3,
		VW_LEVEL_FIELD(VW_IDX_3_SUS_STAT),
		VW_VALID_FIELD(VW_IDX_3_SUS_STAT)),
	VW_CHAN(VW_PLTRST_L,
		ESPI_SYSTEM_EVENT_VW_IDX_3,
		VW_LEVEL_FIELD(VW_IDX_3_PLTRST),
		VW_VALID_FIELD(VW_IDX_3_PLTRST)),
	VW_CHAN(VW_OOB_RST_WARN,
		ESPI_SYSTEM_EVENT_VW_IDX_3,
		VW_LEVEL_FIELD(VW_IDX_3_OOB_RST_WARN),
		VW_VALID_FIELD(VW_IDX_3_OOB_RST_WARN)),
	/* index 04h: peripheral to controller. */
	VW_CHAN(VW_OOB_RST_ACK,
		ESPI_SYSTEM_EVENT_VW_IDX_4,
		VW_LEVEL_FIELD(VW_IDX_4_OOB_RST_ACK),
		VW_VALID_FIELD(VW_IDX_4_OOB_RST_ACK)),
	VW_CHAN(VW_WAKE_L,
		ESPI_SYSTEM_EVENT_VW_IDX_4,
		VW_LEVEL_FIELD(VW_IDX_4_WAKE),
		VW_VALID_FIELD(VW_IDX_4_WAKE)),
	VW_CHAN(VW_PME_L,
		ESPI_SYSTEM_EVENT_VW_IDX_4,
		VW_LEVEL_FIELD(VW_IDX_4_PME),
		VW_VALID_FIELD(VW_IDX_4_PME)),
	/* index 05h: peripheral to controller. */
	VW_CHAN(VW_ERROR_FATAL,
		ESPI_SYSTEM_EVENT_VW_IDX_5,
		VW_LEVEL_FIELD(VW_IDX_5_FATAL),
		VW_VALID_FIELD(VW_IDX_5_FATAL)),
	VW_CHAN(VW_ERROR_NON_FATAL,
		ESPI_SYSTEM_EVENT_VW_IDX_5,
		VW_LEVEL_FIELD(VW_IDX_5_NON_FATAL),
		VW_VALID_FIELD(VW_IDX_5_NON_FATAL)),
	VW_CHAN(VW_PERIPHERAL_BTLD_STATUS_DONE,
		ESPI_SYSTEM_EVENT_VW_IDX_5,
		VW_LEVEL_FIELD(VW_IDX_5_BTLD_STATUS_DONE),
		VW_VALID_FIELD(VW_IDX_5_BTLD_STATUS_DONE)),
	/* index 06h: peripheral to controller. */
	VW_CHAN(VW_SCI_L,
		ESPI_SYSTEM_EVENT_VW_IDX_6,
		VW_LEVEL_FIELD(VW_IDX_6_SCI),
		VW_VALID_FIELD(VW_IDX_6_SCI)),
	VW_CHAN(VW_SMI_L,
		ESPI_SYSTEM_EVENT_VW_IDX_6,
		VW_LEVEL_FIELD(VW_IDX_6_SMI),
		VW_VALID_FIELD(VW_IDX_6_SMI)),
	VW_CHAN(VW_RCIN_L,
		ESPI_SYSTEM_EVENT_VW_IDX_6,
		VW_LEVEL_FIELD(VW_IDX_6_RCIN),
		VW_VALID_FIELD(VW_IDX_6_RCIN)),
	VW_CHAN(VW_HOST_RST_ACK,
		ESPI_SYSTEM_EVENT_VW_IDX_6,
		VW_LEVEL_FIELD(VW_IDX_6_HOST_RST_ACK),
		VW_VALID_FIELD(VW_IDX_6_HOST_RST_ACK)),
	/* index 07h: controller to peripheral. */
	VW_CHAN(VW_HOST_RST_WARN,
		ESPI_SYSTEM_EVENT_VW_IDX_7,
		VW_LEVEL_FIELD(VW_IDX_7_HOST_RST_WARN),
		VW_VALID_FIELD(VW_IDX_7_HOST_RST_WARN)),
	/* index 40h: peripheral to controller. */
	VW_CHAN(VW_SUS_ACK,
		ESPI_SYSTEM_EVENT_VW_IDX_40,
		VW_LEVEL_FIELD(VW_IDX_40_SUS_ACK),
		VW_VALID_FIELD(VW_IDX_40_SUS_ACK)),
	/* index 41h: controller to peripheral. */
	VW_CHAN(VW_SUS_WARN_L,
		ESPI_SYSTEM_EVENT_VW_IDX_41,
		VW_LEVEL_FIELD(VW_IDX_41_SUS_WARN),
		VW_VALID_FIELD(VW_IDX_41_SUS_WARN)),
	VW_CHAN(VW_SUS_PWRDN_ACK_L,
		ESPI_SYSTEM_EVENT_VW_IDX_41,
		VW_LEVEL_FIELD(VW_IDX_41_SUS_PWRDN_ACK),
		VW_VALID_FIELD(VW_IDX_41_SUS_PWRDN_ACK)),
	VW_CHAN(VW_SLP_A_L,
		ESPI_SYSTEM_EVENT_VW_IDX_41,
		VW_LEVEL_FIELD(VW_IDX_41_SLP_A),
		VW_VALID_FIELD(VW_IDX_41_SLP_A)),
	/* index 42h: controller to peripheral. */
	VW_CHAN(VW_SLP_LAN,
		ESPI_SYSTEM_EVENT_VW_IDX_42,
		VW_LEVEL_FIELD(VW_IDX_42_SLP_LAN),
		VW_VALID_FIELD(VW_IDX_42_SLP_LAN)),
	VW_CHAN(VW_SLP_WLAN,
		ESPI_SYSTEM_EVENT_VW_IDX_42,
		VW_LEVEL_FIELD(VW_IDX_42_SLP_WLAN),
		VW_VALID_FIELD(VW_IDX_42_SLP_WLAN)),
};
BUILD_ASSERT(ARRAY_SIZE(vw_channel_list) == VW_SIGNAL_COUNT);

/* Get vw index & value information by signal */
static int espi_vw_get_signal_index(enum espi_vw_signal event)
{
	uint32_t i = event - VW_SIGNAL_START;

	return (i < ARRAY_SIZE(vw_channel_list)) ? i : -1;
}

/**
 * Set eSPI Virtual-Wire signal to Host
 *
 * @param signal vw signal needs to set
 * @param level  level of vw signal
 * @return EC_SUCCESS, or non-zero if error.
 */
int espi_vw_set_wire(enum espi_vw_signal signal, uint8_t level)
{
	/* Get index of vw signal list by signale name */
	int i = espi_vw_get_signal_index(signal);

	if (i < 0)
		return EC_ERROR_PARAM1;

	/* critical section with interrupts off */
	interrupt_disable();
	if (level)
		IT83XX_ESPI_VWIDX(vw_channel_list[i].index) |=
			vw_channel_list[i].level_mask;
	else
		IT83XX_ESPI_VWIDX(vw_channel_list[i].index) &=
			~vw_channel_list[i].level_mask;
	/* restore interrupts */
	interrupt_enable();

	return EC_SUCCESS;
}

/**
 * Get eSPI Virtual-Wire signal from host
 *
 * @param signal vw signal needs to get
 * @return      1: set by host, otherwise: no signal
 */
int espi_vw_get_wire(enum espi_vw_signal signal)
{
	/* Get index of vw signal list by signale name */
	int i = espi_vw_get_signal_index(signal);

	if (i < 0)
		return 0;

	/* Not valid */
	if (!(IT83XX_ESPI_VWIDX(vw_channel_list[i].index) &
		vw_channel_list[i].valid_mask))
		return 0;

	return !!(IT83XX_ESPI_VWIDX(vw_channel_list[i].index) &
		vw_channel_list[i].level_mask);
}

/**
 * Enable VW interrupt of power sequence signal
 *
 * @param signal vw signal needs to enable interrupt
 * @return EC_SUCCESS, or non-zero if error.
 */
int espi_vw_enable_wire_int(enum espi_vw_signal signal)
{
	/*
	 * Common code calls this function to enable VW interrupt of power
	 * sequence signal.
	 * IT83xx only use a bit (bit7@IT83XX_ESPI_VWCTRL0) to enable VW
	 * interrupt.
	 * VW interrupt will be triggerd with any updated VW index flag
	 * if this control bit is set.
	 * So we will always return success here.
	 */
	return EC_SUCCESS;
}

/**
 * Disable VW interrupt of power sequence signal
 *
 * @param signal vw signal needs to disable interrupt
 * @return EC_SUCCESS, or non-zero if error.
 */
int espi_vw_disable_wire_int(enum espi_vw_signal signal)
{
	/*
	 * We can't disable VW interrupt of power sequence signal
	 * individually.
	 */
	return EC_ERROR_UNIMPLEMENTED;
}

/* Configure virtual wire outputs */
static void espi_configure_vw(const struct vw_channel_t *settings,
						size_t entries)
{
	size_t i;

	for (i = 0; i < entries; i++)
		IT83XX_ESPI_VWIDX(settings[i].index) |=
			(settings[i].level_mask | settings[i].valid_mask);
}

static void espi_vw_host_startup(void)
{
	espi_configure_vw(vw_host_startup_setting,
				ARRAY_SIZE(vw_host_startup_setting));
}

static void espi_vw_no_isr(uint8_t flag_changed, uint8_t vw_evt)
{
	CPRINTS("espi VW interrupt event is ignored! (bit%d at VWCTRL1)",
								vw_evt);
}

#ifndef CONFIG_CHIPSET_GEMINILAKE
static void espi_vw_idx41_isr(uint8_t flag_changed, uint8_t vw_evt)
{
	if (flag_changed & VW_LEVEL_FIELD(VW_IDX_41_SUS_WARN))
		espi_vw_set_wire(VW_SUS_ACK, espi_vw_get_wire(VW_SUS_WARN_L));
}
#endif

static void espi_vw_idx7_isr(uint8_t flag_changed, uint8_t vw_evt)
{
	if (flag_changed & VW_LEVEL_FIELD(VW_IDX_7_HOST_RST_WARN))
		espi_vw_set_wire(VW_HOST_RST_ACK,
			espi_vw_get_wire(VW_HOST_RST_WARN));
}

#ifdef CONFIG_CHIPSET_RESET_HOOK
static void espi_chipset_reset(void)
{
	hook_notify(HOOK_CHIPSET_RESET);
}
DECLARE_DEFERRED(espi_chipset_reset);
#endif

static void espi_vw_idx3_isr(uint8_t flag_changed, uint8_t vw_evt)
{
	if (flag_changed & VW_LEVEL_FIELD(VW_IDX_3_PLTRST)) {
		int pltrst = espi_vw_get_wire(VW_PLTRST_L);

		if (pltrst) {
			espi_vw_host_startup();
		} else {
#ifdef CONFIG_CHIPSET_RESET_HOOK
			hook_call_deferred(&espi_chipset_reset_data, MSEC);
#endif
			/* Store port 80 reset event */
			port_80_write(PORT_80_EVENT_RESET);
		}

		CPRINTS("VW PLTRST_L %sasserted", pltrst ? "de" : "");
	}

	if (flag_changed & VW_LEVEL_FIELD(VW_IDX_3_OOB_RST_WARN))
		espi_vw_set_wire(VW_OOB_RST_ACK,
			espi_vw_get_wire(VW_OOB_RST_WARN));
}

static void espi_vw_idx2_isr(uint8_t flag_changed, uint8_t vw_evt)
{
	if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S3))
		power_signal_interrupt(VW_SLP_S3_L);
	if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S4))
		power_signal_interrupt(VW_SLP_S4_L);
	if (flag_changed & VW_LEVEL_FIELD(VW_IDX_2_SLP_S5))
		power_signal_interrupt(VW_SLP_S5_L);
}

struct vw_interrupt_t {
	void (*vw_isr)(uint8_t flag_changed, uint8_t vw_evt);
	uint8_t vw_index;
};

/*
 * The ISR of espi VW interrupt in array needs to match bit order in
 * IT83XX_ESPI_VWCTRL1 register.
 */
#ifdef CONFIG_CHIPSET_GEMINILAKE
static const struct vw_interrupt_t vw_isr_list[] = {
	[0] = {espi_vw_idx2_isr,  ESPI_SYSTEM_EVENT_VW_IDX_2},
	[1] = {espi_vw_idx3_isr,  ESPI_SYSTEM_EVENT_VW_IDX_3},
	[2] = {espi_vw_idx7_isr,  ESPI_SYSTEM_EVENT_VW_IDX_7},
	[3] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_41},
	[4] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_42},
	[5] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_43},
	[6] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_44},
	[7] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_47},
};
#else
static const struct vw_interrupt_t vw_isr_list[] = {
	[0] = {espi_vw_idx2_isr,  ESPI_SYSTEM_EVENT_VW_IDX_2},
	[1] = {espi_vw_idx3_isr,  ESPI_SYSTEM_EVENT_VW_IDX_3},
	[2] = {espi_vw_idx7_isr,  ESPI_SYSTEM_EVENT_VW_IDX_7},
	[3] = {espi_vw_idx41_isr, ESPI_SYSTEM_EVENT_VW_IDX_41},
	[4] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_42},
	[5] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_43},
	[6] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_44},
	[7] = {espi_vw_no_isr,    ESPI_SYSTEM_EVENT_VW_IDX_47},
};
#endif

/*
 * This is used to record the previous VW valid / level field state to discover
 * changes. Then do following sequence only when state is changed.
 */
static uint8_t vw_index_flag[ARRAY_SIZE(vw_isr_list)];

void espi_vw_interrupt(void)
{
	int i;
	uint8_t vwidx_updated = IT83XX_ESPI_VWCTRL1;

#ifdef IT83XX_ESPI_VWCTRL1_WRITE_FF_CLEAR
	/* For IT8320BX, we have to write 0xff to clear pending bit.*/
	IT83XX_ESPI_VWCTRL1 = 0xff;
#else
	/* write-1 to clear */
	IT83XX_ESPI_VWCTRL1 = vwidx_updated;
#endif
	task_clear_pending_irq(IT83XX_IRQ_ESPI_VW);

	for (i = 0; i < ARRAY_SIZE(vw_isr_list); i++) {
		if (vwidx_updated & BIT(i)) {
			uint8_t idx_flag;

			idx_flag = IT83XX_ESPI_VWIDX(vw_isr_list[i].vw_index);
			vw_isr_list[i].vw_isr(vw_index_flag[i] ^ idx_flag, i);
			vw_index_flag[i] = idx_flag;
		}
	}
}

static void espi_reset_vw_index_flags(void)
{
	int i;

	/* reset vw_index_flag */
	for (i = 0; i < ARRAY_SIZE(vw_isr_list); i++)
		vw_index_flag[i] = IT83XX_ESPI_VWIDX(vw_isr_list[i].vw_index);
}

#ifdef IT83XX_ESPI_RESET_MODULE_BY_FW
void __ram_code espi_fw_reset_module(void)
{
	/*
	 * (b/111480168): Force a reset of logic VCC domain in EC. This will
	 * reset both LPC and eSPI blocks. The IT8320DX spec describes the
	 * purpose of these bits as deciding whether VCC power status is used as
	 * an internal "power good" signal. However, toggling this field while
	 * VCC is applied results in resettig VCC domain logic in EC. This code
	 * must reside in SRAM to prevent DMA address corruption.
	 *
	 * bit[7-6]:
	 * 00b: The VCC power status is treated as power-off.
	 * 01b: The VCC power status is treated as power-on.
	 */
	IT83XX_GCTRL_RSTS = (IT83XX_GCTRL_RSTS & ~0xc0);
	IT83XX_GCTRL_RSTS = (IT83XX_GCTRL_RSTS & ~0xc0) | BIT(6);
}
#endif

void espi_reset_pin_asserted_interrupt(enum gpio_signal signal)
{
#ifdef IT83XX_ESPI_RESET_MODULE_BY_FW
	espi_fw_reset_module();
	/*
	 * bit[7], enable P80L function.
	 * bit[6], accept port 80h cycle.
	 * bit[1-0], 10b: I2EC is read-only.
	 */
	IT83XX_GCTRL_SPCTRL1 |= 0xC2;
#endif
	/* reset vw_index_flag when espi_reset# asserted. */
	espi_reset_vw_index_flags();
}

static int espi_get_reset_enable_config(void)
{
	uint8_t config;
	const struct gpio_info *espi_rst = gpio_list + GPIO_ESPI_RESET_L;

	/*
	 * Determine if eSPI HW reset is connected to eiter B7 or D2.
	 * bit[2-1]:
	 * 00b: reserved.
	 * 01b: espi_reset# is enabled on GPB7.
	 * 10b: espi_reset# is enabled on GPD2.
	 * 11b: reset is disabled.
	 */
	if (espi_rst->port == GPIO_D && espi_rst->mask == BIT(2)) {
		config = IT83XX_GPIO_GCR_LPC_RST_D2;
	} else if (espi_rst->port == GPIO_B && espi_rst->mask == BIT(7)) {
		config = IT83XX_GPIO_GCR_LPC_RST_B7;
	} else {
		config = IT83XX_GPIO_GCR_LPC_RST_DISABLE;
		CPRINTS("EC's espi_reset pin is not enabled correctly");
	}

	return config;
}

static void espi_enable_reset(void)
{
	int config = espi_get_reset_enable_config();

#ifdef IT83XX_ESPI_RESET_MODULE_BY_FW
	/*
	 * Need to overwrite the config to ensure that eSPI HW reset is
	 * disabled. The reset function is instead handled by FW in the
	 * interrupt handler.
	 */
	config = IT83XX_GPIO_GCR_LPC_RST_DISABLE;
	CPRINTS("EC's espi_reset pin hw auto reset is disabled");

#endif
	IT83XX_GPIO_GCR = (IT83XX_GPIO_GCR & ~0x6) |
		(config << IT83XX_GPIO_GCR_LPC_RST_POS);

	/* enable interrupt of EC's espi_reset pin */
	gpio_clear_pending_interrupt(GPIO_ESPI_RESET_L);
	gpio_enable_interrupt(GPIO_ESPI_RESET_L);
}

/* Interrupt event of controller enables the VW channel. */
static void espi_vw_en_asserted(uint8_t evt)
{
	/*
	 * Configure peripheral to controller virtual wire outputs after
	 * receiving the event of controller enables the VW channel.
	 */
	espi_configure_vw(en_vw_setting, ARRAY_SIZE(en_vw_setting));
}

/* Interrupt event of controller enables the OOB channel. */
static void espi_oob_en_asserted(uint8_t evt)
{
	/*
	 * Configure peripheral to controller virtual wire outputs after
	 * receiving the event of controller enables the OOB channel.
	 */
	espi_configure_vw(en_oob_setting, ARRAY_SIZE(en_oob_setting));
}

/* Interrupt event of controller enables the flash channel. */
static void espi_flash_en_asserted(uint8_t evt)
{
	/*
	 * Configure peripheral to controller virtual wire outputs after
	 * receiving the event of controller enables the flash channel.
	 */
	espi_configure_vw(en_flash_setting, ARRAY_SIZE(en_flash_setting));
}

static void espi_no_isr(uint8_t evt)
{
	CPRINTS("espi interrupt event is ignored! (bit%d at ESGCTRL0)", evt);
}

/*
 * The ISR of espi interrupt event in array need to be matched bit order in
 * IT83XX_ESPI_ESGCTRL0 register.
 */
static void (*espi_isr[])(uint8_t evt) = {
	[0] = espi_no_isr,
	[1] = espi_vw_en_asserted,
	[2] = espi_oob_en_asserted,
	[3] = espi_flash_en_asserted,
	[4] = espi_no_isr,
	[5] = espi_no_isr,
	[6] = espi_no_isr,
	[7] = espi_no_isr,
};

void espi_interrupt(void)
{
	int i;
	/* get espi interrupt events */
	uint8_t espi_event = IT83XX_ESPI_ESGCTRL0;

	/* write-1 to clear */
	IT83XX_ESPI_ESGCTRL0 = espi_event;
	/* process espi interrupt events */
	for (i = 0; i < ARRAY_SIZE(espi_isr); i++) {
		if (espi_event & BIT(i))
			espi_isr[i](i);
	}
	/*
	 * bit7: the peripheral has received a peripheral posted/completion.
	 * This bit indicates the peripheral has received a packet from eSPI
	 * peripheral channel. We can check cycle type (bit[3-0] at ESPCTRL0)
	 * and make corresponding modification if needed.
	 */
	if (IT83XX_ESPI_ESPCTRL0 & ESPI_INTERRUPT_EVENT_PUT_PC) {
		/* write-1-clear to release PC_FREE */
		IT83XX_ESPI_ESPCTRL0 = ESPI_INTERRUPT_EVENT_PUT_PC;
		CPRINTS("A packet from peripheral channel is ignored!");
	}

	task_clear_pending_irq(IT83XX_IRQ_ESPI);
}

#ifdef IT83XX_ESPI_INHIBIT_CS_BY_PAD_DISABLED
/* Enable/Disable eSPI pad */
void espi_enable_pad(int enable)
{
	if (enable)
		/* Enable eSPI pad. */
		IT83XX_ESPI_ESGCTRL2 &= ~BIT(6);
	else
		/* Disable eSPI pad. */
		IT83XX_ESPI_ESGCTRL2 |= BIT(6);
}
#endif

void espi_init(void)
{
	/*
	 * bit[2-0], the maximum frequency of operation supported by peripheral:
	 * 000b: 20MHz
	 * 001b: 25MHz
	 * 010b: 33MHz
	 * 011b: 50MHz
	 * 100b: 66MHz
	 */
#ifdef IT83XX_ESPI_PERIPHERAL_MAX_FREQ_CONFIGURABLE
	IT83XX_ESPI_GCAC1 = (IT83XX_ESPI_GCAC1 & ~0x7) | BIT(2);
#endif
	/* reset vw_index_flag at initialization */
	espi_reset_vw_index_flags();

	/*
	 * bit[3]: The reset source of PNPCFG is RSTPNP bit in RSTCH
	 * register and WRST#.
	 */
	IT83XX_GCTRL_RSTS &= ~BIT(3);
	task_clear_pending_irq(IT83XX_IRQ_ESPI_VW);
	/* bit7: VW interrupt enable */
	IT83XX_ESPI_VWCTRL0 |= BIT(7);
	task_enable_irq(IT83XX_IRQ_ESPI_VW);

	/* bit7: eSPI interrupt enable */
	IT83XX_ESPI_ESGCTRL1 |= BIT(7);
	/* bit4: eSPI to WUC enable */
	IT83XX_ESPI_ESGCTRL2 |= BIT(4);
	task_enable_irq(IT83XX_IRQ_ESPI);

	/* enable interrupt and reset from eSPI_reset# */
	espi_enable_reset();
}