1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright 2019 Google LLC 4 * 5 * From coreboot Apollo Lake support lpc.c 6 */ 7 8#include <common.h> 9#include <dm.h> 10#include <log.h> 11#include <spl.h> 12#include <acpi/acpi_table.h> 13#include <asm/cpu_common.h> 14#include <asm/intel_acpi.h> 15#include <asm/lpc_common.h> 16#include <asm/pci.h> 17#include <asm/arch/iomap.h> 18#include <asm/arch/lpc.h> 19#include <dm/acpi.h> 20#include <linux/log2.h> 21 22void lpc_enable_fixed_io_ranges(uint io_enables) 23{ 24 pci_x86_clrset_config(PCH_DEV_LPC, LPC_IO_ENABLES, 0, io_enables, 25 PCI_SIZE_16); 26} 27 28/* 29 * Find the first unused IO window. 30 * Returns -1 if not found, 0 for reg 0x84, 1 for reg 0x88 ... 31 */ 32static int find_unused_pmio_window(void) 33{ 34 int i; 35 ulong lgir; 36 37 for (i = 0; i < LPC_NUM_GENERIC_IO_RANGES; i++) { 38 pci_x86_read_config(PCH_DEV_LPC, LPC_GENERIC_IO_RANGE(i), 39 &lgir, PCI_SIZE_32); 40 41 if (!(lgir & LPC_LGIR_EN)) 42 return i; 43 } 44 45 return -1; 46} 47 48int lpc_open_pmio_window(uint base, uint size) 49{ 50 int i, lgir_reg_num; 51 u32 lgir_reg_offset, lgir, window_size, alignment; 52 ulong bridged_size, bridge_base; 53 ulong reg; 54 55 log_debug("LPC: Trying to open IO window from %x size %x\n", base, 56 size); 57 58 bridged_size = 0; 59 bridge_base = base; 60 61 while (bridged_size < size) { 62 /* Each IO range register can only open a 256-byte window */ 63 window_size = min(size, (uint)LPC_LGIR_MAX_WINDOW_SIZE); 64 65 /* Window size must be a power of two for the AMASK to work */ 66 alignment = 1UL << (order_base_2(window_size)); 67 window_size = ALIGN(window_size, alignment); 68 69 /* Address[15:2] in LGIR[15:12] and Mask[7:2] in LGIR[23:18] */ 70 lgir = (bridge_base & LPC_LGIR_ADDR_MASK) | LPC_LGIR_EN; 71 lgir |= ((window_size - 1) << 16) & LPC_LGIR_AMASK_MASK; 72 73 /* Skip programming if same range already programmed */ 74 for (i = 0; i < LPC_NUM_GENERIC_IO_RANGES; i++) { 75 pci_x86_read_config(PCH_DEV_LPC, 76 LPC_GENERIC_IO_RANGE(i), ®, 77 PCI_SIZE_32); 78 if (lgir == reg) 79 return -EALREADY; 80 } 81 82 lgir_reg_num = find_unused_pmio_window(); 83 if (lgir_reg_num < 0) { 84 if (spl_phase() > PHASE_TPL) { 85 log_err("LPC: Cannot open IO window: %lx size %lx\n", 86 bridge_base, size - bridged_size); 87 log_err("No more IO windows\n"); 88 } 89 return -ENOSPC; 90 } 91 lgir_reg_offset = LPC_GENERIC_IO_RANGE(lgir_reg_num); 92 93 pci_x86_write_config(PCH_DEV_LPC, lgir_reg_offset, lgir, 94 PCI_SIZE_32); 95 96 log_debug("LPC: Opened IO window LGIR%d: base %lx size %x\n", 97 lgir_reg_num, bridge_base, window_size); 98 99 bridged_size += window_size; 100 bridge_base += window_size; 101 } 102 103 return 0; 104} 105 106void lpc_io_setup_comm_a_b(void) 107{ 108 /* ComA Range 3F8h-3FFh [2:0] */ 109 u16 com_ranges = LPC_IOD_COMA_RANGE; 110 u16 com_enable = LPC_IOE_COMA_EN; 111 112 /* Setup I/O Decode Range Register for LPC */ 113 pci_write_config16(PCH_DEV_LPC, LPC_IO_DECODE, com_ranges); 114 /* Enable ComA and ComB Port */ 115 lpc_enable_fixed_io_ranges(com_enable); 116} 117 118static int apl_acpi_lpc_get_name(const struct udevice *dev, char *out_name) 119{ 120 return acpi_copy_name(out_name, "LPCB"); 121} 122 123struct acpi_ops apl_lpc_acpi_ops = { 124 .get_name = apl_acpi_lpc_get_name, 125#ifdef CONFIG_GENERATE_ACPI_TABLE 126 .write_tables = intel_southbridge_write_acpi_tables, 127#endif 128 .inject_dsdt = southbridge_inject_dsdt, 129}; 130 131#if CONFIG_IS_ENABLED(OF_REAL) 132static const struct udevice_id apl_lpc_ids[] = { 133 { .compatible = "intel,apl-lpc" }, 134 { } 135}; 136#endif 137 138/* All pads are LPC already configured by the hostbridge, so no probing here */ 139U_BOOT_DRIVER(intel_apl_lpc) = { 140 .name = "intel_apl_lpc", 141 .id = UCLASS_LPC, 142 .of_match = of_match_ptr(apl_lpc_ids), 143 ACPI_OPS_PTR(&apl_lpc_acpi_ops) 144}; 145