2019-12-09 00:40:09 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
/*
|
|
|
|
* Copyright 2019 Google LLC
|
|
|
|
*
|
|
|
|
* From coreboot Apollo Lake support lpc.c
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
|
|
|
#include <dm.h>
|
2020-05-10 17:40:05 +00:00
|
|
|
#include <log.h>
|
2019-12-09 00:40:09 +00:00
|
|
|
#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,
|
|
|
|
};
|