blob: 5a3ad9dd31dc944e34a6c0e848006d6dbf7f73b3 [file] [log] [blame]
Lee Leahy77ff0b12015-05-05 15:07:29 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2008-2009 coresystems GmbH
5 * Copyright (C) 2013 Google Inc.
Lee Leahy32471722015-04-20 15:20:28 -07006 * Copyright (C) 2015 Intel Corp.
Lee Leahy77ff0b12015-05-05 15:07:29 -07007 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; version 2 of the License.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
Patrick Georgi25509ee2015-03-26 15:17:45 +010019 * Foundation, Inc.
Lee Leahy77ff0b12015-05-05 15:07:29 -070020 */
21
Lee Leahy77ff0b12015-05-05 15:07:29 -070022#include <arch/io.h>
23#include <arch/acpi.h>
24#include <bootstate.h>
25#include <cbmem.h>
Lee Leahy32471722015-04-20 15:20:28 -070026#include "chip.h"
Lee Leahy77ff0b12015-05-05 15:07:29 -070027#include <console/console.h>
28#include <cpu/x86/smm.h>
29#include <device/device.h>
30#include <device/pci.h>
31#include <device/pci_ids.h>
32#include <pc80/mc146818rtc.h>
Lee Leahy32471722015-04-20 15:20:28 -070033#include <romstage_handoff.h>
Lee Leahy77ff0b12015-05-05 15:07:29 -070034#include <soc/iomap.h>
35#include <soc/irq.h>
36#include <soc/lpc.h>
Lee Leahy77ff0b12015-05-05 15:07:29 -070037#include <soc/pci_devs.h>
Lee Leahy32471722015-04-20 15:20:28 -070038#include <soc/pm.h>
Lee Leahy77ff0b12015-05-05 15:07:29 -070039#include <soc/ramstage.h>
40#include <soc/spi.h>
Lee Leahy32471722015-04-20 15:20:28 -070041#include <spi-generic.h>
42#include <stdint.h>
Lee Leahy77ff0b12015-05-05 15:07:29 -070043
44static inline void
45add_mmio_resource(device_t dev, int i, unsigned long addr, unsigned long size)
46{
Lee Leahy32471722015-04-20 15:20:28 -070047 printk(BIOS_SPEW, "%s/%s ( %s, 0x%016lx, 0x%016lx )\n",
48 __FILE__, __func__, dev_name(dev), addr, size);
Lee Leahy77ff0b12015-05-05 15:07:29 -070049 mmio_resource(dev, i, addr >> 10, size >> 10);
50}
51
52static void sc_add_mmio_resources(device_t dev)
53{
Lee Leahy32471722015-04-20 15:20:28 -070054 printk(BIOS_SPEW, "%s/%s ( %s )\n",
55 __FILE__, __func__, dev_name(dev));
Lee Leahy77ff0b12015-05-05 15:07:29 -070056 add_mmio_resource(dev, 0xfeb, ABORT_BASE_ADDRESS, ABORT_BASE_SIZE);
57 add_mmio_resource(dev, PBASE, PMC_BASE_ADDRESS, PMC_BASE_SIZE);
58 add_mmio_resource(dev, IOBASE, IO_BASE_ADDRESS, IO_BASE_SIZE);
59 add_mmio_resource(dev, IBASE, ILB_BASE_ADDRESS, ILB_BASE_SIZE);
60 add_mmio_resource(dev, SBASE, SPI_BASE_ADDRESS, SPI_BASE_SIZE);
61 add_mmio_resource(dev, MPBASE, MPHY_BASE_ADDRESS, MPHY_BASE_SIZE);
62 add_mmio_resource(dev, PUBASE, PUNIT_BASE_ADDRESS, PUNIT_BASE_SIZE);
63 add_mmio_resource(dev, RCBA, RCBA_BASE_ADDRESS, RCBA_BASE_SIZE);
64}
65
66/* Default IO range claimed by the LPC device. The upper bound is exclusive. */
67#define LPC_DEFAULT_IO_RANGE_LOWER 0
68#define LPC_DEFAULT_IO_RANGE_UPPER 0x1000
69
70static inline int io_range_in_default(int base, int size)
71{
72 /* Does it start above the range? */
73 if (base >= LPC_DEFAULT_IO_RANGE_UPPER)
74 return 0;
75
76 /* Is it entirely contained? */
77 if (base >= LPC_DEFAULT_IO_RANGE_LOWER &&
78 (base + size) < LPC_DEFAULT_IO_RANGE_UPPER)
79 return 1;
80
81 /* This will return not in range for partial overlaps. */
82 return 0;
83}
84
85/*
86 * Note: this function assumes there is no overlap with the default LPC device's
87 * claimed range: LPC_DEFAULT_IO_RANGE_LOWER -> LPC_DEFAULT_IO_RANGE_UPPER.
88 */
89static void sc_add_io_resource(device_t dev, int base, int size, int index)
90{
91 struct resource *res;
92
Lee Leahy32471722015-04-20 15:20:28 -070093 printk(BIOS_SPEW, "%s/%s ( %s, 0x%08x, 0x%08x, 0x%08x )\n",
94 __FILE__, __func__, dev_name(dev), base, size, index);
95
Lee Leahy77ff0b12015-05-05 15:07:29 -070096 if (io_range_in_default(base, size))
97 return;
98
99 res = new_resource(dev, index);
100 res->base = base;
101 res->size = size;
102 res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
103}
104
105static void sc_add_io_resources(device_t dev)
106{
107 struct resource *res;
108
Lee Leahy32471722015-04-20 15:20:28 -0700109 printk(BIOS_SPEW, "%s/%s ( %s )\n",
110 __FILE__, __func__, dev_name(dev));
111
Lee Leahy77ff0b12015-05-05 15:07:29 -0700112 /* Add the default claimed IO range for the LPC device. */
113 res = new_resource(dev, 0);
114 res->base = LPC_DEFAULT_IO_RANGE_LOWER;
115 res->size = LPC_DEFAULT_IO_RANGE_UPPER - LPC_DEFAULT_IO_RANGE_LOWER;
116 res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
117
118 /* GPIO */
119 sc_add_io_resource(dev, GPIO_BASE_ADDRESS, 256, GBASE);
120
121 /* ACPI */
122 sc_add_io_resource(dev, ACPI_BASE_ADDRESS, 128, ABASE);
123}
124
125static void sc_read_resources(device_t dev)
126{
Lee Leahy32471722015-04-20 15:20:28 -0700127 printk(BIOS_SPEW, "%s/%s ( %s )\n",
128 __FILE__, __func__, dev_name(dev));
129
Lee Leahy77ff0b12015-05-05 15:07:29 -0700130 /* Get the normal PCI resources of this device. */
131 pci_dev_read_resources(dev);
132
133 /* Add non-standard MMIO resources. */
134 sc_add_mmio_resources(dev);
135
136 /* Add IO resources. */
137 sc_add_io_resources(dev);
138}
139
140static void sc_rtc_init(void)
141{
142 uint32_t gen_pmcon1;
143 int rtc_fail;
144 struct chipset_power_state *ps = cbmem_find(CBMEM_ID_POWER_STATE);
145
Lee Leahy32471722015-04-20 15:20:28 -0700146 printk(BIOS_SPEW, "%s/%s\n",
147 __FILE__, __func__);
148 if (ps != NULL)
Lee Leahy77ff0b12015-05-05 15:07:29 -0700149 gen_pmcon1 = ps->gen_pmcon1;
Lee Leahy32471722015-04-20 15:20:28 -0700150 else
151 gen_pmcon1 = read32((void *)(PMC_BASE_ADDRESS + GEN_PMCON1));
Lee Leahy77ff0b12015-05-05 15:07:29 -0700152
153 rtc_fail = !!(gen_pmcon1 & RPS);
154
Lee Leahy32471722015-04-20 15:20:28 -0700155 if (rtc_fail)
Lee Leahy77ff0b12015-05-05 15:07:29 -0700156 printk(BIOS_DEBUG, "RTC failure.\n");
Lee Leahy77ff0b12015-05-05 15:07:29 -0700157
158 cmos_init(rtc_fail);
159}
160
Lee Leahy77ff0b12015-05-05 15:07:29 -0700161static void sc_init(device_t dev)
162{
163 int i;
Lee Leahy32471722015-04-20 15:20:28 -0700164 const unsigned long pr_base = ILB_BASE_ADDRESS + 0x08;
165 const unsigned long ir_base = ILB_BASE_ADDRESS + 0x20;
166 void *gen_pmcon1 = (void *)(PMC_BASE_ADDRESS + GEN_PMCON1);
167 void *actl = (void *)(ILB_BASE_ADDRESS + ACTL);
168 const struct soc_irq_route *ir = &global_soc_irq_route;
169 struct soc_intel_braswell_config *config = dev->chip_info;
170
171 printk(BIOS_SPEW, "%s/%s ( %s )\n",
172 __FILE__, __func__, dev_name(dev));
Lee Leahy77ff0b12015-05-05 15:07:29 -0700173
174 /* Set up the PIRQ PIC routing based on static config. */
Lee Leahy32471722015-04-20 15:20:28 -0700175 for (i = 0; i < NUM_PIRQS; i++)
176 write8((void *)(pr_base + i*sizeof(ir->pic[i])),
177 ir->pic[i]);
178
Lee Leahy77ff0b12015-05-05 15:07:29 -0700179 /* Set up the per device PIRQ routing base on static config. */
Lee Leahy32471722015-04-20 15:20:28 -0700180 for (i = 0; i < NUM_IR_DEVS; i++)
181 write16((void *)(ir_base + i*sizeof(ir->pcidev[i])),
182 ir->pcidev[i]);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700183
184 /* Route SCI to IRQ9 */
185 write32(actl, (read32(actl) & ~SCIS_MASK) | SCIS_IRQ9);
186
187 sc_rtc_init();
188
189 if (config->disable_slp_x_stretch_sus_fail) {
190 printk(BIOS_DEBUG, "Disabling slp_x stretching.\n");
191 write32(gen_pmcon1,
192 read32(gen_pmcon1) | DIS_SLP_X_STRCH_SUS_UP);
193 } else {
194 write32(gen_pmcon1,
195 read32(gen_pmcon1) & ~DIS_SLP_X_STRCH_SUS_UP);
196 }
197
Lee Leahy77ff0b12015-05-05 15:07:29 -0700198}
199
200/*
201 * Common code for the south cluster devices.
202 */
203
Lee Leahy32471722015-04-20 15:20:28 -0700204/* Set bit in function disble register to hide this device. */
Lee Leahy77ff0b12015-05-05 15:07:29 -0700205static void sc_disable_devfn(device_t dev)
206{
Lee Leahy32471722015-04-20 15:20:28 -0700207 void *func_dis = (void *)(PMC_BASE_ADDRESS + FUNC_DIS);
208 void *func_dis2 = (void *)(PMC_BASE_ADDRESS + FUNC_DIS2);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700209 uint32_t mask = 0;
210 uint32_t mask2 = 0;
211
Lee Leahy32471722015-04-20 15:20:28 -0700212 printk(BIOS_SPEW, "%s/%s ( %s )\n",
213 __FILE__, __func__, dev_name(dev));
214
215#define SET_DIS_MASK(name_) \
216 case PCI_DEVFN(name_ ## _DEV, name_ ## _FUNC): \
217 mask |= name_ ## _DIS
218#define SET_DIS_MASK2(name_) \
219 case PCI_DEVFN(name_ ## _DEV, name_ ## _FUNC): \
220 mask2 |= name_ ## _DIS
221
Lee Leahy77ff0b12015-05-05 15:07:29 -0700222 switch (dev->path.pci.devfn) {
Lee Leahy32471722015-04-20 15:20:28 -0700223 SET_DIS_MASK(SDIO);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700224 break;
Lee Leahy32471722015-04-20 15:20:28 -0700225 SET_DIS_MASK(SD);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700226 break;
Lee Leahy32471722015-04-20 15:20:28 -0700227 SET_DIS_MASK(SATA);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700228 break;
Lee Leahy32471722015-04-20 15:20:28 -0700229 SET_DIS_MASK(XHCI);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700230 /* Disable super speed PHY when XHCI is not available. */
231 mask2 |= USH_SS_PHY_DIS;
232 break;
Lee Leahy32471722015-04-20 15:20:28 -0700233 SET_DIS_MASK(LPE);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700234 break;
Lee Leahy32471722015-04-20 15:20:28 -0700235 SET_DIS_MASK(MMC);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700236 break;
Lee Leahy32471722015-04-20 15:20:28 -0700237 SET_DIS_MASK(SIO_DMA1);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700238 break;
Lee Leahy32471722015-04-20 15:20:28 -0700239 SET_DIS_MASK(I2C1);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700240 break;
Lee Leahy32471722015-04-20 15:20:28 -0700241 SET_DIS_MASK(I2C2);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700242 break;
Lee Leahy32471722015-04-20 15:20:28 -0700243 SET_DIS_MASK(I2C3);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700244 break;
Lee Leahy32471722015-04-20 15:20:28 -0700245 SET_DIS_MASK(I2C4);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700246 break;
Lee Leahy32471722015-04-20 15:20:28 -0700247 SET_DIS_MASK(I2C5);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700248 break;
Lee Leahy32471722015-04-20 15:20:28 -0700249 SET_DIS_MASK(I2C6);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700250 break;
Lee Leahy32471722015-04-20 15:20:28 -0700251 SET_DIS_MASK(I2C7);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700252 break;
Lee Leahy32471722015-04-20 15:20:28 -0700253 SET_DIS_MASK(TXE);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700254 break;
Lee Leahy32471722015-04-20 15:20:28 -0700255 SET_DIS_MASK(HDA);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700256 break;
Lee Leahy32471722015-04-20 15:20:28 -0700257 SET_DIS_MASK(PCIE_PORT1);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700258 break;
Lee Leahy32471722015-04-20 15:20:28 -0700259 SET_DIS_MASK(PCIE_PORT2);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700260 break;
Lee Leahy32471722015-04-20 15:20:28 -0700261 SET_DIS_MASK(PCIE_PORT3);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700262 break;
Lee Leahy32471722015-04-20 15:20:28 -0700263 SET_DIS_MASK(PCIE_PORT4);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700264 break;
Lee Leahy32471722015-04-20 15:20:28 -0700265 SET_DIS_MASK(SIO_DMA2);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700266 break;
Lee Leahy32471722015-04-20 15:20:28 -0700267 SET_DIS_MASK(PWM1);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700268 break;
Lee Leahy32471722015-04-20 15:20:28 -0700269 SET_DIS_MASK(PWM2);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700270 break;
Lee Leahy32471722015-04-20 15:20:28 -0700271 SET_DIS_MASK(HSUART1);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700272 break;
Lee Leahy32471722015-04-20 15:20:28 -0700273 SET_DIS_MASK(HSUART2);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700274 break;
Lee Leahy32471722015-04-20 15:20:28 -0700275 SET_DIS_MASK(SPI);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700276 break;
Lee Leahy32471722015-04-20 15:20:28 -0700277 SET_DIS_MASK2(SMBUS);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700278 break;
279 }
280
281 if (mask != 0) {
282 write32(func_dis, read32(func_dis) | mask);
283 /* Ensure posted write hits. */
284 read32(func_dis);
285 }
286
287 if (mask2 != 0) {
288 write32(func_dis2, read32(func_dis2) | mask2);
289 /* Ensure posted write hits. */
290 read32(func_dis2);
291 }
292}
293
294static inline void set_d3hot_bits(device_t dev, int offset)
295{
296 uint32_t reg8;
Lee Leahy32471722015-04-20 15:20:28 -0700297
298 printk(BIOS_SPEW, "%s/%s ( %s, 0x%08x )\n",
299 __FILE__, __func__, dev_name(dev), offset);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700300 printk(BIOS_DEBUG, "Power management CAP offset 0x%x.\n", offset);
301 reg8 = pci_read_config8(dev, offset + 4);
302 reg8 |= 0x3;
303 pci_write_config8(dev, offset + 4, reg8);
304}
305
Lee Leahy32471722015-04-20 15:20:28 -0700306/*
307 * Parts of the audio subsystem are powered by the HDA device. Therefore, one
Lee Leahy77ff0b12015-05-05 15:07:29 -0700308 * cannot put HDA into D3Hot. Instead perform this workaround to make some of
Lee Leahy32471722015-04-20 15:20:28 -0700309 * the audio paths work for LPE audio.
310 */
Lee Leahy77ff0b12015-05-05 15:07:29 -0700311static void hda_work_around(device_t dev)
312{
Lee Leahy32471722015-04-20 15:20:28 -0700313 void *gctl = (void *)(TEMP_BASE_ADDRESS + 0x8);
314
315 printk(BIOS_SPEW, "%s/%s ( %s )\n",
316 __FILE__, __func__, dev_name(dev));
Lee Leahy77ff0b12015-05-05 15:07:29 -0700317
318 /* Need to set magic register 0x43 to 0xd7 in config space. */
319 pci_write_config8(dev, 0x43, 0xd7);
320
Lee Leahy32471722015-04-20 15:20:28 -0700321 /*
322 * Need to set bit 0 of GCTL to take the device out of reset. However,
323 * that requires setting up the 64-bit BAR.
324 */
Lee Leahy77ff0b12015-05-05 15:07:29 -0700325 pci_write_config32(dev, PCI_BASE_ADDRESS_0, TEMP_BASE_ADDRESS);
326 pci_write_config32(dev, PCI_BASE_ADDRESS_1, 0);
327 pci_write_config8(dev, PCI_COMMAND, PCI_COMMAND_MEMORY);
328 write32(gctl, read32(gctl) | 0x1);
329 pci_write_config8(dev, PCI_COMMAND, 0);
330 pci_write_config32(dev, PCI_BASE_ADDRESS_0, 0);
331}
332
333static int place_device_in_d3hot(device_t dev)
334{
335 unsigned offset;
336
Lee Leahy32471722015-04-20 15:20:28 -0700337 printk(BIOS_SPEW, "%s/%s ( %s )\n",
338 __FILE__, __func__, dev_name(dev));
339
340 /*
341 * Parts of the HDA block are used for LPE audio as well.
342 * Therefore assume the HDA will never be put into D3Hot.
343 */
Lee Leahy77ff0b12015-05-05 15:07:29 -0700344 if (dev->path.pci.devfn == PCI_DEVFN(HDA_DEV, HDA_FUNC)) {
345 hda_work_around(dev);
346 return 0;
347 }
348
349 offset = pci_find_capability(dev, PCI_CAP_ID_PM);
350
351 if (offset != 0) {
352 set_d3hot_bits(dev, offset);
353 return 0;
354 }
355
Lee Leahy32471722015-04-20 15:20:28 -0700356 /*
357 * For some reason some of the devices don't have the capability
358 * pointer set correctly. Work around this by hard coding the offset.
359 */
360#define DEV_CASE(name_) \
361 case PCI_DEVFN(name_ ## _DEV, name_ ## _FUNC)
362
Lee Leahy77ff0b12015-05-05 15:07:29 -0700363 switch (dev->path.pci.devfn) {
Lee Leahy32471722015-04-20 15:20:28 -0700364 DEV_CASE(SDIO) :
365 DEV_CASE(SD) :
366 DEV_CASE(MMC) :
367 DEV_CASE(LPE) :
368 DEV_CASE(SIO_DMA1) :
369 DEV_CASE(I2C1) :
370 DEV_CASE(I2C2) :
371 DEV_CASE(I2C3) :
372 DEV_CASE(I2C4) :
373 DEV_CASE(I2C5) :
374 DEV_CASE(I2C6) :
375 DEV_CASE(I2C7) :
376 DEV_CASE(SIO_DMA2) :
377 DEV_CASE(PWM1) :
378 DEV_CASE(PWM2) :
379 DEV_CASE(HSUART1) :
380 DEV_CASE(HSUART2) :
381 DEV_CASE(SPI) :
Lee Leahy77ff0b12015-05-05 15:07:29 -0700382 offset = 0x80;
383 break;
Lee Leahy32471722015-04-20 15:20:28 -0700384 DEV_CASE(SATA) :
385 DEV_CASE(XHCI) :
Lee Leahy77ff0b12015-05-05 15:07:29 -0700386 offset = 0x70;
387 break;
Lee Leahy32471722015-04-20 15:20:28 -0700388 DEV_CASE(HDA) :
389 DEV_CASE(SMBUS) :
Lee Leahy77ff0b12015-05-05 15:07:29 -0700390 offset = 0x50;
391 break;
Lee Leahy32471722015-04-20 15:20:28 -0700392 DEV_CASE(TXE) :
Lee Leahy77ff0b12015-05-05 15:07:29 -0700393 /* TXE cannot be placed in D3Hot. */
394 return 0;
Lee Leahy77ff0b12015-05-05 15:07:29 -0700395 break;
Lee Leahy32471722015-04-20 15:20:28 -0700396 DEV_CASE(PCIE_PORT1) :
397 DEV_CASE(PCIE_PORT2) :
398 DEV_CASE(PCIE_PORT3) :
399 DEV_CASE(PCIE_PORT4) :
Lee Leahy77ff0b12015-05-05 15:07:29 -0700400 offset = 0xa0;
401 break;
402 }
403
404 if (offset != 0) {
405 set_d3hot_bits(dev, offset);
406 return 0;
407 }
408
409 return -1;
410}
411
412/* Common PCI device function disable. */
413void southcluster_enable_dev(device_t dev)
414{
415 uint32_t reg32;
416
Lee Leahy32471722015-04-20 15:20:28 -0700417 printk(BIOS_SPEW, "%s/%s ( %s )\n",
418 __FILE__, __func__, dev_name(dev));
Lee Leahy77ff0b12015-05-05 15:07:29 -0700419 if (!dev->enabled) {
420 int slot = PCI_SLOT(dev->path.pci.devfn);
421 int func = PCI_FUNC(dev->path.pci.devfn);
422 printk(BIOS_DEBUG, "%s: Disabling device: %02x.%01x\n",
423 dev_path(dev), slot, func);
424
425 /* Ensure memory, io, and bus master are all disabled */
426 reg32 = pci_read_config32(dev, PCI_COMMAND);
427 reg32 &= ~(PCI_COMMAND_MASTER |
428 PCI_COMMAND_MEMORY | PCI_COMMAND_IO);
429 pci_write_config32(dev, PCI_COMMAND, reg32);
430
431 /* Place device in D3Hot */
432 if (place_device_in_d3hot(dev) < 0) {
433 printk(BIOS_WARNING,
434 "Could not place %02x.%01x into D3Hot. "
435 "Keeping device visible.\n", slot, func);
436 return;
437 }
438 /* Disable this device if possible */
439 sc_disable_devfn(dev);
440 } else {
441 /* Enable SERR */
442 reg32 = pci_read_config32(dev, PCI_COMMAND);
443 reg32 |= PCI_COMMAND_SERR;
444 pci_write_config32(dev, PCI_COMMAND, reg32);
445 }
446}
447
448static struct device_operations device_ops = {
449 .read_resources = sc_read_resources,
450 .set_resources = pci_dev_set_resources,
451 .enable_resources = NULL,
452 .init = sc_init,
453 .enable = southcluster_enable_dev,
Lee Leahy32471722015-04-20 15:20:28 -0700454 .scan_bus = scan_lpc_bus,
Lee Leahy77ff0b12015-05-05 15:07:29 -0700455 .ops_pci = &soc_pci_ops,
456};
457
458static const struct pci_driver southcluster __pci_driver = {
459 .ops = &device_ops,
460 .vendor = PCI_VENDOR_ID_INTEL,
461 .device = LPC_DEVID,
462};
463
464int __attribute__((weak)) mainboard_get_spi_config(struct spi_config *cfg)
465{
Lee Leahy32471722015-04-20 15:20:28 -0700466 printk(BIOS_SPEW, "%s/%s ( 0x%p )\n",
467 __FILE__, __func__, (void *)cfg);
Lee Leahy77ff0b12015-05-05 15:07:29 -0700468 return -1;
469}
470
471static void finalize_chipset(void *unused)
472{
Lee Leahy32471722015-04-20 15:20:28 -0700473 void *bcr = (void *)(SPI_BASE_ADDRESS + BCR);
474 void *gcs = (void *)(RCBA_BASE_ADDRESS + GCS);
475 void *gen_pmcon2 = (void *)(PMC_BASE_ADDRESS + GEN_PMCON2);
476 void *etr = (void *)(PMC_BASE_ADDRESS + ETR);
477 uint8_t *spi = (uint8_t *)SPI_BASE_ADDRESS;
Lee Leahy77ff0b12015-05-05 15:07:29 -0700478 struct spi_config cfg;
479
Lee Leahy32471722015-04-20 15:20:28 -0700480 printk(BIOS_SPEW, "%s/%s ( 0x%p )\n",
481 __FILE__, __func__, unused);
482
Lee Leahy77ff0b12015-05-05 15:07:29 -0700483 /* Set the lock enable on the BIOS control register. */
484 write32(bcr, read32(bcr) | BCR_LE);
485
486 /* Set BIOS lock down bit controlling boot block size and swapping. */
487 write32(gcs, read32(gcs) | BILD);
488
489 /* Lock sleep stretching policy and set SMI lock. */
490 write32(gen_pmcon2, read32(gen_pmcon2) | SLPSX_STR_POL_LOCK | SMI_LOCK);
491
492 /* Set the CF9 lock. */
493 write32(etr, read32(etr) | CF9LOCK);
494
495 if (mainboard_get_spi_config(&cfg) < 0) {
496 printk(BIOS_DEBUG, "No SPI lockdown configuration.\n");
497 } else {
498 write16(spi + PREOP, cfg.preop);
499 write16(spi + OPTYPE, cfg.optype);
500 write32(spi + OPMENU0, cfg.opmenu[0]);
501 write32(spi + OPMENU1, cfg.opmenu[1]);
502 write16(spi + HSFSTS, read16(spi + HSFSTS) | FLOCKDN);
503 write32(spi + UVSCC, cfg.uvscc);
504 write32(spi + LVSCC, cfg.lvscc | VCL);
505 }
Lee Leahy32471722015-04-20 15:20:28 -0700506 spi_init();
Lee Leahy77ff0b12015-05-05 15:07:29 -0700507
508 printk(BIOS_DEBUG, "Finalizing SMM.\n");
509 outb(APM_CNT_FINALIZE, APM_CNT);
510}
511
512BOOT_STATE_INIT_ENTRY(BS_OS_RESUME, BS_ON_ENTRY, finalize_chipset, NULL);
513BOOT_STATE_INIT_ENTRY(BS_PAYLOAD_LOAD, BS_ON_EXIT, finalize_chipset, NULL);