diff options
author | Simon Glass <sjg@chromium.org> | 2019-12-08 17:40:09 -0700 |
---|---|---|
committer | Bin Meng <bmeng.cn@gmail.com> | 2019-12-15 11:44:28 +0800 |
commit | 8bd5dcd89581018f163e3424c8d2a3268ca79544 (patch) | |
tree | ecfe8005c583cfc3c93ddef0b4b09cf1dbfdb6ea /arch/x86 | |
parent | 3a02f699fa82d1c79654b1f0f48a7e296669d65d (diff) |
x86: apl: Add LPC driver
This driver the LPC and provides a few functions to set up LPC features.
These should probably use ioctls() or perhaps, better, have specific
uclass methods.
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/cpu/apollolake/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/cpu/apollolake/lpc.c | 122 | ||||
-rw-r--r-- | arch/x86/include/asm/arch-apollolake/lpc.h | 82 |
3 files changed, 205 insertions, 0 deletions
diff --git a/arch/x86/cpu/apollolake/Makefile b/arch/x86/cpu/apollolake/Makefile index 2d78368150..31045a03c1 100644 --- a/arch/x86/cpu/apollolake/Makefile +++ b/arch/x86/cpu/apollolake/Makefile @@ -6,5 +6,6 @@ obj-$(CONFIG_SPL_BUILD) += systemagent.o obj-y += hostbridge.o obj-y += itss.o +obj-y += lpc.o obj-y += pmc.o obj-y += uart.o diff --git a/arch/x86/cpu/apollolake/lpc.c b/arch/x86/cpu/apollolake/lpc.c new file mode 100644 index 0000000000..45b2144fc6 --- /dev/null +++ b/arch/x86/cpu/apollolake/lpc.c @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 Google LLC + * + * From coreboot Apollo Lake support lpc.c + */ + +#include <common.h> +#include <dm.h> +#include <spl.h> +#include <asm/lpc_common.h> +#include <asm/pci.h> +#include <asm/arch/iomap.h> +#include <asm/arch/lpc.h> +#include <linux/log2.h> + +void lpc_enable_fixed_io_ranges(uint io_enables) +{ + pci_x86_clrset_config(PCH_DEV_LPC, LPC_IO_ENABLES, 0, io_enables, + PCI_SIZE_16); +} + +/* + * Find the first unused IO window. + * Returns -1 if not found, 0 for reg 0x84, 1 for reg 0x88 ... + */ +static int find_unused_pmio_window(void) +{ + int i; + ulong lgir; + + for (i = 0; i < LPC_NUM_GENERIC_IO_RANGES; i++) { + pci_x86_read_config(PCH_DEV_LPC, LPC_GENERIC_IO_RANGE(i), + &lgir, PCI_SIZE_32); + + if (!(lgir & LPC_LGIR_EN)) + return i; + } + + return -1; +} + +int lpc_open_pmio_window(uint base, uint size) +{ + int i, lgir_reg_num; + u32 lgir_reg_offset, lgir, window_size, alignment; + ulong bridged_size, bridge_base; + ulong reg; + + log_debug("LPC: Trying to open IO window from %x size %x\n", base, + size); + + bridged_size = 0; + bridge_base = base; + + while (bridged_size < size) { + /* Each IO range register can only open a 256-byte window */ + window_size = min(size, (uint)LPC_LGIR_MAX_WINDOW_SIZE); + + /* Window size must be a power of two for the AMASK to work */ + alignment = 1UL << (order_base_2(window_size)); + window_size = ALIGN(window_size, alignment); + + /* Address[15:2] in LGIR[15:12] and Mask[7:2] in LGIR[23:18] */ + lgir = (bridge_base & LPC_LGIR_ADDR_MASK) | LPC_LGIR_EN; + lgir |= ((window_size - 1) << 16) & LPC_LGIR_AMASK_MASK; + + /* Skip programming if same range already programmed */ + for (i = 0; i < LPC_NUM_GENERIC_IO_RANGES; i++) { + pci_x86_read_config(PCH_DEV_LPC, + LPC_GENERIC_IO_RANGE(i), ®, + PCI_SIZE_32); + if (lgir == reg) + return -EALREADY; + } + + lgir_reg_num = find_unused_pmio_window(); + if (lgir_reg_num < 0) { + log_err("LPC: Cannot open IO window: %lx size %lx\n", + bridge_base, size - bridged_size); + log_err("No more IO windows\n"); + + return -ENOSPC; + } + lgir_reg_offset = LPC_GENERIC_IO_RANGE(lgir_reg_num); + + pci_x86_write_config(PCH_DEV_LPC, lgir_reg_offset, lgir, + PCI_SIZE_32); + + log_debug("LPC: Opened IO window LGIR%d: base %lx size %x\n", + lgir_reg_num, bridge_base, window_size); + + bridged_size += window_size; + bridge_base += window_size; + } + + return 0; +} + +void lpc_io_setup_comm_a_b(void) +{ + /* ComA Range 3F8h-3FFh [2:0] */ + u16 com_ranges = LPC_IOD_COMA_RANGE; + u16 com_enable = LPC_IOE_COMA_EN; + + /* Setup I/O Decode Range Register for LPC */ + pci_write_config16(PCH_DEV_LPC, LPC_IO_DECODE, com_ranges); + /* Enable ComA and ComB Port */ + lpc_enable_fixed_io_ranges(com_enable); +} + +static const struct udevice_id apl_lpc_ids[] = { + { .compatible = "intel,apl-lpc" }, + { } +}; + +/* All pads are LPC already configured by the hostbridge, so no probing here */ +U_BOOT_DRIVER(apl_lpc_drv) = { + .name = "intel_apl_lpc", + .id = UCLASS_LPC, + .of_match = apl_lpc_ids, +}; diff --git a/arch/x86/include/asm/arch-apollolake/lpc.h b/arch/x86/include/asm/arch-apollolake/lpc.h new file mode 100644 index 0000000000..5d2adad319 --- /dev/null +++ b/arch/x86/include/asm/arch-apollolake/lpc.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2017 Intel Corporation. + * Take from coreboot project file of the same name + */ + +#ifndef _ASM_ARCH_LPC_H +#define _ASM_ARCH_LPC_H + +#define LPC_SERIRQ_CTL 0x64 +#define LPC_SCNT_EN BIT(7) +#define LPC_SCNT_MODE BIT(6) +#define LPC_IO_DECODE 0x80 +#define LPC_IOD_COMA_RANGE (0 << 0) /* 0x3F8 - 0x3FF COMA*/ +#define LPC_IOD_COMB_RANGE (1 << 4) /* 0x2F8 - 0x2FF COMB*/ +/* + * Use IO_<peripheral>_<IO port> style macros defined in lpc_lib.h + * to enable decoding of I/O locations for a peripheral + */ +#define LPC_IO_ENABLES 0x82 +#define LPC_GENERIC_IO_RANGE(n) ((((n) & 0x3) * 4) + 0x84) +#define LPC_LGIR_AMASK_MASK (0xfc << 16) +#define LPC_LGIR_ADDR_MASK 0xfffc +#define LPC_LGIR_EN BIT(0) +#define LPC_LGIR_MAX_WINDOW_SIZE 256 +#define LPC_GENERIC_MEM_RANGE 0x98 +#define LPC_LGMR_ADDR_MASK 0xffff0000 +#define LPC_LGMR_EN BIT(0) +#define LPC_LGMR_WINDOW_SIZE (64 * KiB) +#define LPC_BIOS_CNTL 0xdc +#define LPC_BC_BILD BIT(7) +#define LPC_BC_LE BIT(1) +#define LPC_BC_EISS BIT(5) +#define LPC_PCCTL 0xE0 /* PCI Clock Control */ +#define LPC_PCCTL_CLKRUN_EN BIT(0) + +/* + * IO decode enable macros are in the format IO_<peripheral>_<IO port>. + * For example, to open ports 0x60, 0x64 for the keyboard controller, + * use IOE_KBC_60_64 macro. For IOE_ macros that do not specify a port range, + * the port range is selectable via the IO decodes register. + */ +#define LPC_IOE_EC_4E_4F BIT(13) +#define LPC_IOE_SUPERIO_2E_2F BIT(12) +#define LPC_IOE_EC_62_66 BIT(11) +#define LPC_IOE_KBC_60_64 BIT(10) +#define LPC_IOE_HGE_208 BIT(9) +#define LPC_IOE_LGE_200 BIT(8) +#define LPC_IOE_FDD_EN BIT(3) +#define LPC_IOE_LPT_EN BIT(2) +#define LPC_IOE_COMB_EN BIT(1) +#define LPC_IOE_COMA_EN BIT(0) +#define LPC_NUM_GENERIC_IO_RANGES 4 + +#define LPC_IO_ENABLES 0x82 + +/** + * lpc_enable_fixed_io_ranges() - enable the fixed I/O ranges + * + * @io_enables: Mask of things to enable (LPC_IOE_.) + */ +void lpc_enable_fixed_io_ranges(uint io_enables); + +/** + * lpc_open_pmio_window() - Open an IO port range + * + * @base: Base I/O address (e.g. 0x800) + * @size: Size of window (e.g. 0x100) + * @return 0 if OK, -ENOSPC if there are no more windows available, -EALREADY + * if already set up + */ +int lpc_open_pmio_window(uint base, uint size); + +/** + * lpc_io_setup_comm_a_b() - Set up basic serial UARTs + * + * Set up the LPC to handle I/O to the COMA/COMB serial UART addresses + * 2f8-2ff and 3f8-3ff. + */ +void lpc_io_setup_comm_a_b(void); + +#endif |