blob: 8f2967048a1f85e7ed46314259b2820926602b04 [file] [log] [blame]
Martin Roth829c41d2014-05-21 14:21:22 -06001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2008-2009 coresystems GmbH
5 * Copyright (C) 2013 Sage Electronic Engineering, LLC.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; version 2 of
10 * 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
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include <console/console.h>
23#include <device/device.h>
24#include <device/pci.h>
25#include <device/pci_ids.h>
26#include <pc80/mc146818rtc.h>
27#include <pc80/isa-dma.h>
28#include <pc80/i8259.h>
29#include <arch/io.h>
30#include <arch/ioapic.h>
31#include <arch/acpi.h>
32#include <cpu/cpu.h>
33#include <elog.h>
Vladimir Serbinenko689ddf62014-10-11 11:25:41 +020034#include <string.h>
35#include <cbmem.h>
36#include <arch/acpi.h>
37#include <arch/acpigen.h>
Martin Roth829c41d2014-05-21 14:21:22 -060038#include "soc.h"
39#include "irq.h"
Vladimir Serbinenko689ddf62014-10-11 11:25:41 +020040#include "nvs.h"
Martin Roth829c41d2014-05-21 14:21:22 -060041
42#define NMI_OFF 0
43
44#define ENABLE_ACPI_MODE_IN_COREBOOT 0
45#define TEST_SMM_FLASH_LOCKDOWN 0
46
47typedef struct southbridge_intel_fsp_rangeley_config config_t;
48
49static void soc_enable_apic(struct device *dev)
50{
51 int i;
52 u32 reg32;
53 volatile u32 *ioapic_index = (volatile u32 *)(IO_APIC_ADDR);
54 volatile u32 *ioapic_data = (volatile u32 *)(IO_APIC_ADDR + 0x10);
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080055 u32 *ilb_base = (u32 *)(pci_read_config32(dev, IBASE) & ~0x0f);
Martin Roth829c41d2014-05-21 14:21:22 -060056
57 /*
58 * Enable ACPI I/O and power management.
59 * Set SCI IRQ to IRQ9
60 */
61 write32(ilb_base + ILB_OIC, 0x100); /* AEN */
62 reg32 = read32(ilb_base + ILB_OIC); /* Read back per BWG */
63 write32(ilb_base + ILB_ACTL, 0); /* ACTL bit 2:0 SCIS IRQ9 */
64
65 *ioapic_index = 0;
66 *ioapic_data = (1 << 25);
67
68 /* affirm full set of redirection table entries ("write once") */
69 *ioapic_index = 1;
70 reg32 = *ioapic_data;
71 *ioapic_index = 1;
72 *ioapic_data = reg32;
73
74 *ioapic_index = 0;
75 reg32 = *ioapic_data;
76 printk(BIOS_DEBUG, "Southbridge APIC ID = %x\n", (reg32 >> 24) & 0x0f);
77 if (reg32 != (1 << 25))
78 die("APIC Error\n");
79
80 printk(BIOS_SPEW, "Dumping IOAPIC registers\n");
81 for (i=0; i<3; i++) {
82 *ioapic_index = i;
83 printk(BIOS_SPEW, " reg 0x%04x:", i);
84 reg32 = *ioapic_data;
85 printk(BIOS_SPEW, " 0x%08x\n", reg32);
86 }
87
88 *ioapic_index = 3; /* Select Boot Configuration register. */
89 *ioapic_data = 1; /* Use Processor System Bus to deliver interrupts. */
90}
91
92static void soc_enable_serial_irqs(struct device *dev)
93{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080094 u8 *ibase;
Martin Roth829c41d2014-05-21 14:21:22 -060095
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -080096 ibase = (u8 *)(pci_read_config32(dev, IBASE) & ~0xF);
Martin Roth829c41d2014-05-21 14:21:22 -060097
98 /* Set packet length and toggle silent mode bit for one frame. */
99 write8(ibase + ILB_SERIRQ_CNTL, (1 << 7));
100
101#if !CONFIG_SERIRQ_CONTINUOUS_MODE
102 write8(ibase + ILB_SERIRQ_CNTL, 0);
103#endif
104}
105
106/*
107 * Write PCI config space IRQ assignments. PCI devices have the INT_LINE
108 * (0x3C) and INT_PIN (0x3D) registers which report interrupt routing
109 * information to operating systems and drivers. The INT_PIN register is
110 * generally read only and reports which interrupt pin A - D it uses. The
111 * INT_LINE register is configurable and reports which IRQ (generally the
112 * PIC IRQs 1 - 15) it will use. This needs to take interrupt pin swizzling
113 * on devices that are downstream on a PCI bridge into account.
114 *
115 * This function will loop through all enabled PCI devices and program the
116 * INT_LINE register with the correct PIC IRQ number for the INT_PIN that it
117 * uses. It then configures each interrupt in the pic to be level triggered.
118 */
119static void write_pci_config_irqs(void)
120{
121 device_t irq_dev;
122 device_t targ_dev;
123 uint8_t int_line = 0;
124 uint8_t original_int_pin = 0;
125 uint8_t new_int_pin = 0;
126 uint16_t current_bdf = 0;
127 uint16_t parent_bdf = 0;
128 uint8_t pirq = 0;
129 uint8_t device_num = 0;
130 const struct rangeley_irq_route *ir = &global_rangeley_irq_route;
131
132 if (ir == NULL) {
133 printk(BIOS_WARNING, "Warning: Can't write PCI IRQ assignments because"
134 " 'global_rangeley_irq_route' structure does not exist\n");
135 return;
136 }
137
138 /*
139 * Loop through all enabled devices and program their
140 * INT_LINE, INT_PIN registers from values taken from
141 * the Interrupt Route registers in the ILB
142 */
143 printk(BIOS_DEBUG, "PCI_CFG IRQ: Write PCI config space IRQ assignments\n");
144 for(irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) {
145
146 if ((irq_dev->path.type != DEVICE_PATH_PCI) ||
147 (!irq_dev->enabled))
148 continue;
149
150 current_bdf = irq_dev->path.pci.devfn |
151 irq_dev->bus->secondary << 8;
152
153 /*
154 * Step 1: Get the INT_PIN and device structure to look for
155 * in the pirq_data table defined in the mainboard directory.
156 */
157 targ_dev = NULL;
158 new_int_pin = get_pci_irq_pins(irq_dev, &targ_dev);
159 if (targ_dev == NULL || new_int_pin < 1)
160 continue;
161
162 /* Get the original INT_PIN for record keeping */
163 original_int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN);
164
165 parent_bdf = targ_dev->path.pci.devfn
166 | targ_dev->bus->secondary << 8;
167 device_num = PCI_SLOT(parent_bdf);
168
169 if (ir->pcidev[device_num] == 0) {
170 printk(BIOS_WARNING,
171 "Warning: PCI Device %d does not have an IRQ entry, skipping it\n",
172 device_num);
173 continue;
174 }
175
176 /* Find the PIRQ that is attached to the INT_PIN this device uses */
177 pirq = (ir->pcidev[device_num] >> ((new_int_pin - 1) * 4)) & 0xF;
178
179 /* Get the INT_LINE this device/function will use */
180 int_line = ir->pic[pirq];
181
182 if (int_line != PIRQ_PIC_IRQDISABLE) {
183 /* Set this IRQ to level triggered since it is used by a PCI device */
184 i8259_configure_irq_trigger(int_line, IRQ_LEVEL_TRIGGERED);
185 /* Set the Interrupt Line register in PCI config space */
186 pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line);
187 } else {
188 /* Set the Interrupt line register as "unknown or unused" */
189 pci_write_config8(irq_dev, PCI_INTERRUPT_LINE,
190 PIRQ_PIC_UNKNOWN_UNUSED);
191 }
192
193 printk(BIOS_SPEW, "\tINT_PIN\t\t: %d (%s)\n",
194 original_int_pin, pin_to_str(original_int_pin));
195 if (parent_bdf != current_bdf)
196 printk(BIOS_SPEW, "\tSwizzled to\t: %d (%s)\n",
197 new_int_pin, pin_to_str(new_int_pin));
198 printk(BIOS_SPEW, "\tPIRQ\t\t: %c\n"
199 "\tINT_LINE\t: 0x%X (IRQ %d)\n",
200 'A' + pirq, int_line, int_line);
201 }
202 printk(BIOS_DEBUG, "PCI_CFG IRQ: Finished writing PCI config space IRQ assignments\n");
203}
204
205static void soc_pirq_init(device_t dev)
206{
207 int i, j;
208 int pirq;
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -0800209 u8 *ibase = (u8 *)(pci_read_config32(dev, IBASE) & ~0xF);
210 u8 *pr_base = ibase + 0x08;
211 u16 *ir_base = (u16 *)(ibase + 0x20);
212 u32 *actl = (u32 *)ibase;
Martin Roth829c41d2014-05-21 14:21:22 -0600213 const struct rangeley_irq_route *ir = &global_rangeley_irq_route;
214
215 /* Set up the PIRQ PIC routing based on static config. */
216 printk(BIOS_SPEW, "Start writing IRQ assignments\n"
217 "PIRQ\tA \tB \tC \tD \tE \tF \tG \tH\n"
218 "IRQ ");
219 for (i = 0; i < NUM_PIRQS; i++) {
220 write8(pr_base + i*sizeof(ir->pic[i]), ir->pic[i]);
221 printk(BIOS_SPEW, "\t%d", ir->pic[i]);
222 }
223 printk(BIOS_SPEW, "\n\n");
224
225 /* Set up the per device PIRQ routing based on static config. */
226 printk(BIOS_SPEW, "\t\t\tPIRQ[A-H] routed to each INT_PIN[A-D]\n"
227 "Dev\tINTA (IRQ)\tINTB (IRQ)\tINTC (IRQ)\tINTD (IRQ)\n");
228 for (i = 0; i < NUM_OF_PCI_DEVS; i++) {
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -0800229 write16(ir_base + i, ir->pcidev[i]);
Martin Roth829c41d2014-05-21 14:21:22 -0600230
231 /* If the entry is more than just 0, print it out */
232 if(ir->pcidev[i]) {
233 printk(BIOS_SPEW, " %d: ", i);
234 for (j = 0; j < 4; j++) {
235 pirq = (ir->pcidev[i] >> (j * 4)) & 0xF;
236 printk(BIOS_SPEW, "\t%-4c (%d)", 'A' + pirq, ir->pic[pirq]);
237 }
238 printk(BIOS_SPEW, "\n");
239 }
240 }
241
242 /* Route SCI to IRQ9 */
243 write32(actl, (read32(actl) & ~SCIS_MASK) | SCIS_IRQ9);
244 printk(BIOS_SPEW, "Finished writing IRQ assignments\n");
245
246 /* Write IRQ assignments to PCI config space */
247 write_pci_config_irqs();
248}
249
250static void soc_power_options(device_t dev)
251{
252 u8 reg8;
253 u16 pmbase;
254 u32 reg32;
255
256 /* Get the chip configuration */
257 config_t *config = dev->chip_info;
258
259 int nmi_option;
260
261 /* Set up NMI on errors. */
262 reg8 = inb(0x61);
263 reg8 &= 0x0f; /* Higher Nibble must be 0 */
264 reg8 &= ~(1 << 3); /* IOCHK# NMI Enable */
265 // reg8 &= ~(1 << 2); /* PCI SERR# Enable */
266 reg8 |= (1 << 2); /* PCI SERR# Disable for now */
267 outb(reg8, 0x61);
268
269 reg8 = inb(0x70);
270 nmi_option = NMI_OFF;
271 get_option(&nmi_option, "nmi");
272 if (nmi_option) {
273 printk(BIOS_INFO, "NMI sources enabled.\n");
274 reg8 &= ~(1 << 7); /* Set NMI. */
275 } else {
276 printk(BIOS_INFO, "NMI sources disabled.\n");
277 reg8 |= ( 1 << 7); /* Can't mask NMI from PCI-E and NMI_NOW */
278 }
279 outb(reg8, 0x70);
280
281 pmbase = pci_read_config16(dev, ABASE) & ~0xf;
282
283 outl(config->gpe0_en, pmbase + GPE0_EN);
284 outw(config->alt_gp_smi_en, pmbase + ALT_GP_SMI_EN);
285
286 /* Set up power management block and determine sleep mode */
287 reg32 = inl(pmbase + PM1_CNT); // PM1_CNT
288 reg32 &= ~(7 << 10); // SLP_TYP
289 reg32 |= (1 << 0); // SCI_EN
290 outl(reg32, pmbase + PM1_CNT);
291}
292
293/* Disable the HPET, Clear the counter, and re-enable it. */
294static void enable_hpet(void)
295{
Kevin Paul Herbertbde6d302014-12-24 18:43:20 -0800296 write8((u8 *)HPET_GCFG, 0x00);
297 write32((u32 *)HPET_MCV, 0x00000000);
298 write32((u32 *)(HPET_MCV + 0x04), 0x00000000);
299 write8((u8 *)HPET_GCFG, 0x01);
Martin Roth829c41d2014-05-21 14:21:22 -0600300}
301
302static void soc_disable_smm_only_flashing(struct device *dev)
303{
304 u8 reg8;
305
306 printk(BIOS_SPEW, "Enabling BIOS updates outside of SMM... ");
307 reg8 = pci_read_config8(dev, 0xdc); /* BIOS_CNTL */
308 reg8 &= ~(1 << 5);
309 pci_write_config8(dev, 0xdc, reg8);
310}
311
312static void lpc_init(struct device *dev)
313{
314 printk(BIOS_DEBUG, "soc: lpc_init\n");
315
316 /* Set the value for PCI command register. */
317 pci_write_config16(dev, PCI_COMMAND, 0x000f);
318
319 /* IO APIC initialization. */
320 soc_enable_apic(dev);
321
322 soc_enable_serial_irqs(dev);
323
324 /* Setup the PIRQ. */
325 soc_pirq_init(dev);
326
327 /* Setup power options. */
328 soc_power_options(dev);
329
330 /* Initialize power management */
331 switch (soc_silicon_type()) {
332 case SOC_TYPE_RANGELEY:
333 break;
334 default:
335 printk(BIOS_DEBUG, "Unknown Chipset: 0x%04x\n", dev->device);
336 }
337
338 /* Initialize ISA DMA. */
339 isa_dma_init();
340
341 /* Initialize the High Precision Event Timers, if present. */
342 enable_hpet();
343
344 setup_i8259();
345
346 /* Interrupt 9 should be level triggered (SCI) */
347 i8259_configure_irq_trigger(9, 1);
348
349 soc_disable_smm_only_flashing(dev);
350}
351
352static void soc_lpc_read_resources(device_t dev)
353{
354 struct resource *res;
355 config_t *config = dev->chip_info;
356 u8 io_index = 0;
357
358 /* Get the normal PCI resources of this device. */
359 pci_dev_read_resources(dev);
360
361 /* Add an extra subtractive resource for both memory and I/O. */
362 res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
363 res->base = 0;
364 res->size = 0x1000;
365 res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
366 IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
367
368 res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
369 res->base = 0xff800000;
370 res->size = 0x00800000; /* 8 MB for flash */
371 res->flags = IORESOURCE_MEM | IORESOURCE_SUBTRACTIVE |
372 IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
373
374 res = new_resource(dev, 3); /* IOAPIC */
375 res->base = IO_APIC_ADDR;
376 res->size = 0x00001000;
377 res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
378
379 /* Set SOC IO decode ranges if required.*/
380 if ((config->gen1_dec & 0xFFFC) > 0x1000) {
381 res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
382 res->base = config->gen1_dec & 0xFFFC;
383 res->size = (config->gen1_dec >> 16) & 0xFC;
384 res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
385 IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
386 }
387
388 if ((config->gen2_dec & 0xFFFC) > 0x1000) {
389 res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
390 res->base = config->gen2_dec & 0xFFFC;
391 res->size = (config->gen2_dec >> 16) & 0xFC;
392 res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
393 IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
394 }
395
396 if ((config->gen3_dec & 0xFFFC) > 0x1000) {
397 res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
398 res->base = config->gen3_dec & 0xFFFC;
399 res->size = (config->gen3_dec >> 16) & 0xFC;
400 res->flags = IORESOURCE_IO | IORESOURCE_SUBTRACTIVE |
401 IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
402 }
403
404 if ((config->gen4_dec & 0xFFFC) > 0x1000) {
405 res = new_resource(dev, IOINDEX_SUBTRACTIVE(io_index++, 0));
406 res->base = config->gen4_dec & 0xFFFC;
407 res->size = (config->gen4_dec >> 16) & 0xFC;
408 res->flags = IORESOURCE_IO| IORESOURCE_SUBTRACTIVE |
409 IORESOURCE_ASSIGNED | IORESOURCE_FIXED;
410 }
411}
412
413static void soc_lpc_enable_resources(device_t dev)
414{
415 return pci_dev_enable_resources(dev);
416}
417
418static void soc_lpc_enable(device_t dev)
419{
420 soc_enable(dev);
421}
422
423static void set_subsystem(device_t dev, unsigned vendor, unsigned device)
424{
425 if (!vendor || !device) {
426 pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
427 pci_read_config32(dev, PCI_VENDOR_ID));
428 } else {
429 pci_write_config32(dev, PCI_SUBSYSTEM_VENDOR_ID,
430 ((device & 0xffff) << 16) | (vendor & 0xffff));
431 }
432}
433
Vladimir Serbinenko689ddf62014-10-11 11:25:41 +0200434static void southbridge_inject_dsdt(void)
435{
436 global_nvs_t *gnvs = cbmem_add (CBMEM_ID_ACPI_GNVS, sizeof (*gnvs));
437
438 if (gnvs) {
439 memset(gnvs, 0, sizeof(*gnvs));
440 acpi_create_gnvs(gnvs);
441 acpi_save_gnvs((unsigned long)gnvs);
442#if CONFIG_HAVE_SMI_HANDLER
443 /* And tell SMI about it */
444 smm_setup_structures(gnvs, NULL, NULL);
445#endif
446
447 /* Add it to DSDT. */
448 acpigen_write_scope("\\");
449 acpigen_write_name_dword("NVSA", (u32) gnvs);
450 acpigen_pop_len();
451 }
452}
453
Martin Roth829c41d2014-05-21 14:21:22 -0600454static struct pci_operations pci_ops = {
455 .set_subsystem = set_subsystem,
456};
457
458static struct device_operations device_ops = {
459 .read_resources = soc_lpc_read_resources,
460 .set_resources = pci_dev_set_resources,
461 .enable_resources = soc_lpc_enable_resources,
462 .init = lpc_init,
Vladimir Serbinenko689ddf62014-10-11 11:25:41 +0200463 .write_acpi_tables = acpi_write_hpet,
464 .acpi_inject_dsdt_generator = southbridge_inject_dsdt,
Martin Roth829c41d2014-05-21 14:21:22 -0600465 .enable = soc_lpc_enable,
466 .scan_bus = scan_static_bus,
467 .ops_pci = &pci_ops,
468};
469
470/* IDs for LPC device of Intel 89xx Series Chipset */
471static const unsigned short pci_device_ids[] = { 0x1F38, 0x1F39, 0x1F3A, 0x1F3B,
472 0 };
473
474static const struct pci_driver soc_lpc __pci_driver = {
475 .ops = &device_ops,
476 .vendor = PCI_VENDOR_ID_INTEL,
477 .devices = pci_device_ids,
478};