]>
Commit | Line | Data |
---|---|---|
2b605154 SG |
1 | /* |
2 | * From coreboot southbridge/intel/bd82x6x/lpc.c | |
3 | * | |
4 | * Copyright (C) 2008-2009 coresystems GmbH | |
5 | * | |
6 | * SPDX-License-Identifier: GPL-2.0 | |
7 | */ | |
8 | ||
9 | #include <common.h> | |
aad78d27 | 10 | #include <dm.h> |
2b605154 SG |
11 | #include <errno.h> |
12 | #include <fdtdec.h> | |
72cd085a | 13 | #include <rtc.h> |
2b605154 | 14 | #include <pci.h> |
72cd085a SG |
15 | #include <asm/acpi.h> |
16 | #include <asm/interrupt.h> | |
17 | #include <asm/io.h> | |
18 | #include <asm/ioapic.h> | |
2b605154 SG |
19 | #include <asm/pci.h> |
20 | #include <asm/arch/pch.h> | |
21 | ||
72cd085a SG |
22 | #define NMI_OFF 0 |
23 | ||
24 | #define ENABLE_ACPI_MODE_IN_COREBOOT 0 | |
25 | #define TEST_SMM_FLASH_LOCKDOWN 0 | |
26 | ||
27 | static int pch_enable_apic(pci_dev_t dev) | |
28 | { | |
29 | u32 reg32; | |
30 | int i; | |
31 | ||
32 | /* Enable ACPI I/O and power management. Set SCI IRQ to IRQ9 */ | |
31f57c28 | 33 | x86_pci_write_config8(dev, ACPI_CNTL, 0x80); |
72cd085a SG |
34 | |
35 | writel(0, IO_APIC_INDEX); | |
36 | writel(1 << 25, IO_APIC_DATA); | |
37 | ||
38 | /* affirm full set of redirection table entries ("write once") */ | |
39 | writel(1, IO_APIC_INDEX); | |
40 | reg32 = readl(IO_APIC_DATA); | |
41 | writel(1, IO_APIC_INDEX); | |
42 | writel(reg32, IO_APIC_DATA); | |
43 | ||
44 | writel(0, IO_APIC_INDEX); | |
45 | reg32 = readl(IO_APIC_DATA); | |
46 | debug("PCH APIC ID = %x\n", (reg32 >> 24) & 0x0f); | |
47 | if (reg32 != (1 << 25)) { | |
48 | printf("APIC Error - cannot write to registers\n"); | |
49 | return -EPERM; | |
50 | } | |
51 | ||
52 | debug("Dumping IOAPIC registers\n"); | |
53 | for (i = 0; i < 3; i++) { | |
54 | writel(i, IO_APIC_INDEX); | |
55 | debug(" reg 0x%04x:", i); | |
56 | reg32 = readl(IO_APIC_DATA); | |
57 | debug(" 0x%08x\n", reg32); | |
58 | } | |
59 | ||
60 | /* Select Boot Configuration register. */ | |
61 | writel(3, IO_APIC_INDEX); | |
62 | ||
63 | /* Use Processor System Bus to deliver interrupts. */ | |
64 | writel(1, IO_APIC_DATA); | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
69 | static void pch_enable_serial_irqs(pci_dev_t dev) | |
70 | { | |
71 | u32 value; | |
72 | ||
73 | /* Set packet length and toggle silent mode bit for one frame. */ | |
74 | value = (1 << 7) | (1 << 6) | ((21 - 17) << 2) | (0 << 0); | |
75 | #ifdef CONFIG_SERIRQ_CONTINUOUS_MODE | |
31f57c28 | 76 | x86_pci_write_config8(dev, SERIRQ_CNTL, value); |
72cd085a | 77 | #else |
31f57c28 | 78 | x86_pci_write_config8(dev, SERIRQ_CNTL, value | (1 << 6)); |
72cd085a SG |
79 | #endif |
80 | } | |
81 | ||
82 | static int pch_pirq_init(const void *blob, int node, pci_dev_t dev) | |
83 | { | |
84 | uint8_t route[8], *ptr; | |
85 | ||
86 | if (fdtdec_get_byte_array(blob, node, "intel,pirq-routing", route, | |
87 | sizeof(route))) | |
88 | return -EINVAL; | |
89 | ptr = route; | |
31f57c28 SG |
90 | x86_pci_write_config8(dev, PIRQA_ROUT, *ptr++); |
91 | x86_pci_write_config8(dev, PIRQB_ROUT, *ptr++); | |
92 | x86_pci_write_config8(dev, PIRQC_ROUT, *ptr++); | |
93 | x86_pci_write_config8(dev, PIRQD_ROUT, *ptr++); | |
72cd085a | 94 | |
31f57c28 SG |
95 | x86_pci_write_config8(dev, PIRQE_ROUT, *ptr++); |
96 | x86_pci_write_config8(dev, PIRQF_ROUT, *ptr++); | |
97 | x86_pci_write_config8(dev, PIRQG_ROUT, *ptr++); | |
98 | x86_pci_write_config8(dev, PIRQH_ROUT, *ptr++); | |
72cd085a SG |
99 | |
100 | /* | |
101 | * TODO([email protected]): U-Boot does not set up the interrupts | |
102 | * here. It's unclear if it is needed | |
103 | */ | |
104 | return 0; | |
105 | } | |
106 | ||
107 | static int pch_gpi_routing(const void *blob, int node, pci_dev_t dev) | |
108 | { | |
109 | u8 route[16]; | |
110 | u32 reg; | |
111 | int gpi; | |
112 | ||
113 | if (fdtdec_get_byte_array(blob, node, "intel,gpi-routing", route, | |
114 | sizeof(route))) | |
115 | return -EINVAL; | |
116 | ||
117 | for (reg = 0, gpi = 0; gpi < ARRAY_SIZE(route); gpi++) | |
118 | reg |= route[gpi] << (gpi * 2); | |
119 | ||
31f57c28 | 120 | x86_pci_write_config32(dev, 0xb8, reg); |
72cd085a SG |
121 | |
122 | return 0; | |
123 | } | |
124 | ||
125 | static int pch_power_options(const void *blob, int node, pci_dev_t dev) | |
126 | { | |
127 | u8 reg8; | |
128 | u16 reg16, pmbase; | |
129 | u32 reg32; | |
130 | const char *state; | |
131 | int pwr_on; | |
132 | int nmi_option; | |
133 | int ret; | |
134 | ||
135 | /* | |
136 | * Which state do we want to goto after g3 (power restored)? | |
137 | * 0 == S0 Full On | |
138 | * 1 == S5 Soft Off | |
139 | * | |
140 | * If the option is not existent (Laptops), use Kconfig setting. | |
141 | * TODO([email protected]): Make this configurable | |
142 | */ | |
143 | pwr_on = MAINBOARD_POWER_ON; | |
144 | ||
31f57c28 | 145 | reg16 = x86_pci_read_config16(dev, GEN_PMCON_3); |
72cd085a SG |
146 | reg16 &= 0xfffe; |
147 | switch (pwr_on) { | |
148 | case MAINBOARD_POWER_OFF: | |
149 | reg16 |= 1; | |
150 | state = "off"; | |
151 | break; | |
152 | case MAINBOARD_POWER_ON: | |
153 | reg16 &= ~1; | |
154 | state = "on"; | |
155 | break; | |
156 | case MAINBOARD_POWER_KEEP: | |
157 | reg16 &= ~1; | |
158 | state = "state keep"; | |
159 | break; | |
160 | default: | |
161 | state = "undefined"; | |
162 | } | |
163 | ||
164 | reg16 &= ~(3 << 4); /* SLP_S4# Assertion Stretch 4s */ | |
165 | reg16 |= (1 << 3); /* SLP_S4# Assertion Stretch Enable */ | |
166 | ||
167 | reg16 &= ~(1 << 10); | |
168 | reg16 |= (1 << 11); /* SLP_S3# Min Assertion Width 50ms */ | |
169 | ||
170 | reg16 |= (1 << 12); /* Disable SLP stretch after SUS well */ | |
171 | ||
31f57c28 | 172 | x86_pci_write_config16(dev, GEN_PMCON_3, reg16); |
72cd085a SG |
173 | debug("Set power %s after power failure.\n", state); |
174 | ||
175 | /* Set up NMI on errors. */ | |
176 | reg8 = inb(0x61); | |
177 | reg8 &= 0x0f; /* Higher Nibble must be 0 */ | |
178 | reg8 &= ~(1 << 3); /* IOCHK# NMI Enable */ | |
179 | reg8 |= (1 << 2); /* PCI SERR# Disable for now */ | |
180 | outb(reg8, 0x61); | |
181 | ||
182 | reg8 = inb(0x70); | |
183 | /* TODO([email protected]): Make this configurable */ | |
184 | nmi_option = NMI_OFF; | |
185 | if (nmi_option) { | |
186 | debug("NMI sources enabled.\n"); | |
187 | reg8 &= ~(1 << 7); /* Set NMI. */ | |
188 | } else { | |
189 | debug("NMI sources disabled.\n"); | |
190 | /* Can't mask NMI from PCI-E and NMI_NOW */ | |
191 | reg8 |= (1 << 7); | |
192 | } | |
193 | outb(reg8, 0x70); | |
194 | ||
195 | /* Enable CPU_SLP# and Intel Speedstep, set SMI# rate down */ | |
31f57c28 | 196 | reg16 = x86_pci_read_config16(dev, GEN_PMCON_1); |
72cd085a SG |
197 | reg16 &= ~(3 << 0); /* SMI# rate 1 minute */ |
198 | reg16 &= ~(1 << 10); /* Disable BIOS_PCI_EXP_EN for native PME */ | |
199 | #if DEBUG_PERIODIC_SMIS | |
200 | /* Set DEBUG_PERIODIC_SMIS in pch.h to debug using periodic SMIs */ | |
201 | reg16 |= (3 << 0); /* Periodic SMI every 8s */ | |
202 | #endif | |
31f57c28 | 203 | x86_pci_write_config16(dev, GEN_PMCON_1, reg16); |
72cd085a SG |
204 | |
205 | /* Set the board's GPI routing. */ | |
206 | ret = pch_gpi_routing(blob, node, dev); | |
207 | if (ret) | |
208 | return ret; | |
209 | ||
31f57c28 | 210 | pmbase = x86_pci_read_config16(dev, 0x40) & 0xfffe; |
72cd085a SG |
211 | |
212 | writel(pmbase + GPE0_EN, fdtdec_get_int(blob, node, | |
213 | "intel,gpe0-enable", 0)); | |
214 | writew(pmbase + ALT_GP_SMI_EN, fdtdec_get_int(blob, node, | |
215 | "intel,alt-gp-smi-enable", 0)); | |
216 | ||
217 | /* Set up power management block and determine sleep mode */ | |
218 | reg32 = inl(pmbase + 0x04); /* PM1_CNT */ | |
219 | reg32 &= ~(7 << 10); /* SLP_TYP */ | |
220 | reg32 |= (1 << 0); /* SCI_EN */ | |
221 | outl(reg32, pmbase + 0x04); | |
222 | ||
223 | /* Clear magic status bits to prevent unexpected wake */ | |
224 | setbits_le32(RCB_REG(0x3310), (1 << 4) | (1 << 5) | (1 << 0)); | |
225 | clrbits_le32(RCB_REG(0x3f02), 0xf); | |
226 | ||
227 | return 0; | |
228 | } | |
229 | ||
230 | static void pch_rtc_init(pci_dev_t dev) | |
231 | { | |
232 | int rtc_failed; | |
233 | u8 reg8; | |
234 | ||
31f57c28 | 235 | reg8 = x86_pci_read_config8(dev, GEN_PMCON_3); |
72cd085a SG |
236 | rtc_failed = reg8 & RTC_BATTERY_DEAD; |
237 | if (rtc_failed) { | |
238 | reg8 &= ~RTC_BATTERY_DEAD; | |
31f57c28 | 239 | x86_pci_write_config8(dev, GEN_PMCON_3, reg8); |
72cd085a SG |
240 | } |
241 | debug("rtc_failed = 0x%x\n", rtc_failed); | |
242 | ||
72cd085a SG |
243 | /* TODO: Handle power failure */ |
244 | if (rtc_failed) | |
245 | printf("RTC power failed\n"); | |
72cd085a SG |
246 | } |
247 | ||
248 | /* CougarPoint PCH Power Management init */ | |
249 | static void cpt_pm_init(pci_dev_t dev) | |
250 | { | |
251 | debug("CougarPoint PM init\n"); | |
31f57c28 | 252 | x86_pci_write_config8(dev, 0xa9, 0x47); |
72cd085a SG |
253 | setbits_le32(RCB_REG(0x2238), (1 << 6) | (1 << 0)); |
254 | ||
255 | setbits_le32(RCB_REG(0x228c), 1 << 0); | |
256 | setbits_le32(RCB_REG(0x1100), (1 << 13) | (1 << 14)); | |
257 | setbits_le32(RCB_REG(0x0900), 1 << 14); | |
258 | writel(0xc0388400, RCB_REG(0x2304)); | |
259 | setbits_le32(RCB_REG(0x2314), (1 << 5) | (1 << 18)); | |
260 | setbits_le32(RCB_REG(0x2320), (1 << 15) | (1 << 1)); | |
261 | clrsetbits_le32(RCB_REG(0x3314), ~0x1f, 0xf); | |
262 | writel(0x050f0000, RCB_REG(0x3318)); | |
263 | writel(0x04000000, RCB_REG(0x3324)); | |
264 | setbits_le32(RCB_REG(0x3340), 0xfffff); | |
265 | setbits_le32(RCB_REG(0x3344), 1 << 1); | |
266 | ||
267 | writel(0x0001c000, RCB_REG(0x3360)); | |
268 | writel(0x00061100, RCB_REG(0x3368)); | |
269 | writel(0x7f8fdfff, RCB_REG(0x3378)); | |
270 | writel(0x000003fc, RCB_REG(0x337c)); | |
271 | writel(0x00001000, RCB_REG(0x3388)); | |
272 | writel(0x0001c000, RCB_REG(0x3390)); | |
273 | writel(0x00000800, RCB_REG(0x33a0)); | |
274 | writel(0x00001000, RCB_REG(0x33b0)); | |
275 | writel(0x00093900, RCB_REG(0x33c0)); | |
276 | writel(0x24653002, RCB_REG(0x33cc)); | |
277 | writel(0x062108fe, RCB_REG(0x33d0)); | |
278 | clrsetbits_le32(RCB_REG(0x33d4), 0x0fff0fff, 0x00670060); | |
279 | writel(0x01010000, RCB_REG(0x3a28)); | |
280 | writel(0x01010404, RCB_REG(0x3a2c)); | |
281 | writel(0x01041041, RCB_REG(0x3a80)); | |
282 | clrsetbits_le32(RCB_REG(0x3a84), 0x0000ffff, 0x00001001); | |
283 | setbits_le32(RCB_REG(0x3a84), 1 << 24); /* SATA 2/3 disabled */ | |
284 | setbits_le32(RCB_REG(0x3a88), 1 << 0); /* SATA 4/5 disabled */ | |
285 | writel(0x00000001, RCB_REG(0x3a6c)); | |
286 | clrsetbits_le32(RCB_REG(0x2344), ~0x00ffff00, 0xff00000c); | |
287 | clrsetbits_le32(RCB_REG(0x80c), 0xff << 20, 0x11 << 20); | |
288 | writel(0, RCB_REG(0x33c8)); | |
289 | setbits_le32(RCB_REG(0x21b0), 0xf); | |
290 | } | |
291 | ||
292 | /* PantherPoint PCH Power Management init */ | |
293 | static void ppt_pm_init(pci_dev_t dev) | |
294 | { | |
295 | debug("PantherPoint PM init\n"); | |
31f57c28 | 296 | x86_pci_write_config8(dev, 0xa9, 0x47); |
72cd085a SG |
297 | setbits_le32(RCB_REG(0x2238), 1 << 0); |
298 | setbits_le32(RCB_REG(0x228c), 1 << 0); | |
299 | setbits_le16(RCB_REG(0x1100), (1 << 13) | (1 << 14)); | |
300 | setbits_le16(RCB_REG(0x0900), 1 << 14); | |
301 | writel(0xc03b8400, RCB_REG(0x2304)); | |
302 | setbits_le32(RCB_REG(0x2314), (1 << 5) | (1 << 18)); | |
303 | setbits_le32(RCB_REG(0x2320), (1 << 15) | (1 << 1)); | |
304 | clrsetbits_le32(RCB_REG(0x3314), 0x1f, 0xf); | |
305 | writel(0x054f0000, RCB_REG(0x3318)); | |
306 | writel(0x04000000, RCB_REG(0x3324)); | |
307 | setbits_le32(RCB_REG(0x3340), 0xfffff); | |
308 | setbits_le32(RCB_REG(0x3344), (1 << 1) | (1 << 0)); | |
309 | writel(0x0001c000, RCB_REG(0x3360)); | |
310 | writel(0x00061100, RCB_REG(0x3368)); | |
311 | writel(0x7f8fdfff, RCB_REG(0x3378)); | |
312 | writel(0x000003fd, RCB_REG(0x337c)); | |
313 | writel(0x00001000, RCB_REG(0x3388)); | |
314 | writel(0x0001c000, RCB_REG(0x3390)); | |
315 | writel(0x00000800, RCB_REG(0x33a0)); | |
316 | writel(0x00001000, RCB_REG(0x33b0)); | |
317 | writel(0x00093900, RCB_REG(0x33c0)); | |
318 | writel(0x24653002, RCB_REG(0x33cc)); | |
319 | writel(0x067388fe, RCB_REG(0x33d0)); | |
320 | clrsetbits_le32(RCB_REG(0x33d4), 0x0fff0fff, 0x00670060); | |
321 | writel(0x01010000, RCB_REG(0x3a28)); | |
322 | writel(0x01010404, RCB_REG(0x3a2c)); | |
323 | writel(0x01040000, RCB_REG(0x3a80)); | |
324 | clrsetbits_le32(RCB_REG(0x3a84), 0x0000ffff, 0x00001001); | |
325 | /* SATA 2/3 disabled */ | |
326 | setbits_le32(RCB_REG(0x3a84), 1 << 24); | |
327 | /* SATA 4/5 disabled */ | |
328 | setbits_le32(RCB_REG(0x3a88), 1 << 0); | |
329 | writel(0x00000001, RCB_REG(0x3a6c)); | |
330 | clrsetbits_le32(RCB_REG(0x2344), 0xff0000ff, 0xff00000c); | |
331 | clrsetbits_le32(RCB_REG(0x80c), 0xff << 20, 0x11 << 20); | |
332 | setbits_le32(RCB_REG(0x33a4), (1 << 0)); | |
333 | writel(0, RCB_REG(0x33c8)); | |
334 | setbits_le32(RCB_REG(0x21b0), 0xf); | |
335 | } | |
336 | ||
337 | static void enable_hpet(void) | |
338 | { | |
339 | /* Move HPET to default address 0xfed00000 and enable it */ | |
340 | clrsetbits_le32(RCB_REG(HPTC), 3 << 0, 1 << 7); | |
341 | } | |
342 | ||
343 | static void enable_clock_gating(pci_dev_t dev) | |
344 | { | |
345 | u32 reg32; | |
346 | u16 reg16; | |
347 | ||
348 | setbits_le32(RCB_REG(0x2234), 0xf); | |
349 | ||
31f57c28 | 350 | reg16 = x86_pci_read_config16(dev, GEN_PMCON_1); |
72cd085a | 351 | reg16 |= (1 << 2) | (1 << 11); |
31f57c28 | 352 | x86_pci_write_config16(dev, GEN_PMCON_1, reg16); |
72cd085a SG |
353 | |
354 | pch_iobp_update(0xEB007F07, ~0UL, (1 << 31)); | |
355 | pch_iobp_update(0xEB004000, ~0UL, (1 << 7)); | |
356 | pch_iobp_update(0xEC007F07, ~0UL, (1 << 31)); | |
357 | pch_iobp_update(0xEC004000, ~0UL, (1 << 7)); | |
358 | ||
359 | reg32 = readl(RCB_REG(CG)); | |
360 | reg32 |= (1 << 31); | |
361 | reg32 |= (1 << 29) | (1 << 28); | |
362 | reg32 |= (1 << 27) | (1 << 26) | (1 << 25) | (1 << 24); | |
363 | reg32 |= (1 << 16); | |
364 | reg32 |= (1 << 17); | |
365 | reg32 |= (1 << 18); | |
366 | reg32 |= (1 << 22); | |
367 | reg32 |= (1 << 23); | |
368 | reg32 &= ~(1 << 20); | |
369 | reg32 |= (1 << 19); | |
370 | reg32 |= (1 << 0); | |
371 | reg32 |= (0xf << 1); | |
372 | writel(reg32, RCB_REG(CG)); | |
373 | ||
374 | setbits_le32(RCB_REG(0x38c0), 0x7); | |
375 | setbits_le32(RCB_REG(0x36d4), 0x6680c004); | |
376 | setbits_le32(RCB_REG(0x3564), 0x3); | |
377 | } | |
378 | ||
379 | #if CONFIG_HAVE_SMI_HANDLER | |
380 | static void pch_lock_smm(pci_dev_t dev) | |
381 | { | |
382 | #if TEST_SMM_FLASH_LOCKDOWN | |
383 | u8 reg8; | |
384 | #endif | |
385 | ||
386 | if (acpi_slp_type != 3) { | |
387 | #if ENABLE_ACPI_MODE_IN_COREBOOT | |
388 | debug("Enabling ACPI via APMC:\n"); | |
389 | outb(0xe1, 0xb2); /* Enable ACPI mode */ | |
390 | debug("done.\n"); | |
391 | #else | |
392 | debug("Disabling ACPI via APMC:\n"); | |
393 | outb(0x1e, 0xb2); /* Disable ACPI mode */ | |
394 | debug("done.\n"); | |
395 | #endif | |
396 | } | |
397 | ||
398 | /* Don't allow evil boot loaders, kernels, or | |
399 | * userspace applications to deceive us: | |
400 | */ | |
401 | smm_lock(); | |
402 | ||
403 | #if TEST_SMM_FLASH_LOCKDOWN | |
404 | /* Now try this: */ | |
405 | debug("Locking BIOS to RO... "); | |
31f57c28 | 406 | reg8 = x86_pci_read_config8(dev, 0xdc); /* BIOS_CNTL */ |
72cd085a SG |
407 | debug(" BLE: %s; BWE: %s\n", (reg8 & 2) ? "on" : "off", |
408 | (reg8 & 1) ? "rw" : "ro"); | |
409 | reg8 &= ~(1 << 0); /* clear BIOSWE */ | |
31f57c28 | 410 | x86_pci_write_config8(dev, 0xdc, reg8); |
72cd085a | 411 | reg8 |= (1 << 1); /* set BLE */ |
31f57c28 | 412 | x86_pci_write_config8(dev, 0xdc, reg8); |
72cd085a | 413 | debug("ok.\n"); |
31f57c28 | 414 | reg8 = x86_pci_read_config8(dev, 0xdc); /* BIOS_CNTL */ |
72cd085a SG |
415 | debug(" BLE: %s; BWE: %s\n", (reg8 & 2) ? "on" : "off", |
416 | (reg8 & 1) ? "rw" : "ro"); | |
417 | ||
418 | debug("Writing:\n"); | |
419 | writeb(0, 0xfff00000); | |
420 | debug("Testing:\n"); | |
421 | reg8 |= (1 << 0); /* set BIOSWE */ | |
31f57c28 | 422 | x86_pci_write_config8(dev, 0xdc, reg8); |
72cd085a | 423 | |
31f57c28 | 424 | reg8 = x86_pci_read_config8(dev, 0xdc); /* BIOS_CNTL */ |
72cd085a SG |
425 | debug(" BLE: %s; BWE: %s\n", (reg8 & 2) ? "on" : "off", |
426 | (reg8 & 1) ? "rw" : "ro"); | |
427 | debug("Done.\n"); | |
428 | #endif | |
429 | } | |
430 | #endif | |
431 | ||
432 | static void pch_disable_smm_only_flashing(pci_dev_t dev) | |
433 | { | |
434 | u8 reg8; | |
435 | ||
436 | debug("Enabling BIOS updates outside of SMM... "); | |
31f57c28 | 437 | reg8 = x86_pci_read_config8(dev, 0xdc); /* BIOS_CNTL */ |
72cd085a | 438 | reg8 &= ~(1 << 5); |
31f57c28 | 439 | x86_pci_write_config8(dev, 0xdc, reg8); |
72cd085a SG |
440 | } |
441 | ||
442 | static void pch_fixups(pci_dev_t dev) | |
443 | { | |
444 | u8 gen_pmcon_2; | |
445 | ||
446 | /* Indicate DRAM init done for MRC S3 to know it can resume */ | |
31f57c28 | 447 | gen_pmcon_2 = x86_pci_read_config8(dev, GEN_PMCON_2); |
72cd085a | 448 | gen_pmcon_2 |= (1 << 7); |
31f57c28 | 449 | x86_pci_write_config8(dev, GEN_PMCON_2, gen_pmcon_2); |
72cd085a SG |
450 | |
451 | /* Enable DMI ASPM in the PCH */ | |
452 | clrbits_le32(RCB_REG(0x2304), 1 << 10); | |
453 | setbits_le32(RCB_REG(0x21a4), (1 << 11) | (1 << 10)); | |
454 | setbits_le32(RCB_REG(0x21a8), 0x3); | |
455 | } | |
456 | ||
788cd908 SG |
457 | /** |
458 | * lpc_early_init() - set up LPC serial ports and other early things | |
459 | * | |
460 | * @dev: LPC device | |
461 | * @return 0 if OK, -ve on error | |
462 | */ | |
463 | static int lpc_early_init(struct udevice *dev) | |
2b605154 SG |
464 | { |
465 | struct reg_info { | |
466 | u32 base; | |
467 | u32 size; | |
468 | } values[4], *ptr; | |
469 | int count; | |
470 | int i; | |
471 | ||
788cd908 SG |
472 | count = fdtdec_get_int_array_count(gd->fdt_blob, dev->of_offset, |
473 | "intel,gen-dec", (u32 *)values, | |
474 | sizeof(values) / sizeof(u32)); | |
2b605154 SG |
475 | if (count < 0) |
476 | return -EINVAL; | |
477 | ||
478 | /* Set COM1/COM2 decode range */ | |
788cd908 | 479 | dm_pci_write_config16(dev->parent, LPC_IO_DEC, 0x0010); |
2b605154 SG |
480 | |
481 | /* Enable PS/2 Keyboard/Mouse, EC areas and COM1 */ | |
788cd908 SG |
482 | dm_pci_write_config16(dev->parent, LPC_EN, KBC_LPC_EN | MC_LPC_EN | |
483 | GAMEL_LPC_EN | COMA_LPC_EN); | |
2b605154 SG |
484 | |
485 | /* Write all registers but use 0 if we run out of data */ | |
486 | count = count * sizeof(u32) / sizeof(values[0]); | |
487 | for (i = 0, ptr = values; i < ARRAY_SIZE(values); i++, ptr++) { | |
488 | u32 reg = 0; | |
489 | ||
490 | if (i < count) | |
491 | reg = ptr->base | PCI_COMMAND_IO | (ptr->size << 16); | |
788cd908 | 492 | dm_pci_write_config32(dev->parent, LPC_GENX_DEC(i), reg); |
2b605154 SG |
493 | } |
494 | ||
495 | return 0; | |
496 | } | |
72cd085a SG |
497 | |
498 | int lpc_init(struct pci_controller *hose, pci_dev_t dev) | |
499 | { | |
500 | const void *blob = gd->fdt_blob; | |
501 | int node; | |
502 | ||
503 | debug("pch: lpc_init\n"); | |
504 | pci_write_bar32(hose, dev, 0, 0); | |
505 | pci_write_bar32(hose, dev, 1, 0xff800000); | |
506 | pci_write_bar32(hose, dev, 2, 0xfec00000); | |
507 | pci_write_bar32(hose, dev, 3, 0x800); | |
508 | pci_write_bar32(hose, dev, 4, 0x900); | |
509 | ||
90b16d14 | 510 | node = fdtdec_next_compatible(blob, 0, COMPAT_INTEL_PCH); |
72cd085a SG |
511 | if (node < 0) |
512 | return -ENOENT; | |
513 | ||
514 | /* Set the value for PCI command register. */ | |
31f57c28 | 515 | x86_pci_write_config16(dev, PCI_COMMAND, 0x000f); |
72cd085a SG |
516 | |
517 | /* IO APIC initialization. */ | |
518 | pch_enable_apic(dev); | |
519 | ||
520 | pch_enable_serial_irqs(dev); | |
521 | ||
522 | /* Setup the PIRQ. */ | |
523 | pch_pirq_init(blob, node, dev); | |
524 | ||
525 | /* Setup power options. */ | |
526 | pch_power_options(blob, node, dev); | |
527 | ||
528 | /* Initialize power management */ | |
529 | switch (pch_silicon_type()) { | |
530 | case PCH_TYPE_CPT: /* CougarPoint */ | |
531 | cpt_pm_init(dev); | |
532 | break; | |
533 | case PCH_TYPE_PPT: /* PantherPoint */ | |
534 | ppt_pm_init(dev); | |
535 | break; | |
536 | default: | |
537 | printf("Unknown Chipset: %#02x.%dx\n", PCI_DEV(dev), | |
538 | PCI_FUNC(dev)); | |
539 | return -ENOSYS; | |
540 | } | |
541 | ||
542 | /* Initialize the real time clock. */ | |
543 | pch_rtc_init(dev); | |
544 | ||
545 | /* Initialize the High Precision Event Timers, if present. */ | |
546 | enable_hpet(); | |
547 | ||
548 | /* Initialize Clock Gating */ | |
549 | enable_clock_gating(dev); | |
550 | ||
551 | pch_disable_smm_only_flashing(dev); | |
552 | ||
553 | #if CONFIG_HAVE_SMI_HANDLER | |
554 | pch_lock_smm(dev); | |
555 | #endif | |
556 | ||
557 | pch_fixups(dev); | |
558 | ||
559 | return 0; | |
560 | } | |
561 | ||
562 | void lpc_enable(pci_dev_t dev) | |
563 | { | |
564 | /* Enable PCH Display Port */ | |
565 | writew(0x0010, RCB_REG(DISPBDF)); | |
566 | setbits_le32(RCB_REG(FD2), PCH_ENABLE_DBDF); | |
567 | } | |
90b16d14 | 568 | |
4acc83d4 SG |
569 | static int bd82x6x_lpc_probe(struct udevice *dev) |
570 | { | |
788cd908 SG |
571 | int ret; |
572 | ||
573 | if (gd->flags & GD_FLG_RELOC) | |
574 | return 0; | |
575 | ||
576 | ret = lpc_early_init(dev); | |
577 | if (ret) { | |
578 | debug("%s: lpc_early_init() failed\n", __func__); | |
579 | return ret; | |
580 | } | |
581 | ||
4acc83d4 SG |
582 | return 0; |
583 | } | |
584 | ||
90b16d14 SG |
585 | static const struct udevice_id bd82x6x_lpc_ids[] = { |
586 | { .compatible = "intel,bd82x6x-lpc" }, | |
587 | { } | |
588 | }; | |
589 | ||
590 | U_BOOT_DRIVER(bd82x6x_lpc_drv) = { | |
591 | .name = "lpc", | |
592 | .id = UCLASS_LPC, | |
593 | .of_match = bd82x6x_lpc_ids, | |
4acc83d4 | 594 | .probe = bd82x6x_lpc_probe, |
90b16d14 | 595 | }; |