x86: apl: Add LPC driver
authorSimon Glass <sjg@chromium.org>
Mon, 9 Dec 2019 00:40:09 +0000 (17:40 -0700)
committerBin Meng <bmeng.cn@gmail.com>
Sun, 15 Dec 2019 03:44:28 +0000 (11:44 +0800)
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>
arch/x86/cpu/apollolake/Makefile
arch/x86/cpu/apollolake/lpc.c [new file with mode: 0644]
arch/x86/include/asm/arch-apollolake/lpc.h [new file with mode: 0644]

index 2d78368150258167811b6b99d09a76066fda0a24..31045a03c1b004413e874af0aa1e7c79b1f56dba 100644 (file)
@@ -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 (file)
index 0000000..45b2144
--- /dev/null
@@ -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), &reg,
+                                           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 (file)
index 0000000..5d2adad
--- /dev/null
@@ -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