]>
Commit | Line | Data |
---|---|---|
65dd74a6 SG |
1 | /* |
2 | * From Coreboot src/southbridge/intel/bd82x6x/early_me.c | |
3 | * | |
4 | * Copyright (C) 2011 The Chromium OS Authors. All rights reserved. | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0 | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
c02a4242 | 10 | #include <dm.h> |
65dd74a6 SG |
11 | #include <errno.h> |
12 | #include <asm/pci.h> | |
98655f3a | 13 | #include <asm/cpu.h> |
65dd74a6 SG |
14 | #include <asm/processor.h> |
15 | #include <asm/arch/me.h> | |
16 | #include <asm/arch/pch.h> | |
17 | #include <asm/io.h> | |
18 | ||
19 | static const char *const me_ack_values[] = { | |
20 | [ME_HFS_ACK_NO_DID] = "No DID Ack received", | |
21 | [ME_HFS_ACK_RESET] = "Non-power cycle reset", | |
22 | [ME_HFS_ACK_PWR_CYCLE] = "Power cycle reset", | |
23 | [ME_HFS_ACK_S3] = "Go to S3", | |
24 | [ME_HFS_ACK_S4] = "Go to S4", | |
25 | [ME_HFS_ACK_S5] = "Go to S5", | |
26 | [ME_HFS_ACK_GBL_RESET] = "Global Reset", | |
27 | [ME_HFS_ACK_CONTINUE] = "Continue to boot" | |
28 | }; | |
29 | ||
c02a4242 SG |
30 | static inline void pci_read_dword_ptr(struct udevice *me_dev, void *ptr, |
31 | int offset) | |
65dd74a6 SG |
32 | { |
33 | u32 dword; | |
34 | ||
c02a4242 | 35 | dm_pci_read_config32(me_dev, offset, &dword); |
65dd74a6 SG |
36 | memcpy(ptr, &dword, sizeof(dword)); |
37 | } | |
38 | ||
c02a4242 SG |
39 | static inline void pci_write_dword_ptr(struct udevice *me_dev, void *ptr, |
40 | int offset) | |
65dd74a6 SG |
41 | { |
42 | u32 dword = 0; | |
c02a4242 | 43 | |
65dd74a6 | 44 | memcpy(&dword, ptr, sizeof(dword)); |
c02a4242 | 45 | dm_pci_write_config32(me_dev, offset, dword); |
65dd74a6 SG |
46 | } |
47 | ||
c02a4242 | 48 | void intel_early_me_status(struct udevice *me_dev) |
65dd74a6 SG |
49 | { |
50 | struct me_hfs hfs; | |
51 | struct me_gmes gmes; | |
52 | ||
c02a4242 SG |
53 | pci_read_dword_ptr(me_dev, &hfs, PCI_ME_HFS); |
54 | pci_read_dword_ptr(me_dev, &gmes, PCI_ME_GMES); | |
65dd74a6 SG |
55 | |
56 | intel_me_status(&hfs, &gmes); | |
57 | } | |
58 | ||
c02a4242 | 59 | int intel_early_me_init(struct udevice *me_dev) |
65dd74a6 SG |
60 | { |
61 | int count; | |
62 | struct me_uma uma; | |
63 | struct me_hfs hfs; | |
64 | ||
65 | debug("Intel ME early init\n"); | |
66 | ||
67 | /* Wait for ME UMA SIZE VALID bit to be set */ | |
68 | for (count = ME_RETRY; count > 0; --count) { | |
c02a4242 | 69 | pci_read_dword_ptr(me_dev, &uma, PCI_ME_UMA); |
65dd74a6 SG |
70 | if (uma.valid) |
71 | break; | |
72 | udelay(ME_DELAY); | |
73 | } | |
74 | if (!count) { | |
75 | printf("ERROR: ME is not ready!\n"); | |
76 | return -EBUSY; | |
77 | } | |
78 | ||
79 | /* Check for valid firmware */ | |
c02a4242 | 80 | pci_read_dword_ptr(me_dev, &hfs, PCI_ME_HFS); |
65dd74a6 SG |
81 | if (hfs.fpt_bad) { |
82 | printf("WARNING: ME has bad firmware\n"); | |
83 | return -EBADF; | |
84 | } | |
85 | ||
86 | debug("Intel ME firmware is ready\n"); | |
87 | ||
88 | return 0; | |
89 | } | |
90 | ||
c02a4242 | 91 | int intel_early_me_uma_size(struct udevice *me_dev) |
65dd74a6 SG |
92 | { |
93 | struct me_uma uma; | |
94 | ||
c02a4242 | 95 | pci_read_dword_ptr(me_dev, &uma, PCI_ME_UMA); |
65dd74a6 SG |
96 | if (uma.valid) { |
97 | debug("ME: Requested %uMB UMA\n", uma.size); | |
98 | return uma.size; | |
99 | } | |
100 | ||
101 | debug("ME: Invalid UMA size\n"); | |
102 | return -EINVAL; | |
103 | } | |
104 | ||
c02a4242 | 105 | static inline void set_global_reset(struct udevice *dev, int enable) |
65dd74a6 SG |
106 | { |
107 | u32 etr3; | |
108 | ||
c02a4242 | 109 | dm_pci_read_config32(dev, ETR3, &etr3); |
65dd74a6 SG |
110 | |
111 | /* Clear CF9 Without Resume Well Reset Enable */ | |
112 | etr3 &= ~ETR3_CWORWRE; | |
113 | ||
114 | /* CF9GR indicates a Global Reset */ | |
115 | if (enable) | |
116 | etr3 |= ETR3_CF9GR; | |
117 | else | |
118 | etr3 &= ~ETR3_CF9GR; | |
119 | ||
c02a4242 | 120 | dm_pci_write_config32(dev, ETR3, etr3); |
65dd74a6 SG |
121 | } |
122 | ||
c02a4242 SG |
123 | int intel_early_me_init_done(struct udevice *dev, struct udevice *me_dev, |
124 | uint status) | |
65dd74a6 | 125 | { |
65dd74a6 SG |
126 | int count; |
127 | u32 mebase_l, mebase_h; | |
128 | struct me_hfs hfs; | |
129 | struct me_did did = { | |
130 | .init_done = ME_INIT_DONE, | |
131 | .status = status | |
132 | }; | |
133 | ||
134 | /* MEBASE from MESEG_BASE[35:20] */ | |
c02a4242 SG |
135 | dm_pci_read_config32(PCH_DEV, PCI_CPU_MEBASE_L, &mebase_l); |
136 | dm_pci_read_config32(PCH_DEV, PCI_CPU_MEBASE_H, &mebase_h); | |
65dd74a6 SG |
137 | mebase_h &= 0xf; |
138 | did.uma_base = (mebase_l >> 20) | (mebase_h << 12); | |
139 | ||
140 | /* Send message to ME */ | |
141 | debug("ME: Sending Init Done with status: %d, UMA base: 0x%04x\n", | |
142 | status, did.uma_base); | |
143 | ||
c02a4242 | 144 | pci_write_dword_ptr(me_dev, &did, PCI_ME_H_GS); |
65dd74a6 SG |
145 | |
146 | /* Must wait for ME acknowledgement */ | |
147 | for (count = ME_RETRY; count > 0; --count) { | |
c02a4242 | 148 | pci_read_dword_ptr(me_dev, &hfs, PCI_ME_HFS); |
65dd74a6 SG |
149 | if (hfs.bios_msg_ack) |
150 | break; | |
151 | udelay(ME_DELAY); | |
152 | } | |
153 | if (!count) { | |
154 | printf("ERROR: ME failed to respond\n"); | |
c02a4242 | 155 | return -ETIMEDOUT; |
65dd74a6 SG |
156 | } |
157 | ||
158 | /* Return the requested BIOS action */ | |
159 | debug("ME: Requested BIOS Action: %s\n", me_ack_values[hfs.ack_data]); | |
160 | ||
161 | /* Check status after acknowledgement */ | |
c02a4242 | 162 | intel_early_me_status(me_dev); |
65dd74a6 | 163 | |
65dd74a6 SG |
164 | switch (hfs.ack_data) { |
165 | case ME_HFS_ACK_CONTINUE: | |
166 | /* Continue to boot */ | |
167 | return 0; | |
168 | case ME_HFS_ACK_RESET: | |
169 | /* Non-power cycle reset */ | |
c02a4242 | 170 | set_global_reset(dev, 0); |
5021c81f | 171 | reset_cpu(0); |
65dd74a6 SG |
172 | break; |
173 | case ME_HFS_ACK_PWR_CYCLE: | |
174 | /* Power cycle reset */ | |
c02a4242 | 175 | set_global_reset(dev, 0); |
5021c81f | 176 | x86_full_reset(); |
65dd74a6 SG |
177 | break; |
178 | case ME_HFS_ACK_GBL_RESET: | |
179 | /* Global reset */ | |
c02a4242 | 180 | set_global_reset(dev, 1); |
5021c81f | 181 | x86_full_reset(); |
65dd74a6 SG |
182 | break; |
183 | case ME_HFS_ACK_S3: | |
184 | case ME_HFS_ACK_S4: | |
185 | case ME_HFS_ACK_S5: | |
186 | break; | |
187 | } | |
188 | ||
c02a4242 | 189 | return -EINVAL; |
65dd74a6 | 190 | } |
c02a4242 SG |
191 | |
192 | static const struct udevice_id ivybridge_syscon_ids[] = { | |
98655f3a | 193 | { .compatible = "intel,me", .data = X86_SYSCON_ME }, |
c02a4242 SG |
194 | { } |
195 | }; | |
196 | ||
197 | U_BOOT_DRIVER(syscon_intel_me) = { | |
198 | .name = "intel_me_syscon", | |
199 | .id = UCLASS_SYSCON, | |
200 | .of_match = ivybridge_syscon_ids, | |
201 | }; |