]>
Commit | Line | Data |
---|---|---|
8bd5dcd8 SG |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright 2019 Google LLC | |
4 | * | |
5 | * From coreboot Apollo Lake support lpc.c | |
6 | */ | |
7 | ||
d678a59d | 8 | #include <common.h> |
8bd5dcd8 | 9 | #include <dm.h> |
f7ae49fc | 10 | #include <log.h> |
8bd5dcd8 | 11 | #include <spl.h> |
ea78675b SG |
12 | #include <acpi/acpi_table.h> |
13 | #include <asm/cpu_common.h> | |
14 | #include <asm/intel_acpi.h> | |
8bd5dcd8 SG |
15 | #include <asm/lpc_common.h> |
16 | #include <asm/pci.h> | |
17 | #include <asm/arch/iomap.h> | |
18 | #include <asm/arch/lpc.h> | |
ea78675b | 19 | #include <dm/acpi.h> |
8bd5dcd8 SG |
20 | #include <linux/log2.h> |
21 | ||
22 | void 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 | */ | |
32 | static 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 | ||
48 | int 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) { | |
8b842be1 SG |
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 | } | |
8bd5dcd8 SG |
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 | ||
106 | void 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 | ||
ea78675b SG |
118 | static int apl_acpi_lpc_get_name(const struct udevice *dev, char *out_name) |
119 | { | |
120 | return acpi_copy_name(out_name, "LPCB"); | |
121 | } | |
122 | ||
123 | struct 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 | ||
95397385 | 131 | #if CONFIG_IS_ENABLED(OF_REAL) |
8bd5dcd8 SG |
132 | static const struct udevice_id apl_lpc_ids[] = { |
133 | { .compatible = "intel,apl-lpc" }, | |
134 | { } | |
135 | }; | |
8b842be1 | 136 | #endif |
8bd5dcd8 SG |
137 | |
138 | /* All pads are LPC already configured by the hostbridge, so no probing here */ | |
9d20db04 | 139 | U_BOOT_DRIVER(intel_apl_lpc) = { |
8bd5dcd8 SG |
140 | .name = "intel_apl_lpc", |
141 | .id = UCLASS_LPC, | |
8b842be1 | 142 | .of_match = of_match_ptr(apl_lpc_ids), |
ea78675b | 143 | ACPI_OPS_PTR(&apl_lpc_acpi_ops) |
8bd5dcd8 | 144 | }; |