/* Copyright (c) 2014 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.
 */

/* LPC module for Chrome EC */

#include "acpi.h"
#include "chipset.h"
#include "clock.h"
#include "common.h"
#include "console.h"
#include "ec2i_chip.h"
#include "gpio.h"
#include "hooks.h"
#include "host_command.h"
#include "irq_chip.h"
#include "keyboard_protocol.h"
#include "lpc.h"
#include "port80.h"
#include "pwm.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
#include "util.h"

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

#define LPC_SYSJUMP_TAG 0x4c50  /* "LP" */

/* LPC PM channels */
enum lpc_pm_ch {
	LPC_PM1 = 0,
	LPC_PM2,
	LPC_PM3,
	LPC_PM4,
	LPC_PM5,
};

enum pm_ctrl_mask {
	/* Input Buffer Full Interrupt Enable. */
	PM_CTRL_IBFIE = 0x01,
	/* Output Buffer Empty Interrupt Enable. */
	PM_CTRL_OBEIE = 0x02,
};

#define LPC_ACPI_CMD      LPC_PM1   /* ACPI commands 62h/66h port */
#define LPC_HOST_CMD      LPC_PM2   /* Host commands 200h/204h port */
#define LPC_HOST_PORT_80H LPC_PM3   /* Host 80h port */

static uint8_t acpi_ec_memmap[EC_MEMMAP_SIZE]
			__attribute__((section(".h2ram.pool.acpiec")));
static uint8_t host_cmd_memmap[256]
			__attribute__((section(".h2ram.pool.hostcmd")));

static uint32_t host_events;     /* Currently pending SCI/SMI events */
static uint32_t event_mask[3];   /* Event masks for each type */
static struct host_packet lpc_packet;
static struct host_cmd_handler_args host_cmd_args;
static uint8_t host_cmd_flags;   /* Flags from host command */

/* Params must be 32-bit aligned */
static uint8_t params_copy[EC_LPC_HOST_PACKET_SIZE] __aligned(4);
static int init_done;
static int p80l_index;

static uint8_t * const cmd_params = (uint8_t *)host_cmd_memmap +
	EC_LPC_ADDR_HOST_PARAM - EC_LPC_ADDR_HOST_ARGS;
static struct ec_lpc_host_args * const lpc_host_args =
	(struct ec_lpc_host_args *)host_cmd_memmap;

static void pm_set_ctrl(enum lpc_pm_ch ch, enum pm_ctrl_mask ctrl, int set)
{
	if (set)
		IT83XX_PMC_PMCTL(ch) |= ctrl;
	else
		IT83XX_PMC_PMCTL(ch) &= ~ctrl;
}

static void pm_set_status(enum lpc_pm_ch ch, uint8_t status, int set)
{
	if (set)
		IT83XX_PMC_PMSTS(ch) |= status;
	else
		IT83XX_PMC_PMSTS(ch) &= ~status;
}

static uint8_t pm_get_status(enum lpc_pm_ch ch)
{
	return IT83XX_PMC_PMSTS(ch);
}

static uint8_t pm_get_data_in(enum lpc_pm_ch ch)
{
	return IT83XX_PMC_PMDI(ch);
}

static void pm_put_data_out(enum lpc_pm_ch ch, uint8_t out)
{
	IT83XX_PMC_PMDO(ch) = out;
}

static void pm_clear_ibf(enum lpc_pm_ch ch)
{
	/* bit7, write-1 clear IBF */
	IT83XX_PMC_PMIE(ch) |= (1 << 7);
}

#ifdef CONFIG_KEYBOARD_IRQ_GPIO
static void keyboard_irq_assert(void)
{
	/*
	 * Enforce signal-high for long enough for the signal to be pulled high
	 * by the external pullup resistor.  This ensures the host will see the
	 * following falling edge, regardless of the line state before this
	 * function call.
	 */
	gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1);
	udelay(4);
	/* Generate a falling edge */
	gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 0);
	udelay(4);

	/* Set signal high, now that we've generated the edge */
	gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1);
}
#endif

/**
 * Generate SMI pulse to the host chipset via GPIO.
 *
 * If the x86 is in S0, SMI# is sampled at 33MHz, so minimum pulse length is
 * 60ns.  If the x86 is in S3, SMI# is sampled at 32.768KHz, so we need pulse
 * length >61us.  Both are short enough and events are infrequent, so just
 * delay for 65us.
 */
static void lpc_generate_smi(void)
{
	gpio_set_level(GPIO_PCH_SMI_L, 0);
	udelay(65);
	gpio_set_level(GPIO_PCH_SMI_L, 1);
}

static void lpc_generate_sci(void)
{
	gpio_set_level(GPIO_PCH_SCI_L, 0);
	udelay(65);
	gpio_set_level(GPIO_PCH_SCI_L, 1);
}

/**
 * Update the level-sensitive wake signal to the AP.
 *
 * @param wake_events	Currently asserted wake events
 */
static void lpc_update_wake(uint32_t wake_events)
{
	/*
	 * Mask off power button event, since the AP gets that through a
	 * separate dedicated GPIO.
	 */
	wake_events &= ~EC_HOST_EVENT_MASK(EC_HOST_EVENT_POWER_BUTTON);

	/* Signal is asserted low when wake events is non-zero */
	gpio_set_level(GPIO_PCH_WAKE_L, !wake_events);
}

static void lpc_send_response(struct host_cmd_handler_args *args)
{
	uint8_t *out;
	int size = args->response_size;
	int csum;
	int i;

	/* Ignore in-progress on LPC since interface is synchronous anyway */
	if (args->result == EC_RES_IN_PROGRESS)
		return;

	/* Handle negative size */
	if (size < 0) {
		args->result = EC_RES_INVALID_RESPONSE;
		size = 0;
	}

	/* New-style response */
	lpc_host_args->flags =
		(host_cmd_flags & ~EC_HOST_ARGS_FLAG_FROM_HOST) |
		EC_HOST_ARGS_FLAG_TO_HOST;

	lpc_host_args->data_size = size;

	csum = args->command + lpc_host_args->flags +
		lpc_host_args->command_version +
		lpc_host_args->data_size;

	for (i = 0, out = (uint8_t *)args->response; i < size; i++, out++)
		csum += *out;

	lpc_host_args->checksum = (uint8_t)csum;

	/* Fail if response doesn't fit in the param buffer */
	if (size > EC_PROTO2_MAX_PARAM_SIZE)
		args->result = EC_RES_INVALID_RESPONSE;

	/* Write result to the data byte.  This sets the OBF status bit. */
	pm_put_data_out(LPC_HOST_CMD, args->result);

	/* Clear the busy bit, so the host knows the EC is done. */
	pm_set_status(LPC_HOST_CMD, EC_LPC_STATUS_PROCESSING, 0);
}

static void update_host_event_status(void)
{
	int need_sci = 0;
	int need_smi = 0;

	if (!init_done)
		return;

	/* Disable PMC1 interrupt while updating status register */
	task_disable_irq(IT83XX_IRQ_PMC_IN);

	if (host_events & event_mask[LPC_HOST_EVENT_SMI]) {
		/* Only generate SMI for first event */
		if (!(pm_get_status(LPC_ACPI_CMD) & EC_LPC_STATUS_SMI_PENDING))
			need_smi = 1;
		pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_SMI_PENDING, 1);
	} else {
		pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_SMI_PENDING, 0);
	}

	if (host_events & event_mask[LPC_HOST_EVENT_SCI]) {
		/* Generate SCI for every event */
		need_sci = 1;
		pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_SCI_PENDING, 1);
	} else {
		pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_SCI_PENDING, 0);
	}

	/* Copy host events to mapped memory */
	*(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = host_events;

	task_enable_irq(IT83XX_IRQ_PMC_IN);

	/* Process the wake events. */
	lpc_update_wake(host_events & event_mask[LPC_HOST_EVENT_WAKE]);

	/* Send pulse on SMI signal if needed */
	if (need_smi)
		lpc_generate_smi();

	/* ACPI 5.0-12.6.1: Generate SCI for SCI_EVT=1. */
	if (need_sci)
		lpc_generate_sci();
}

static void lpc_send_response_packet(struct host_packet *pkt)
{
	/* Ignore in-progress on LPC since interface is synchronous anyway */
	if (pkt->driver_result == EC_RES_IN_PROGRESS)
		return;

	/* Write result to the data byte. */
	pm_put_data_out(LPC_HOST_CMD, pkt->driver_result);

	/* Clear the busy bit, so the host knows the EC is done. */
	pm_set_status(LPC_HOST_CMD, EC_LPC_STATUS_PROCESSING, 0);
}

uint8_t *lpc_get_memmap_range(void)
{
	return (uint8_t *)acpi_ec_memmap;
}

int lpc_keyboard_has_char(void)
{
	/* OBE or OBF */
	return IT83XX_KBC_KBHISR & 0x01;
}

int lpc_keyboard_input_pending(void)
{
	/* IBE or IBF */
	return IT83XX_KBC_KBHISR & 0x02;
}

void lpc_keyboard_put_char(uint8_t chr, int send_irq)
{
	/* Clear programming data bit 7-4 */
	IT83XX_KBC_KBHISR &= 0x0F;

	/* keyboard */
	IT83XX_KBC_KBHISR |= 0x10;

#ifdef CONFIG_KEYBOARD_IRQ_GPIO
	task_clear_pending_irq(IT83XX_IRQ_KBC_OUT);
	/* The data output to the KBC Data Output Register. */
	IT83XX_KBC_KBHIKDOR = chr;
	task_enable_irq(IT83XX_IRQ_KBC_OUT);
	if (send_irq)
		keyboard_irq_assert();
#else
	/*
	 * bit0 = 0, The IRQ1 is controlled by the IRQ1B bit in KBIRQR.
	 * bit1 = 0, The IRQ12 is controlled by the IRQ12B bit in KBIRQR.
	 */
	IT83XX_KBC_KBHICR &= 0x3C;

	/*
	 * Enable the interrupt to keyboard driver in the host processor
	 * via SERIRQ when the output buffer is full.
	 */
	if (send_irq)
		IT83XX_KBC_KBHICR |= 0x01;

	udelay(16);

	task_clear_pending_irq(IT83XX_IRQ_KBC_OUT);
	/* The data output to the KBC Data Output Register. */
	IT83XX_KBC_KBHIKDOR = chr;
	task_enable_irq(IT83XX_IRQ_KBC_OUT);
#endif
}

void lpc_keyboard_clear_buffer(void)
{
	uint32_t int_mask = get_int_mask();
	interrupt_disable();
	/* bit6, write-1 clear OBF */
	IT83XX_KBC_KBHICR |= (1 << 6);
	IT83XX_KBC_KBHICR &= ~(1 << 6);
	set_int_mask(int_mask);
}

void lpc_keyboard_resume_irq(void)
{
	if (lpc_keyboard_has_char()) {
#ifdef CONFIG_KEYBOARD_IRQ_GPIO
		keyboard_irq_assert();
#else
		/* The IRQ1 is controlled by the IRQ1B bit in KBIRQR. */
		IT83XX_KBC_KBHICR &= ~0x01;

		/*
		 * When the OBFKIE bit in KBC Host Interface Control Register
		 * (KBHICR) is 0, the bit directly controls the IRQ1 signal.
		 */
		IT83XX_KBC_KBIRQR |= 0x01;
#endif

		task_clear_pending_irq(IT83XX_IRQ_KBC_OUT);

		task_enable_irq(IT83XX_IRQ_KBC_OUT);
	}
}

void lpc_set_host_event_state(uint32_t mask)
{
	if (mask != host_events) {
		host_events = mask;
		update_host_event_status();
	}
}

int lpc_query_host_event_state(void)
{
	const uint32_t any_mask = event_mask[0] | event_mask[1] | event_mask[2];
	int evt_index = 0;
	int i;

	for (i = 0; i < 32; i++) {
		const uint32_t e = (1 << i);

		if (host_events & e) {
			host_clear_events(e);

			/*
			 * If host hasn't unmasked this event, drop it.  We do
			 * this at query time rather than event generation time
			 * so that the host has a chance to unmask events
			 * before they're dropped by a query.
			 */
			if (!(e & any_mask))
				continue;

			evt_index = i + 1;	/* Events are 1-based */
			break;
		}
	}

	return evt_index;
}

void lpc_set_host_event_mask(enum lpc_host_event_type type, uint32_t mask)
{
	event_mask[type] = mask;
	update_host_event_status();
}

uint32_t lpc_get_host_event_mask(enum lpc_host_event_type type)
{
	return event_mask[type];
}

void lpc_set_acpi_status_mask(uint8_t mask)
{
	pm_set_status(LPC_ACPI_CMD, mask, 1);
}

void lpc_clear_acpi_status_mask(uint8_t mask)
{
	pm_set_status(LPC_ACPI_CMD, mask, 0);
}

int lpc_get_pltrst_asserted(void)
{
	return !gpio_get_level(GPIO_PCH_PLTRST_L);
}

/* KBC and PMC control modules */
void lpc_kbc_ibf_interrupt(void)
{
	if (lpc_keyboard_input_pending()) {
		keyboard_host_write(IT83XX_KBC_KBHIDIR,
			(IT83XX_KBC_KBHISR & 0x08) ? 1 : 0);
		/* bit7, write-1 clear IBF */
		IT83XX_KBC_KBHICR |= (1 << 7);
		IT83XX_KBC_KBHICR &= ~(1 << 7);
	}

	task_clear_pending_irq(IT83XX_IRQ_KBC_IN);

#ifdef HAS_TASK_KEYPROTO
	task_wake(TASK_ID_KEYPROTO);
#endif
}

void lpc_kbc_obe_interrupt(void)
{
	task_disable_irq(IT83XX_IRQ_KBC_OUT);

	task_clear_pending_irq(IT83XX_IRQ_KBC_OUT);

#ifndef CONFIG_KEYBOARD_IRQ_GPIO
	if (!(IT83XX_KBC_KBHICR & 0x01)) {
		IT83XX_KBC_KBIRQR &= ~0x01;

		IT83XX_KBC_KBHICR |= 0x01;
	}
#endif

#ifdef HAS_TASK_KEYPROTO
	task_wake(TASK_ID_KEYPROTO);
#endif
}

void pm1_ibf_interrupt(void)
{
	int is_cmd;
	uint8_t value, result;

	if (pm_get_status(LPC_ACPI_CMD) & EC_LPC_STATUS_FROM_HOST) {
		/* Set the busy bit */
		pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_PROCESSING, 1);

		/* data from command port or data port */
		is_cmd = pm_get_status(LPC_ACPI_CMD) & EC_LPC_STATUS_LAST_CMD;

		/* Get command or data */
		value = pm_get_data_in(LPC_ACPI_CMD);

		/* Handle whatever this was. */
		if (acpi_ap_to_ec(is_cmd, value, &result))
			pm_put_data_out(LPC_ACPI_CMD, result);

		pm_clear_ibf(LPC_ACPI_CMD);

		/* Clear the busy bit */
		pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_PROCESSING, 0);

		/*
		 * ACPI 5.0-12.6.1: Generate SCI for Input Buffer Empty
		 * Output Buffer Full condition on the kernel channel.
		 */
		lpc_generate_sci();
	}

	task_clear_pending_irq(IT83XX_IRQ_PMC_IN);
}

void pm2_ibf_interrupt(void)
{
	uint8_t value __attribute__((unused)) = 0;
	uint8_t status;

	status = pm_get_status(LPC_HOST_CMD);
	/* IBE */
	if (!(status & EC_LPC_STATUS_FROM_HOST)) {
		task_clear_pending_irq(IT83XX_IRQ_PMC2_IN);
		return;
	}

	/* IBF and data port */
	if (!(status & EC_LPC_STATUS_LAST_CMD)) {
		/* R/C IBF*/
		value = pm_get_data_in(LPC_HOST_CMD);
		pm_clear_ibf(LPC_HOST_CMD);
		task_clear_pending_irq(IT83XX_IRQ_PMC2_IN);
		return;
	}

	/* Set the busy bit */
	pm_set_status(LPC_HOST_CMD, EC_LPC_STATUS_PROCESSING, 1);

	/*
	 * Read the command byte.  This clears the FRMH bit in
	 * the status byte.
	 */
	host_cmd_args.command = pm_get_data_in(LPC_HOST_CMD);

	host_cmd_args.result = EC_RES_SUCCESS;
	if (host_cmd_args.command != EC_COMMAND_PROTOCOL_3)
		host_cmd_args.send_response = lpc_send_response;
	host_cmd_flags = lpc_host_args->flags;

	/* We only support new style command (v3) now */
	if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) {
		lpc_packet.send_response = lpc_send_response_packet;

		lpc_packet.request = (const void *)host_cmd_memmap;
		lpc_packet.request_temp = params_copy;
		lpc_packet.request_max = sizeof(params_copy);
		/* Don't know the request size so pass in the entire buffer */
		lpc_packet.request_size = EC_LPC_HOST_PACKET_SIZE;

		lpc_packet.response = (void *)host_cmd_memmap;
		lpc_packet.response_max = EC_LPC_HOST_PACKET_SIZE;
		lpc_packet.response_size = 0;

		lpc_packet.driver_result = EC_RES_SUCCESS;
		host_packet_receive(&lpc_packet);

		pm_clear_ibf(LPC_HOST_CMD);
		task_clear_pending_irq(IT83XX_IRQ_PMC2_IN);
		return;
	} else {
		/* Old style command, now unsupported */
		host_cmd_args.result = EC_RES_INVALID_COMMAND;
	}

	/* Hand off to host command handler */
	host_command_received(&host_cmd_args);

	pm_clear_ibf(LPC_HOST_CMD);
	task_clear_pending_irq(IT83XX_IRQ_PMC2_IN);
}

void pm3_ibf_interrupt(void)
{
	int new_p80_idx, i;
	enum ec2i_message ec2i_r;

	/* set LDN */
	if (ec2i_write(HOST_INDEX_LDN, LDN_RTCT) == EC2I_WRITE_SUCCESS) {
		/* get P80L current index */
		ec2i_r = ec2i_read(HOST_INDEX_DSLDC6);
		/* clear IBF */
		pm_clear_ibf(LPC_HOST_PORT_80H);
		/* read OK */
		if ((ec2i_r & 0xff00) == EC2I_READ_SUCCESS) {
			new_p80_idx = ec2i_r & P80L_BRAM_BANK1_SIZE_MASK;
			for (i = 0; i < (P80L_P80LE - P80L_P80LB + 1); i++) {
				if (++p80l_index > P80L_P80LE)
					p80l_index = P80L_P80LB;
				port_80_write(IT83XX_BRAM_BANK1(p80l_index));
				if (p80l_index == new_p80_idx)
					break;
			}
		}
	} else {
		pm_clear_ibf(LPC_HOST_PORT_80H);
	}

	task_clear_pending_irq(IT83XX_IRQ_PMC3_IN);
}

void pm4_ibf_interrupt(void)
{
	pm_clear_ibf(LPC_PM4);
	task_clear_pending_irq(IT83XX_IRQ_PMC4_IN);
}

void pm5_ibf_interrupt(void)
{
	pm_clear_ibf(LPC_PM5);
	task_clear_pending_irq(IT83XX_IRQ_PMC5_IN);
}

/**
 * Preserve event masks across a sysjump.
 */
static void lpc_sysjump(void)
{
	system_add_jump_tag(LPC_SYSJUMP_TAG, 1,
				sizeof(event_mask), event_mask);
}
DECLARE_HOOK(HOOK_SYSJUMP, lpc_sysjump, HOOK_PRIO_DEFAULT);

/**
 * Restore event masks after a sysjump.
 */
static void lpc_post_sysjump(void)
{
	const uint32_t *prev_mask;
	int size, version;

	prev_mask = (const uint32_t *)system_get_jump_tag(LPC_SYSJUMP_TAG,
							  &version, &size);
	if (!prev_mask || version != 1 || size != sizeof(event_mask))
		return;

	memcpy(event_mask, prev_mask, sizeof(event_mask));
}

static void lpc_init(void)
{
	enum ec2i_message ec2i_r;

	/* SPI slave interface is disabled */
	IT83XX_GCTRL_SSCR = 0;
	/*
	 * DLM 52k~56k size select enable.
	 * For mapping LPC I/O cycle 800h ~ 9FFh to DLM 8D800 ~ 8D9FF.
	 */
	IT83XX_GCTRL_MCCR2 |= 0x10;

	/* The register pair to access PNPCFG is 004Eh and 004Fh */
	IT83XX_GCTRL_BADRSEL = 0x01;

	/* Disable KBC IRQ */
	IT83XX_KBC_KBIRQR = 0x00;

	/*
	 * bit2, Output Buffer Empty CPU Interrupt Enable.
	 * bit3, Input Buffer Full CPU Interrupt Enable.
	 * bit5, IBF/OBF EC clear mode.
	 *   0b: IBF cleared if EC read data register, EC reset, or host reset.
	 *       OBF cleared if host read data register, or EC reset.
	 *   1b: IBF cleared if EC write-1 to bit7 at related registers,
	 *       EC reset, or host reset.
	 *       OBF cleared if host read data register, EC write-1 to bit6 at
	 *       related registers, or EC reset.
	 */
	IT83XX_KBC_KBHICR |= 0x2C;

	/* PM1 Input Buffer Full Interrupt Enable for 62h/66 port */
	pm_set_ctrl(LPC_ACPI_CMD, PM_CTRL_IBFIE, 1);

	/* PM2 Input Buffer Full Interrupt Enable for 200h/204 port */
	pm_set_ctrl(LPC_HOST_CMD, PM_CTRL_IBFIE, 1);

	memset(lpc_get_memmap_range(), 0, EC_MEMMAP_SIZE);
	memset(lpc_host_args, 0, sizeof(*lpc_host_args));

	/* Host LPC I/O cycle mapping to RAM */
	/*
	 * bit[4], H2RAM through LPC IO cycle.
	 * bit[1], H2RAM window 1 enabled.
	 * bit[0], H2RAM window 0 enabled.
	 */
	IT83XX_SMFI_HRAMWC |= 0x13;

	/*
	 * bit[7:6]
	 * Host RAM Window[x] Read Protect Enable
	 * 00b: Disabled
	 * 01b: Lower half of RAM window protected
	 * 10b: Upper half of RAM window protected
	 * 11b: All protected
	 *
	 * bit[5:4]
	 * Host RAM Window[x] Write Protect Enable
	 * 00b: Disabled
	 * 01b: Lower half of RAM window protected
	 * 10b: Upper half of RAM window protected
	 * 11b: All protected
	 *
	 * bit[2:0]
	 * Host RAM Window 1 Size (HRAMW1S)
	 * 0h: 16 bytes
	 * 1h: 32 bytes
	 * 2h: 64 bytes
	 * 3h: 128 bytes
	 * 4h: 256 bytes
	 * 5h: 512 bytes
	 * 6h: 1024 bytes
	 * 7h: 2048 bytes
	 */

	/* H2RAM Win 0 Base Address 800h allow r/w for host_cmd_memmap */
	IT83XX_SMFI_HRAMW0BA = 0x80;
	IT83XX_SMFI_HRAMW0AAS = 0x04;

	/* H2RAM Win 1 Base Address 900h allow r for acpi_ec_memmap */
	IT83XX_SMFI_HRAMW1BA = 0x90;
	IT83XX_SMFI_HRAMW1AAS = 0x34;

	/* We support LPC args and version 3 protocol */
	*(lpc_get_memmap_range() + EC_MEMMAP_HOST_CMD_FLAGS) =
		EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED |
		EC_HOST_CMD_FLAG_VERSION_3;

	/*
	 * bit[5], Dedicated interrupt
	 * INT3: PMC1 Output Buffer Empty Int
	 * INT25: PMC1 Input Buffer Full Int
	 * INT26: PMC2 Output Buffer Empty Int
	 * INT27: PMC2 Input Buffer Full Int
	 */
	IT83XX_PMC_MBXCTRL |= 0x20;

	/* PM3 Input Buffer Full Interrupt Enable for 80h port */
	pm_set_ctrl(LPC_HOST_PORT_80H, PM_CTRL_IBFIE, 1);

	p80l_index = P80L_P80LC;
	if (ec2i_write(HOST_INDEX_LDN, LDN_RTCT) == EC2I_WRITE_SUCCESS) {
		/* get P80L current index */
		ec2i_r = ec2i_read(HOST_INDEX_DSLDC6);
		/* read OK */
		if ((ec2i_r & 0xff00) == EC2I_READ_SUCCESS)
			p80l_index = ec2i_r & P80L_BRAM_BANK1_SIZE_MASK;
	}

	/*
	 * bit[7], enable P80L function.
	 * bit[6], accept port 80h cycle.
	 * bit[1-0], 10b: I2EC is read-only.
	 */
	IT83XX_GCTRL_SPCTRL1 |= 0xC2;

	gpio_enable_interrupt(GPIO_PCH_PLTRST_L);

	task_clear_pending_irq(IT83XX_IRQ_KBC_OUT);
	task_disable_irq(IT83XX_IRQ_KBC_OUT);

	task_clear_pending_irq(IT83XX_IRQ_KBC_IN);
	task_enable_irq(IT83XX_IRQ_KBC_IN);

	task_clear_pending_irq(IT83XX_IRQ_PMC_IN);
	pm_set_status(LPC_ACPI_CMD, EC_LPC_STATUS_PROCESSING, 0);
	task_enable_irq(IT83XX_IRQ_PMC_IN);

	task_clear_pending_irq(IT83XX_IRQ_PMC2_IN);
	pm_set_status(LPC_HOST_CMD, EC_LPC_STATUS_PROCESSING, 0);
	task_enable_irq(IT83XX_IRQ_PMC2_IN);

	task_clear_pending_irq(IT83XX_IRQ_PMC3_IN);
	task_enable_irq(IT83XX_IRQ_PMC3_IN);

	/* Restore event masks if needed */
	lpc_post_sysjump();

	/* Sufficiently initialized */
	init_done = 1;

	/* Update host events now that we can copy them to memmap */
	update_host_event_status();
}
/*
 * Set prio to higher than default; this way LPC memory mapped data is ready
 * before other inits try to initialize their memmap data.
 */
DECLARE_HOOK(HOOK_INIT, lpc_init, HOOK_PRIO_INIT_LPC);

void lpcrst_interrupt(enum gpio_signal signal)
{
	if (lpc_get_pltrst_asserted())
		/* Store port 80 reset event */
		port_80_write(PORT_80_EVENT_RESET);

	CPRINTS("LPC RESET# %sasserted",
		lpc_get_pltrst_asserted() ? "" : "de");
}

/* Enable LPC ACPI-EC interrupts */
void lpc_enable_acpi_interrupts(void)
{
	task_enable_irq(IT83XX_IRQ_PMC_IN);
}

/* Disable LPC ACPI-EC interrupts */
void lpc_disable_acpi_interrupts(void)
{
	task_disable_irq(IT83XX_IRQ_PMC_IN);
}

static void lpc_resume(void)
{
#ifdef CONFIG_POWER_S0IX
	if (chipset_in_state(CHIPSET_STATE_SUSPEND | CHIPSET_STATE_ON))
#endif
	{
		/* Mask all host events until the host unmasks them itself.  */
		lpc_set_host_event_mask(LPC_HOST_EVENT_SMI, 0);
		lpc_set_host_event_mask(LPC_HOST_EVENT_SCI, 0);
		lpc_set_host_event_mask(LPC_HOST_EVENT_WAKE, 0);
	}

	/* Store port 80 event so we know where resume happened */
	port_80_write(PORT_80_EVENT_RESUME);
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, lpc_resume, HOOK_PRIO_DEFAULT);

/* Get protocol information */
static int lpc_get_protocol_info(struct host_cmd_handler_args *args)
{
	struct ec_response_get_protocol_info *r = args->response;

	memset(r, 0, sizeof(*r));
	r->protocol_versions = (1 << 3);
	r->max_request_packet_size = EC_LPC_HOST_PACKET_SIZE;
	r->max_response_packet_size = EC_LPC_HOST_PACKET_SIZE;
	r->flags = 0;

	args->response_size = sizeof(*r);

	return EC_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_GET_PROTOCOL_INFO,
		lpc_get_protocol_info,
		EC_VER_MASK(0));
