diff options
Diffstat (limited to 'chip/lm4/lpc.c')
-rw-r--r-- | chip/lm4/lpc.c | 835 |
1 files changed, 0 insertions, 835 deletions
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c deleted file mode 100644 index 6e3c39220d..0000000000 --- a/chip/lm4/lpc.c +++ /dev/null @@ -1,835 +0,0 @@ -/* Copyright 2013 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" - -/* LPC channels */ -#define LPC_CH_ACPI 0 /* ACPI commands */ -#define LPC_CH_PORT80 1 /* Port 80 debug output */ -#define LPC_CH_CMD_DATA 2 /* Data for host commands (args/params/response) */ -#define LPC_CH_KEYBOARD 3 /* 8042 keyboard emulation */ -#define LPC_CH_CMD 4 /* Host commands */ -#define LPC_CH_MEMMAP 5 /* Memory-mapped data */ -#define LPC_CH_COMX 7 /* UART emulation */ -/* LPC pool offsets */ -#define LPC_POOL_OFFS_ACPI 0 /* ACPI commands - 0=in, 1=out */ -#define LPC_POOL_OFFS_PORT80 4 /* Port 80 - 4=in, 5=out */ -#define LPC_POOL_OFFS_COMX 8 /* UART emulation range - 8-15 */ -#define LPC_POOL_OFFS_KEYBOARD 16 /* Keyboard - 16=in, 17=out */ -#define LPC_POOL_OFFS_CMD 20 /* Host commands - 20=in, 21=out */ -#define LPC_POOL_OFFS_CMD_DATA 512 /* Data range for host commands - 512-767 */ -#define LPC_POOL_OFFS_MEMMAP 768 /* Memory-mapped data - 768-1023 */ -/* LPC pool data pointers */ -#define LPC_POOL_ACPI (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_ACPI) -#define LPC_POOL_PORT80 (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_PORT80) -#define LPC_POOL_COMX (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_COMX) -#define LPC_POOL_KEYBOARD (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_KEYBOARD) -#define LPC_POOL_CMD (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_CMD) -#define LPC_POOL_CMD_DATA (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_CMD_DATA) -#define LPC_POOL_MEMMAP (LM4_LPC_LPCPOOL + LPC_POOL_OFFS_MEMMAP) -/* LPC COMx I/O address (in x86 I/O address space) */ -#define LPC_COMX_ADDR 0x3f8 /* COM1 */ - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_LPC, outstr) -#define CPRINTS(format, args...) cprints(CC_LPC, format, ## args) - -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 uint8_t * const cmd_params = (uint8_t *)LPC_POOL_CMD_DATA + - 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 *)LPC_POOL_CMD_DATA; - -static void wait_irq_sent(void) -{ - /* - * A hard-coded delay here isn't very elegant, but it's the best we can - * manage (and it's a short delay, so it's not that horrible). We need - * this because SIRQRIS isn't cleared in continuous mode, and the EC - * has trouble sending more than 1 frame in quiet mode. Waiting 4 us = - * 2 SERIRQ frames ensures the IRQ has been sent out. - */ - udelay(4); -} - -#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. - */ - uint64_t tstop = get_time().val + MSEC; - gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1); - udelay(4); - /* Generate a falling edge */ - gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 0); - /* Wait for host senses the interrupt and gets the char. */ - do { - if (get_time().val > tstop) - break; - } while (lpc_keyboard_has_char()); - /* Set signal high, now that we've generated the edge */ - gpio_set_level(CONFIG_KEYBOARD_IRQ_GPIO, 1); -} -#else -static void wait_send_serirq(uint32_t lpcirqctl) -{ - LM4_LPC_LPCIRQCTL = lpcirqctl; - wait_irq_sent(); -} - -/** - * Manually generate an IRQ to host (edge-trigger). - * - * @param irq_num IRQ number to generate. Pass 0 to set the AH - * (active high) bit. - * - * For SERIRQ quite mode, we need to set LM4_LPC_LPCIRQCTL twice. - * The first one is to assert IRQ (pull low), and then the second one is - * to de-assert it. This generates a pulse (high-low-high) for an IRQ. - */ -static void lpc_manual_irq(int irq_num) -{ - uint32_t common_bits = - 0x00000004 | /* PULSE */ - 0x00000002 | /* ONCHG - for quiet mode */ - 0x00000001; /* SND - send immediately */ - - /* Send out the IRQ first. */ - wait_send_serirq((1 << (irq_num + 16)) | common_bits); - - /* Generate a all-high frame to simulate a rising edge. */ - wait_send_serirq(common_bits); -} - -static inline void keyboard_irq_assert(void) -{ - /* Use serirq method. */ - lpc_manual_irq(1); /* IRQ#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) -{ - host_event_t smi; - - /* 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); - - smi = lpc_get_host_events_by_type(LPC_HOST_EVENT_SMI); - if (smi) - HOST_EVENT_CPRINTS("smi", smi); -} - -/** - * Generate SCI pulse to the host chipset via LPC0SCI. - */ -static void lpc_generate_sci(void) -{ - host_event_t sci; - -#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 - LM4_LPC_LPCCTL |= LM4_LPC_SCI_START; -#endif - - sci = lpc_get_host_events_by_type(LPC_HOST_EVENT_SCI); - if (sci) - HOST_EVENT_CPRINTS("sci", sci); -} - -/** - * Update the level-sensitive wake signal to the AP. - * - * @param wake_events Currently asserted wake events - */ -static void lpc_update_wake(uint64_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 *)LPC_POOL_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. */ - LPC_POOL_CMD[1] = args->result; - - /* Clear the busy bit, so the host knows the EC is done. */ - task_disable_irq(LM4_IRQ_LPC); - LM4_LPC_ST(LPC_CH_CMD) &= ~LM4_LPC_ST_BUSY; - task_enable_irq(LM4_IRQ_LPC); -} - -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. */ - LPC_POOL_CMD[1] = pkt->driver_result; - - /* Clear the busy bit, so the host knows the EC is done. */ - task_disable_irq(LM4_IRQ_LPC); - LM4_LPC_ST(LPC_CH_CMD) &= ~LM4_LPC_ST_BUSY; - task_enable_irq(LM4_IRQ_LPC); -} - -int lpc_keyboard_has_char(void) -{ - return (LM4_LPC_ST(LPC_CH_KEYBOARD) & LM4_LPC_ST_TOH) ? 1 : 0; -} - -/* Return true if the FRMH is set */ -int lpc_keyboard_input_pending(void) -{ - return (LM4_LPC_ST(LPC_CH_KEYBOARD) & LM4_LPC_ST_FRMH) ? 1 : 0; -} - -/* Put a char to host buffer and send IRQ if specified. */ -void lpc_keyboard_put_char(uint8_t chr, int send_irq) -{ - LPC_POOL_KEYBOARD[1] = chr; - if (send_irq) - keyboard_irq_assert(); -} - -void lpc_keyboard_clear_buffer(void) -{ - /* Make sure the previous TOH and IRQ has been sent out. */ - wait_irq_sent(); - - LM4_LPC_ST(LPC_CH_KEYBOARD) &= ~LM4_LPC_ST_TOH; - - /* Ensure there is no TOH set in this period. */ - wait_irq_sent(); -} - -void lpc_keyboard_resume_irq(void) -{ - if (lpc_keyboard_has_char()) - keyboard_irq_assert(); -} - -#ifdef CONFIG_UART_HOST - -int lpc_comx_has_char(void) -{ - return LM4_LPC_ST(LPC_CH_COMX) & LM4_LPC_ST_FRMH; -} - -int lpc_comx_get_char(void) -{ - return LPC_POOL_COMX[0]; -} - -void lpc_comx_put_char(int c) -{ - LPC_POOL_COMX[1] = c; - - /* - * We could in theory manually trigger an IRQ, like we do for the 8042 - * keyboard interface, but neither the kernel nor BIOS seems to require - * this. - */ -} - -#endif /* CONFIG_UART_HOST */ - -/** - * 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 - */ -void lpc_update_host_event_status(void) -{ - int need_sci = 0; - int need_smi = 0; - - if (!init_done) - return; - - /* Disable LPC interrupt while updating status register */ - task_disable_irq(LM4_IRQ_LPC); - - if (lpc_get_host_events_by_type(LPC_HOST_EVENT_SMI)) { - /* Only generate SMI for first event */ - if (!(LM4_LPC_ST(LPC_CH_ACPI) & LM4_LPC_ST_SMI)) - need_smi = 1; - LM4_LPC_ST(LPC_CH_ACPI) |= LM4_LPC_ST_SMI; - } else - LM4_LPC_ST(LPC_CH_ACPI) &= ~LM4_LPC_ST_SMI; - - if (lpc_get_host_events_by_type(LPC_HOST_EVENT_SCI)) { - /* Generate SCI for every event */ - need_sci = 1; - LM4_LPC_ST(LPC_CH_ACPI) |= LM4_LPC_ST_SCI; - } else - LM4_LPC_ST(LPC_CH_ACPI) &= ~LM4_LPC_ST_SCI; - - /* Copy host events to mapped memory */ - *(host_event_t *)host_get_memmap(EC_MEMMAP_HOST_EVENTS) = - lpc_get_host_events(); - - task_enable_irq(LM4_IRQ_LPC); - - /* Process the wake events. */ - lpc_update_wake(lpc_get_host_events_by_type(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_acpi_status_mask(uint8_t mask) -{ - uint32_t set_mask = 0; - if (mask & EC_LPC_STATUS_BURST_MODE) - set_mask |= LM4_LPC_ST_BURST; - - LM4_LPC_ST(LPC_CH_ACPI) |= set_mask; -} - -void lpc_clear_acpi_status_mask(uint8_t mask) -{ - uint32_t clear_mask = 0; - if (mask & EC_LPC_STATUS_BURST_MODE) - clear_mask |= LM4_LPC_ST_BURST; - - LM4_LPC_ST(LPC_CH_ACPI) &= ~clear_mask; -} - -int lpc_get_pltrst_asserted(void) -{ - return (LM4_LPC_LPCSTS & BIT(10)) ? 1 : 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; - - /* Set the busy bit */ - LM4_LPC_ST(LPC_CH_ACPI) |= LM4_LPC_ST_BUSY; - - /* Read command/data; this clears the FRMH status bit. */ - value = LPC_POOL_ACPI[0]; - - /* Handle whatever this was. */ - if (acpi_ap_to_ec(is_cmd, value, &result)) - LPC_POOL_ACPI[1] = result; - - /* Clear the busy bit */ - LM4_LPC_ST(LPC_CH_ACPI) &= ~LM4_LPC_ST_BUSY; - - /* - * 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) -{ - /* Ignore data writes or overlapping commands from host */ - uint32_t is_overlapping = LM4_LPC_ST(LPC_CH_CMD) & LM4_LPC_ST_BUSY; - if (!is_cmd || is_overlapping) { - if (is_overlapping) - CPRINTS("LPC Ignoring overlapping HC"); - LM4_LPC_ST(LPC_CH_CMD) &= ~LM4_LPC_ST_FRMH; - return; - } - - /* Set the busy bit */ - LM4_LPC_ST(LPC_CH_CMD) |= LM4_LPC_ST_BUSY; - - /* - * Read the command byte. This clears the FRMH bit in - * the status byte. - */ - host_cmd_args.command = LPC_POOL_CMD[0]; - - 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 *)LPC_POOL_CMD_DATA; - 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 *)LPC_POOL_CMD_DATA; - 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); - 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); -} - -#ifdef CONFIG_CHIPSET_RESET_HOOK -static void lpc_chipset_reset(void) -{ - hook_notify(HOOK_CHIPSET_RESET); -} -DECLARE_DEFERRED(lpc_chipset_reset); -#endif - -/** - * LPC interrupt handler - */ -void lpc_interrupt(void) -{ - uint32_t mis = LM4_LPC_LPCMIS; - uint32_t st; - - /* Clear the interrupt bits we're handling */ - LM4_LPC_LPCIC = mis; - -#ifdef HAS_TASK_HOSTCMD - /* Handle ACPI command and data writes */ - st = LM4_LPC_ST(LPC_CH_ACPI); - if (st & LM4_LPC_ST_FRMH) - handle_acpi_write(st & LM4_LPC_ST_CMD); - - /* Handle user command writes */ - st = LM4_LPC_ST(LPC_CH_CMD); - if (st & LM4_LPC_ST_FRMH) - handle_host_write(st & LM4_LPC_ST_CMD); -#endif - - /* - * Handle port 80 writes (CH0MIS1). Due to crosbug.com/p/12349 the - * interrupt status (mis & LM4_LPC_INT_MASK(LPC_CH_PORT80, 2)) - * apparently gets lost on back-to-back writes to port 80, so check the - * FRMH bit in the channel status register to see if a write is - * pending. Loop to handle bursts of back-to-back writes. - */ - while (LM4_LPC_ST(LPC_CH_PORT80) & LM4_LPC_ST_FRMH) - port_80_write(LPC_POOL_PORT80[0]); - -#ifdef HAS_TASK_KEYPROTO - /* Handle keyboard interface writes */ - st = LM4_LPC_ST(LPC_CH_KEYBOARD); - if (st & LM4_LPC_ST_FRMH) - keyboard_host_write(LPC_POOL_KEYBOARD[0], st & LM4_LPC_ST_CMD); - - if (mis & LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 1)) { - /* Host read data; wake up task to send remaining bytes */ - task_wake(TASK_ID_KEYPROTO); - } -#endif - -#ifdef CONFIG_UART_HOST - /* Handle COMx */ - if (lpc_comx_has_char()) { - /* Copy a character to the UART if there's space */ - if (uart_comx_putc_ok()) - uart_comx_putc(lpc_comx_get_char()); - } -#endif - - /* Debugging: print changes to LPC0RESET */ - if (mis & BIT(31)) { - if (LM4_LPC_LPCSTS & BIT(10)) { - int i; - - /* Store port 80 reset event */ - port_80_write(PORT_80_EVENT_RESET); - - /* - * Workaround for crosbug.com/p/12349; clear all FRMH - * bits so host writes will trigger interrupts. - */ - for (i = 0; i < 8; i++) - LM4_LPC_ST(i) &= ~LM4_LPC_ST_FRMH; - -#ifdef CONFIG_CHIPSET_RESET_HOOK - /* Notify HOOK_CHIPSET_RESET */ - hook_call_deferred(&lpc_chipset_reset_data, MSEC); -#endif - } - - CPRINTS("LPC RESET# %sasserted", - lpc_get_pltrst_asserted() ? "" : "de"); - } -} -DECLARE_IRQ(LM4_IRQ_LPC, lpc_interrupt, 2); - -/* Enable LPC ACPI-EC interrupts */ -void lpc_enable_acpi_interrupts(void) -{ - LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_ACPI, 6); -} - -/* Disable LPC ACPI-EC interrupts */ -void lpc_disable_acpi_interrupts(void) -{ - LM4_LPC_LPCIM &= ~(LM4_LPC_INT_MASK(LPC_CH_ACPI, 6)); -} - -static void lpc_init(void) -{ - /* Enable LPC clock in run and sleep modes. */ - clock_enable_peripheral(CGC_OFFSET_LPC, 0x1, - CGC_MODE_RUN | CGC_MODE_SLEEP); - - LM4_LPC_LPCIM = 0; - LM4_LPC_LPCCTL = 0; - LM4_LPC_LPCIRQCTL = 0; - - /* Configure GPIOs */ - gpio_config_module(MODULE_LPC, 1); - - /* - * Set LPC channel 0 to I/O address 0x62 (data) / 0x66 (command), - * single endpoint, offset 0 for host command/writes and 1 for EC - * data writes, pool bytes 0(data)/1(cmd) - */ - LM4_LPC_ADR(LPC_CH_ACPI) = EC_LPC_ADDR_ACPI_DATA; - LM4_LPC_CTL(LPC_CH_ACPI) = (LPC_POOL_OFFS_ACPI << (5 - 1)); - LM4_LPC_ST(LPC_CH_ACPI) = 0; - /* Unmask interrupt for host command and data writes */ - LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_ACPI, 6); - - /* - * Set LPC channel 1 to I/O address 0x80 (data), single endpoint, - * pool bytes 4(data)/5(cmd). - */ - LM4_LPC_ADR(LPC_CH_PORT80) = 0x80; - LM4_LPC_CTL(LPC_CH_PORT80) = (LPC_POOL_OFFS_PORT80 << (5 - 1)); - /* Unmask interrupt for host data writes */ - LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_PORT80, 2); - - /* - * Set LPC channel 2 to I/O address 0x880, range endpoint, - * arbitration disabled, pool bytes 512-639. To access this from - * x86, use the following command to set GEN_LPC2: - * - * pci_write32 0 0x1f 0 0x88 0x007c0801 - */ - LM4_LPC_ADR(LPC_CH_CMD_DATA) = EC_LPC_ADDR_HOST_ARGS; - LM4_LPC_CTL(LPC_CH_CMD_DATA) = 0x8019 | - (LPC_POOL_OFFS_CMD_DATA << (5 - 1)); - - /* - * Set LPC channel 3 to I/O address 0x60 (data) / 0x64 (command), - * single endpoint, offset 0 for host command/writes and 1 for EC - * data writes, pool bytes 0(data)/1(cmd) - */ - LM4_LPC_ADR(LPC_CH_KEYBOARD) = 0x60; - LM4_LPC_CTL(LPC_CH_KEYBOARD) = (BIT(24)/* IRQSEL1 */) | - (0 << 18/* IRQEN1 */) | (LPC_POOL_OFFS_KEYBOARD << (5 - 1)); - LM4_LPC_ST(LPC_CH_KEYBOARD) = 0; - /* Unmask interrupt for host command/data writes and data reads */ - LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 7); - - /* - * Set LPC channel 4 to I/O address 0x200 (data) / 0x204 (command), - * single endpoint, offset 0 for host command/writes and 1 for EC - * data writes, pool bytes 0(data)/1(cmd) - */ - LM4_LPC_ADR(LPC_CH_CMD) = EC_LPC_ADDR_HOST_DATA; - LM4_LPC_CTL(LPC_CH_CMD) = (LPC_POOL_OFFS_CMD << (5 - 1)); - /* - * Initialize status bits to 0. We never set the ACPI burst status bit, - * so this guarantees that at least one status bit will always be 0. - * This is used by comm_lpc.c to detect that the EC is present on the - * LPC bus. See crosbug.com/p/10963. - */ - LM4_LPC_ST(LPC_CH_CMD) = 0; - /* Unmask interrupt for host command writes */ - LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_CMD, 4); - - /* - * Set LPC channel 5 to I/O address 0x900, range endpoint, - * arbitration enabled, pool bytes 768-1023. To access this from - * x86, use the following command to set GEN_LPC3: - * - * pci_write32 0 0x1f 0 0x8c 0x007c0901 - */ - LM4_LPC_ADR(LPC_CH_MEMMAP) = EC_LPC_ADDR_MEMMAP; - LM4_LPC_CTL(LPC_CH_MEMMAP) = 0x0019 | (LPC_POOL_OFFS_MEMMAP << (5 - 1)); - -#ifdef CONFIG_UART_HOST - /* - * Set LPC channel 7 to COM port I/O address. Note that channel 7 - * ignores the TYPE bit and is always an 8-byte range. - */ - LM4_LPC_ADR(LPC_CH_COMX) = LPC_COMX_ADDR; - /* - * In theory we could configure IRQSELs and set IRQEN2/CX, and then the - * host could enable IRQs on its own. So far that hasn't been - * necessary, and due to the issues with IRQs (see wait_irq_sent() - * above) it might not work anyway. - */ - LM4_LPC_CTL(LPC_CH_COMX) = 0x0004 | (LPC_POOL_OFFS_COMX << (5 - 1)); - /* Enable COMx emulation for reads and writes. */ - LM4_LPC_LPCDMACX = 0x00310000; - /* - * Unmask interrupt for host data writes. We don't need interrupts for - * reads, because there's no flow control in that direction; LPC is - * much faster than the UART, and the UART doesn't have anywhere - * sensible to buffer input anyway. - */ - LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_COMX, 2); -#endif /* CONFIG_UART_HOST */ - - /* - * Unmask LPC bus reset interrupt. This lets us monitor the PCH - * PLTRST# signal for debugging. - */ - LM4_LPC_LPCIM |= BIT(31); - - /* Enable LPC channels */ - LM4_LPC_LPCCTL = LM4_LPC_SCI_CLK_1 | - BIT(LPC_CH_ACPI) | - BIT(LPC_CH_PORT80) | - BIT(LPC_CH_CMD_DATA) | - BIT(LPC_CH_KEYBOARD) | - BIT(LPC_CH_CMD) | - BIT(LPC_CH_MEMMAP); - -#ifdef CONFIG_UART_HOST - LM4_LPC_LPCCTL |= 1 << LPC_CH_COMX; -#endif - - /* - * Ensure the EC (peripheral) has control of the memory-mapped - * I/O space. Once the EC has won arbitration for the - * memory-mapped space, it will keep control of it until it - * writes the last byte in the space. (That never happens; we - * can't use the last byte in the space because ACPI can't see - * it anyway.) - */ - while (!(LM4_LPC_ST(LPC_CH_MEMMAP) & 0x10)) { - /* Clear HW1ST */ - LM4_LPC_ST(LPC_CH_MEMMAP) &= ~0x40; - /* Do a peripheral write; this should cause SW1ST to be set */ - *LPC_POOL_MEMMAP = *LPC_POOL_MEMMAP; - } - - /* 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; - - /* Enable LPC interrupt */ - task_enable_irq(LM4_IRQ_LPC); - -#ifdef CONFIG_UART_HOST - /* Enable COMx UART */ - uart_comx_enable(); -#endif - - /* Sufficiently initialized */ - init_done = 1; - - /* Update host events now that we can copy them to memmap */ - lpc_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); - -static void lpc_tick(void) -{ - /* - * Make sure pending LPC interrupts have been processed. - * This works around a LM4 bug where host writes sometimes - * don't trigger interrupts. See crosbug.com/p/13965. - */ - task_trigger_irq(LM4_IRQ_LPC); -} -DECLARE_HOOK(HOOK_TICK, lpc_tick, HOOK_PRIO_DEFAULT); - -/** - * Get protocol information - */ -static enum ec_status 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 = BIT(2) | BIT(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)); |