[U-Boot,v3,098/108] x86: apollolake: Add LPC driver
diff mbox series

Message ID 20191021033913.220758-93-sjg@chromium.org
State New
Delegated to: Bin Meng
Headers show
Series
  • x86: Add initial support for apollolake
Related show

Commit Message

Simon Glass Oct. 21, 2019, 3:39 a.m. UTC
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>
---

Changes in v3:
- Drop unused code in lpc_configure_pads()
- Fix value of LPC_BC_LE

Changes in v2: None

 arch/x86/cpu/apollolake/Makefile           |   1 +
 arch/x86/cpu/apollolake/lpc.c              | 141 +++++++++++++++++++++
 arch/x86/include/asm/arch-apollolake/lpc.h |  61 +++++++++
 3 files changed, 203 insertions(+)
 create mode 100644 arch/x86/cpu/apollolake/lpc.c
 create mode 100644 arch/x86/include/asm/arch-apollolake/lpc.h

Patch
diff mbox series

diff --git a/arch/x86/cpu/apollolake/Makefile b/arch/x86/cpu/apollolake/Makefile
index edd7e49154b..71c3cd08241 100644
--- a/arch/x86/cpu/apollolake/Makefile
+++ b/arch/x86/cpu/apollolake/Makefile
@@ -7,6 +7,7 @@  obj-$(CONFIG_SPL_BUILD) += systemagent.o
 obj-y += gpio.o
 obj-y += hostbridge.o
 obj-y += itss.o
+obj-y += lpc.o
 obj-y += lpss.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 00000000000..1426b182542
--- /dev/null
+++ b/arch/x86/cpu/apollolake/lpc.c
@@ -0,0 +1,141 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2019 Google LLC
+ *
+ * From coreboot apollolake 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_configure_pads(void)
+{
+	/* All pads are configured by the hostbridge */
+}
+
+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;
+
+	/* ComB Range 2F8h-2FFh [6:4] */
+	if (0) {
+		com_ranges |= LPC_IOD_COMB_RANGE;
+		com_enable |= LPC_IOE_COMB_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 int apl_lpc_probe(struct udevice *dev)
+{
+	if (spl_phase() == PHASE_TPL)
+		lpc_configure_pads();
+
+	return 0;
+}
+
+static const struct udevice_id apl_lpc_ids[] = {
+	{ .compatible = "intel,apl-lpc" },
+	{ }
+};
+
+U_BOOT_DRIVER(apl_lpc_drv) = {
+	.name		= "intel_apl_lpc",
+	.id		= UCLASS_LPC,
+	.of_match	= apl_lpc_ids,
+	.probe		= apl_lpc_probe,
+};
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 00000000000..641ccc01ca5
--- /dev/null
+++ b/arch/x86/include/asm/arch-apollolake/lpc.h
@@ -0,0 +1,61 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2017 Intel Corporation.
+ * Take from coreboot project file of the same name
+ */
+
+#ifndef __ARCH_LPC_H
+#define __ARCH_LPC_H
+
+#define LPC_SERIRQ_CTL			0x64
+#define  LPC_SCNT_EN			(1 << 7)
+#define  LPC_SCNT_MODE			(1 << 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			(1 << 0)
+#define LPC_LGIR_MAX_WINDOW_SIZE	256
+#define LPC_GENERIC_MEM_RANGE		0x98
+#define  LPC_LGMR_ADDR_MASK		0xffff0000
+#define  LPC_LGMR_EN			(1 << 0)
+#define LPC_LGMR_WINDOW_SIZE		(64 * KiB)
+#define LPC_BIOS_CNTL			0xdc
+#define  LPC_BC_BILD			(1 << 7) /* BILD */
+#define  LPC_BC_LE			(1 << 1) /* LE */
+#define  LPC_BC_EISS			(1 << 5) /* EISS */
+#define LPC_PCCTL			0xE0 /* PCI Clock Control */
+#define  LPC_PCCTL_CLKRUN_EN		(1 << 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
+
+void lpc_enable_fixed_io_ranges(uint io_enables);
+int lpc_open_pmio_window(uint base, uint size);
+void lpc_io_setup_comm_a_b(void);
+
+#endif