summaryrefslogtreecommitdiff
path: root/chip/npcx/lpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/npcx/lpc.c')
-rw-r--r--chip/npcx/lpc.c672
1 files changed, 672 insertions, 0 deletions
diff --git a/chip/npcx/lpc.c b/chip/npcx/lpc.c
new file mode 100644
index 0000000000..55d76f46d0
--- /dev/null
+++ b/chip/npcx/lpc.c
@@ -0,0 +1,672 @@
+/* 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 "clock.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "host_command.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"
+#include "system_chip.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" */
+
+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 */
+static uint8_t shm_mem_host_cmd[256] __aligned(8);
+static uint8_t shm_memmap[256] __aligned(8);
+/* Params must be 32-bit aligned */
+static uint8_t params_copy[EC_LPC_HOST_PACKET_SIZE] __aligned(4);
+static int init_done;
+
+static uint8_t * const cmd_params = (uint8_t *)shm_mem_host_cmd +
+ 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 *)shm_mem_host_cmd;
+
+
+#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);
+}
+#else
+static inline void keyboard_irq_assert(void)
+{
+ /* Use serirq method. */
+ /* Using manual IRQ for KBC */
+ SET_BIT(NPCX_HIIRQC, 0); /* set IRQ1B to high */
+ CLEAR_BIT(NPCX_HICTRL, 0); /* set IRQ1 control by IRQB1 */
+}
+#endif
+
+static void lpc_task_enable_irq(void){
+
+ task_enable_irq(NPCX_IRQ_SHM);
+ task_enable_irq(NPCX_IRQ_KBC_IBF);
+ task_enable_irq(NPCX_IRQ_PM_CHAN_IBF);
+ task_enable_irq(NPCX_IRQ_PORT80);
+}
+static void lpc_task_disable_irq(void){
+
+ task_disable_irq(NPCX_IRQ_SHM);
+ task_disable_irq(NPCX_IRQ_KBC_IBF);
+ task_disable_irq(NPCX_IRQ_PM_CHAN_IBF);
+ task_disable_irq(NPCX_IRQ_PORT80);
+}
+/**
+ * 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)
+{
+#ifdef CONFIG_SCI_GPIO
+ /* Enforce signal-high for long enough to debounce high */
+ gpio_set_level(GPIO_PCH_SMI_L, 1);
+ udelay(65);
+ /* Generate a falling edge */
+ gpio_set_level(GPIO_PCH_SMI_L, 0);
+ udelay(65);
+ /* Set signal high, now that we've generated the edge */
+ gpio_set_level(GPIO_PCH_SMI_L, 1);
+#else
+ NPCX_HIPMIE(PM_CHAN_1) |= NPCX_HIPMIE_SMIE;
+#endif
+ if (host_events & event_mask[LPC_HOST_EVENT_SMI])
+ CPRINTS("smi 0x%08x",
+ host_events & event_mask[LPC_HOST_EVENT_SMI]);
+}
+
+/**
+ * Generate SCI pulse to the host chipset via LPC0SCI.
+ */
+static void lpc_generate_sci(void)
+{
+#ifdef CONFIG_SCI_GPIO
+ /* Enforce signal-high for long enough to debounce high */
+ gpio_set_level(CONFIG_SCI_GPIO, 1);
+ udelay(65);
+ /* Generate a falling edge */
+ gpio_set_level(CONFIG_SCI_GPIO, 0);
+ udelay(65);
+ /* Set signal high, now that we've generated the edge */
+ gpio_set_level(CONFIG_SCI_GPIO, 1);
+#else
+ SET_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SCIE);
+#endif
+
+ if (host_events & event_mask[LPC_HOST_EVENT_SCI])
+ CPRINTS("sci 0x%08x",
+ host_events & event_mask[LPC_HOST_EVENT_SCI]);
+}
+
+/**
+ * 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);
+}
+
+uint8_t *lpc_get_memmap_range(void)
+{
+ return (uint8_t *)shm_memmap;
+}
+
+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 TOH status bit. */
+ NPCX_HIPMDO(PM_CHAN_2) = args->result;
+ /* Clear processing flag */
+ CLEAR_BIT(NPCX_HIPMST(PM_CHAN_2), 2);
+}
+
+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. This sets the TOH status bit. */
+ NPCX_HIPMDO(PM_CHAN_2) = pkt->driver_result;
+ /* Clear processing flag */
+ CLEAR_BIT(NPCX_HIPMST(PM_CHAN_2), 2);
+}
+
+int lpc_keyboard_has_char(void)
+{
+ /* if OBF '1', that mean still have a data in the FIFO */
+ return (NPCX_HIKMST&0x01) ? 1 : 0;
+}
+
+/* Return true if the FRMH is set */
+int lpc_keyboard_input_pending(void)
+{
+ return (NPCX_HIKMST&0x02) ? 1 : 0;
+}
+
+/* Put a char to host buffer and send IRQ if specified. */
+void lpc_keyboard_put_char(uint8_t chr, int send_irq)
+{
+ UPDATE_BIT(NPCX_HICTRL, NPCX_HICTRL_OBFKIE, send_irq);
+ NPCX_HIKDO = chr;
+ task_enable_irq(NPCX_IRQ_KBC_OBF);
+}
+
+void lpc_keyboard_clear_buffer(void)
+{
+ /* Make sure the previous TOH and IRQ has been sent out. */
+ udelay(4);
+ /*FW_OBF write 1*/
+ NPCX_HICTRL |= 0x80;
+ /* Ensure there is no TOH set in this period. */
+ udelay(4);
+}
+
+void lpc_keyboard_resume_irq(void)
+{
+ if (lpc_keyboard_has_char())
+ keyboard_irq_assert();
+}
+
+/**
+ * Update the host event status.
+ *
+ * Sends a pulse if masked event status becomes non-zero:
+ * - SMI pulse via EC_SMI_L GPIO
+ * - SCI pulse via LPC0SCI
+ */
+static void update_host_event_status(void)
+{
+ int need_sci = 0;
+ int need_smi = 0;
+
+ if (!init_done)
+ return;
+
+ /* Disable LPC interrupt while updating status register */
+ lpc_task_disable_irq();
+ if (host_events & event_mask[LPC_HOST_EVENT_SMI]) {
+ /* Only generate SMI for first event */
+ if (!(NPCX_HIPMIE(PM_CHAN_1) & NPCX_HIPMIE_SMIE))
+ need_smi = 1;
+ SET_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SMIE);
+ } else
+ CLEAR_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SMIE);
+
+ if (host_events & event_mask[LPC_HOST_EVENT_SCI]) {
+ /* Generate SCI for every event */
+ need_sci = 1;
+ SET_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SCIE);
+ } else
+ CLEAR_BIT(NPCX_HIPMIE(PM_CHAN_1), NPCX_HIPMIE_SCIE);
+
+ /* Copy host events to mapped memory */
+ *(uint32_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = host_events;
+
+ lpc_task_enable_irq();
+
+ /* 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();
+}
+
+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];
+}
+
+int lpc_get_pltrst_asserted(void)
+{
+ /* TODO: (Simon) need to define GPIO_PLTRST */
+ return 0;
+}
+
+/**
+ * Handle write to ACPI I/O port
+ *
+ * @param is_cmd Is write command (is_cmd=1) or data (is_cmd=0)
+ */
+static void handle_acpi_write(int is_cmd)
+{
+ uint8_t value, result;
+
+ /* Read command/data; this clears the FRMH status bit. */
+ value = NPCX_HIPMDI(PM_CHAN_1);
+
+ /* Handle whatever this was. */
+ if (acpi_ap_to_ec(is_cmd, value, &result))
+ NPCX_HIPMDO(PM_CHAN_1) = result;
+
+ /*
+ * ACPI 5.0-12.6.1: Generate SCI for Input Buffer Empty / Output Buffer
+ * Full condition on the kernel channel.
+ */
+ lpc_generate_sci();
+}
+
+/**
+ * Handle write to host command I/O ports.
+ *
+ * @param is_cmd Is write command (1) or data (0)?
+ */
+static void handle_host_write(int is_cmd)
+{
+ /*
+ * Read the command byte. This clears the FRMH bit in
+ * the status byte.
+ */
+ host_cmd_args.command = NPCX_HIPMDI(PM_CHAN_2);
+
+ host_cmd_args.result = EC_RES_SUCCESS;
+ host_cmd_args.send_response = lpc_send_response;
+ host_cmd_flags = lpc_host_args->flags;
+
+ /* See if we have an old or new style command */
+ if (host_cmd_args.command == EC_COMMAND_PROTOCOL_3) {
+ lpc_packet.send_response = lpc_send_response_packet;
+
+ lpc_packet.request = (const void *)shm_mem_host_cmd;
+ 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 *)shm_mem_host_cmd;
+ lpc_packet.response_max = EC_LPC_HOST_PACKET_SIZE;
+ lpc_packet.response_size = 0;
+
+ lpc_packet.driver_result = EC_RES_SUCCESS;
+ /* Set processing flag */
+ SET_BIT(NPCX_HIPMST(PM_CHAN_2), 2);
+ host_packet_receive(&lpc_packet);
+ return;
+
+ } else if (host_cmd_flags & EC_HOST_ARGS_FLAG_FROM_HOST) {
+ /* Version 2 (link) style command */
+ int size = lpc_host_args->data_size;
+ int csum, i;
+
+ host_cmd_args.version = lpc_host_args->command_version;
+ host_cmd_args.params = params_copy;
+ host_cmd_args.params_size = size;
+ host_cmd_args.response = cmd_params;
+ host_cmd_args.response_max = EC_PROTO2_MAX_PARAM_SIZE;
+ host_cmd_args.response_size = 0;
+
+ /* Verify params size */
+ if (size > EC_PROTO2_MAX_PARAM_SIZE) {
+ host_cmd_args.result = EC_RES_INVALID_PARAM;
+ } else {
+ const uint8_t *src = cmd_params;
+ uint8_t *copy = params_copy;
+
+ /*
+ * Verify checksum and copy params out of LPC space.
+ * This ensures the data acted on by the host command
+ * handler can't be changed by host writes after the
+ * checksum is verified.
+ */
+ csum = host_cmd_args.command +
+ host_cmd_flags +
+ host_cmd_args.version +
+ host_cmd_args.params_size;
+
+ for (i = 0; i < size; i++) {
+ csum += *src;
+ *(copy++) = *(src++);
+ }
+
+ if ((uint8_t)csum != lpc_host_args->checksum)
+ host_cmd_args.result = EC_RES_INVALID_CHECKSUM;
+ }
+ } 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);
+}
+
+
+void lpc_shm_interrupt(void){
+}
+DECLARE_IRQ(NPCX_IRQ_SHM, lpc_shm_interrupt, 2);
+
+void lpc_kbc_ibf_interrupt(void)
+{
+#ifdef CONFIG_KEYBOARD_PROTOCOL_8042
+ /* If "command" input 0, else 1*/
+ keyboard_host_write(NPCX_HIKMDI, (NPCX_HIKMST & 0x08) ? 1 : 0);
+#endif
+}
+DECLARE_IRQ(NPCX_IRQ_KBC_IBF, lpc_kbc_ibf_interrupt, 2);
+
+void lpc_kbc_obf_interrupt(void){
+ /* reserve for future handle */
+ if (!IS_BIT_SET(NPCX_HICTRL, 0)) {
+ SET_BIT(NPCX_HICTRL, 0); /* back to H/W control of IRQ1 */
+ CLEAR_BIT(NPCX_HIIRQC, 0); /* back to default of IRQB1 */
+ }
+ task_disable_irq(NPCX_IRQ_KBC_OBF);
+}
+DECLARE_IRQ(NPCX_IRQ_KBC_OBF, lpc_kbc_obf_interrupt, 2);
+
+void lpc_pmc_ibf_interrupt(void){
+ /* Channel-1 for ACPI usage*/
+ /* Channel-2 for Host Command usage , so the argument data had been
+ * put on the share memory firstly*/
+ if (NPCX_HIPMST(PM_CHAN_1) & 0x02)
+ handle_acpi_write((NPCX_HIPMST(PM_CHAN_1)&0x08) ? 1 : 0);
+ else if (NPCX_HIPMST(PM_CHAN_2)&0x02)
+ handle_host_write((NPCX_HIPMST(PM_CHAN_2)&0x08) ? 1 : 0);
+}
+DECLARE_IRQ(NPCX_IRQ_PM_CHAN_IBF, lpc_pmc_ibf_interrupt, 2);
+
+void lpc_pmc_obf_interrupt(void){
+}
+DECLARE_IRQ(NPCX_IRQ_PM_CHAN_OBF, lpc_pmc_obf_interrupt, 2);
+
+void lpc_port80_interrupt(void){
+ port_80_write((NPCX_GLUE_SDPD0<<0) | (NPCX_GLUE_SDPD1<<8));
+ /* No matter what , just clear error status bit */
+ SET_BIT(NPCX_DP80STS, 7);
+ SET_BIT(NPCX_DP80STS, 5);
+}
+DECLARE_IRQ(NPCX_IRQ_PORT80, lpc_port80_interrupt, 2);
+
+/**
+ * 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)
+{
+ /* Enable clock for LPC peripheral */
+ clock_enable_peripheral(CGC_OFFSET_LPC, CGC_LPC_MASK,
+ CGC_MODE_RUN | CGC_MODE_SLEEP);
+ /* Switching to LPC interface */
+ NPCX_DEVCNT |= 0x04;
+ /* Enable 4E/4F */
+ if (!IS_BIT_SET(NPCX_MSWCTL1, 3)) {
+ NPCX_HCBAL = 0x4E;
+ NPCX_HCBAH = 0x0;
+ }
+ /* Clear Host Access Hold state */
+ NPCX_SMC_CTL = 0xC0;
+
+ /* Initialize Hardware for UART Host */
+#if CONFIG_UART_HOST
+ /* Init COMx LPC UART */
+ /* FMCLK have to using 50MHz */
+ NPCX_DEVALT(0xB) = 0xFF;
+ /* Make sure Host Access unlock */
+ CLEAR_BIT(NPCX_LKSIOHA, 2);
+ /* Clear Host Access Lock Violation */
+ SET_BIT(NPCX_SIOLV, 2);
+#endif
+
+ /* Don't stall SHM transactions */
+ NPCX_SHM_CTL = NPCX_SHM_CTL & ~0x40;
+ /* Semaphore and Indirect access disable */
+ NPCX_SHCFG = 0xE0;
+ /* Disable Protect Win1&2*/
+ NPCX_WIN_WR_PROT(0) = 0;
+ NPCX_WIN_WR_PROT(1) = 0;
+ NPCX_WIN_RD_PROT(0) = 0;
+ NPCX_WIN_RD_PROT(1) = 0;
+ /* Open Win1 256 byte for Host CMD, Win2 256 for MEMMAP*/
+ NPCX_WIN_SIZE = 0x88;
+ NPCX_WIN_BASE(0) = (uint32_t)shm_mem_host_cmd;
+ NPCX_WIN_BASE(1) = (uint32_t)shm_memmap;
+
+ /* Turn on PMC2 for Host Command usage */
+ SET_BIT(NPCX_HIPMCTL(PM_CHAN_2), 0);
+ SET_BIT(NPCX_HIPMCTL(PM_CHAN_2), 1);
+ /* enable PMC2 IRQ */
+ SET_BIT(NPCX_HIPMIE(PM_CHAN_2), 0);
+ /* IRQ control from HW */
+ SET_BIT(NPCX_HIPMIE(PM_CHAN_2), 3);
+ /*
+ * Set required control value (avoid setting HOSTWAIT bit at this stage)
+ */
+ NPCX_SMC_CTL = NPCX_SMC_CTL&~0x7F;
+ /* Clear status */
+ NPCX_SMC_STS = NPCX_SMC_STS;
+ /* Create mailbox */
+
+ /*
+ * Init KBC
+ * Clear OBF status, PM1 IBF/OBF INT enable, IRQ11 enable,
+ * IBF(K&M) INT enable, OBF(K&M) empty INT enable ,
+ * OBF Mouse Full INT enable and OBF KB Full INT enable
+ */
+ NPCX_HICTRL = 0xFF;
+ /* Normally Polarity IRQ1,12,11 type (level + high) setting */
+ NPCX_HIIRQC = 0x00; /* Make sure to default */
+
+ /*
+ * Init PORT80
+ * Enable Port80, Enable Port80 function & Interrupt & Read auto
+ */
+ NPCX_DP80CTL = 0x29;
+ SET_BIT(NPCX_GLUE_SDP_CTS, 3);
+ SET_BIT(NPCX_GLUE_SDP_CTS, 0);
+ /* Just turn on IRQE */
+ NPCX_HIPMIE(PM_CHAN_1) = 0x01;
+ lpc_task_enable_irq();
+
+ /* Initialize host args and memory map to all zero */
+ memset(lpc_host_args, 0, sizeof(*lpc_host_args));
+ memset(lpc_get_memmap_range(), 0, EC_MEMMAP_SIZE);
+
+ /* 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;
+
+
+
+ /* 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();
+
+ /* initial IO port address via SIB-write modules */
+ system_lpc_host_register_init();
+}
+/*
+ * 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);
+
+static void lpc_resume(void)
+{
+ /* 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 << 2) | (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));