Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
| 3 | * |
| 4 | * Copyright (C) 2008-2009 coresystems GmbH |
| 5 | * Copyright (C) 2014 Google Inc. |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 6 | * Copyright (C) 2015 Intel Corporation. |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 7 | * |
| 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. |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 16 | */ |
| 17 | |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 18 | #include <arch/acpigen.h> |
| 19 | #include "chip.h" |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 20 | #include <console/console.h> |
| 21 | #include <delay.h> |
| 22 | #include <device/device.h> |
| 23 | #include <device/pci.h> |
| 24 | #include <device/pci_ids.h> |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 25 | #include <pc80/isa-dma.h> |
| 26 | #include <pc80/i8259.h> |
| 27 | #include <arch/io.h> |
| 28 | #include <arch/ioapic.h> |
| 29 | #include <arch/acpi.h> |
| 30 | #include <cpu/cpu.h> |
| 31 | #include <cpu/x86/smm.h> |
| 32 | #include <cbmem.h> |
| 33 | #include <reg_script.h> |
| 34 | #include <string.h> |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 35 | #include <soc/acpi.h> |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 36 | #include <soc/gpio.h> |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 37 | #include <soc/iomap.h> |
| 38 | #include <soc/lpc.h> |
| 39 | #include <soc/nvs.h> |
| 40 | #include <soc/pch.h> |
| 41 | #include <soc/pci_devs.h> |
| 42 | #include <soc/pm.h> |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 43 | #include <soc/pmc.h> |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 44 | #include <soc/ramstage.h> |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 45 | #include <soc/pcr.h> |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 46 | #if IS_ENABLED(CONFIG_CHROMEOS) |
| 47 | #include <vendorcode/google/chromeos/chromeos.h> |
| 48 | #endif |
| 49 | |
| 50 | static void pch_enable_ioapic(struct device *dev) |
| 51 | { |
| 52 | u32 reg32; |
Aaron Durbin | 416bf45 | 2015-08-13 09:05:22 -0500 | [diff] [blame] | 53 | /* PCH-LP has 120 redirection entries */ |
| 54 | const int redir_entries = 120; |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 55 | |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 56 | set_ioapic_id((void *)IO_APIC_ADDR, 0x02); |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 57 | |
| 58 | /* affirm full set of redirection table entries ("write once") */ |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 59 | reg32 = io_apic_read((void *)IO_APIC_ADDR, 0x01); |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 60 | |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 61 | reg32 &= ~0x00ff0000; |
Aaron Durbin | 416bf45 | 2015-08-13 09:05:22 -0500 | [diff] [blame] | 62 | reg32 |= (redir_entries - 1) << 16; |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 63 | |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 64 | io_apic_write((void *)IO_APIC_ADDR, 0x01, reg32); |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 65 | |
| 66 | /* |
| 67 | * Select Boot Configuration register (0x03) and |
| 68 | * use Processor System Bus (0x01) to deliver interrupts. |
| 69 | */ |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 70 | io_apic_write((void *)IO_APIC_ADDR, 0x03, 0x01); |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 71 | } |
| 72 | |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 73 | /* |
| 74 | * PIRQ[n]_ROUT[3:0] - PIRQ Routing Control |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 75 | * 0x00 - 0000 = Reserved |
| 76 | * 0x01 - 0001 = Reserved |
| 77 | * 0x02 - 0010 = Reserved |
| 78 | * 0x03 - 0011 = IRQ3 |
| 79 | * 0x04 - 0100 = IRQ4 |
| 80 | * 0x05 - 0101 = IRQ5 |
| 81 | * 0x06 - 0110 = IRQ6 |
| 82 | * 0x07 - 0111 = IRQ7 |
| 83 | * 0x08 - 1000 = Reserved |
| 84 | * 0x09 - 1001 = IRQ9 |
| 85 | * 0x0A - 1010 = IRQ10 |
| 86 | * 0x0B - 1011 = IRQ11 |
| 87 | * 0x0C - 1100 = IRQ12 |
| 88 | * 0x0D - 1101 = Reserved |
| 89 | * 0x0E - 1110 = IRQ14 |
| 90 | * 0x0F - 1111 = IRQ15 |
| 91 | * PIRQ[n]_ROUT[7] - PIRQ Routing Control |
| 92 | * 0x80 - The PIRQ is not routed. |
| 93 | */ |
| 94 | |
| 95 | static void pch_pirq_init(device_t dev) |
| 96 | { |
| 97 | device_t irq_dev; |
| 98 | config_t *config = dev->chip_info; |
| 99 | |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 100 | pcr_write8(PID_ITSS, R_PCH_PCR_ITSS_PIRQA_ROUT, config->pirqa_routing); |
| 101 | pcr_write8(PID_ITSS, R_PCH_PCR_ITSS_PIRQB_ROUT, config->pirqb_routing); |
| 102 | pcr_write8(PID_ITSS, R_PCH_PCR_ITSS_PIRQC_ROUT, config->pirqc_routing); |
| 103 | pcr_write8(PID_ITSS, R_PCH_PCR_ITSS_PIRQD_ROUT, config->pirqd_routing); |
| 104 | pcr_write8(PID_ITSS, R_PCH_PCR_ITSS_PIRQE_ROUT, config->pirqe_routing); |
| 105 | pcr_write8(PID_ITSS, R_PCH_PCR_ITSS_PIRQF_ROUT, config->pirqf_routing); |
| 106 | pcr_write8(PID_ITSS, R_PCH_PCR_ITSS_PIRQG_ROUT, config->pirqg_routing); |
| 107 | pcr_write8(PID_ITSS, R_PCH_PCR_ITSS_PIRQH_ROUT, config->pirqh_routing); |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 108 | |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 109 | for (irq_dev = all_devices; irq_dev; irq_dev = irq_dev->next) { |
| 110 | u8 int_pin = 0, int_line = 0; |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 111 | |
| 112 | if (!irq_dev->enabled || irq_dev->path.type != DEVICE_PATH_PCI) |
| 113 | continue; |
| 114 | |
| 115 | int_pin = pci_read_config8(irq_dev, PCI_INTERRUPT_PIN); |
| 116 | |
| 117 | switch (int_pin) { |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 118 | case 1: /* INTA# */ |
| 119 | int_line = config->pirqa_routing; |
| 120 | break; |
| 121 | case 2: /* INTB# */ |
| 122 | int_line = config->pirqb_routing; |
| 123 | break; |
| 124 | case 3: /* INTC# */ |
| 125 | int_line = config->pirqc_routing; |
| 126 | break; |
| 127 | case 4: /* INTD# */ |
| 128 | int_line = config->pirqd_routing; |
| 129 | break; |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | if (!int_line) |
| 133 | continue; |
| 134 | |
| 135 | pci_write_config8(irq_dev, PCI_INTERRUPT_LINE, int_line); |
| 136 | } |
| 137 | } |
| 138 | |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 139 | |
| 140 | static const struct reg_script pch_misc_init_script[] = { |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 141 | /* Setup NMI on errors, disable SERR */ |
| 142 | REG_IO_RMW8(0x61, ~0xf0, (1 << 2)), |
| 143 | /* Disable NMI sources */ |
| 144 | REG_IO_OR8(0x70, (1 << 7)), |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 145 | /* Enable BIOS updates outside of SMM */ |
| 146 | REG_PCI_RMW8(0xdc, ~(1 << 5), 0), |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 147 | /* Setup SERIRQ, enable continuous mode */ |
| 148 | REG_PCI_OR8(SERIRQ_CNTL, (1 << 7) | (1 << 6)), |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 149 | #if !IS_ENABLED(CONFIG_SERIRQ_CONTINUOUS_MODE) |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 150 | REG_PCI_RMW8(SERIRQ_CNTL, ~(1 << 6), 0), |
| 151 | #endif |
pchandri | 1d77c72 | 2015-09-09 17:22:09 -0700 | [diff] [blame] | 152 | /* Enable CLKRUN_EN for power gating LPC */ |
| 153 | REG_PCI_OR8(PCCTL, (CLKRUN_EN)), |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 154 | REG_SCRIPT_END |
| 155 | }; |
| 156 | |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 157 | static void lpc_init(struct device *dev) |
| 158 | { |
| 159 | /* Legacy initialization */ |
| 160 | isa_dma_init(); |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 161 | reg_script_run_on_dev(dev, pch_misc_init_script); |
| 162 | |
| 163 | /* Interrupt configuration */ |
| 164 | pch_enable_ioapic(dev); |
| 165 | pch_pirq_init(dev); |
| 166 | setup_i8259(); |
| 167 | i8259_configure_irq_trigger(9, 1); |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 168 | } |
| 169 | |
| 170 | static void pch_lpc_add_mmio_resources(device_t dev) |
| 171 | { |
| 172 | u32 reg; |
| 173 | struct resource *res; |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 174 | /* |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 175 | * As per the BWG, Chapter 5.9.1. "PCH BIOS component will reserve |
| 176 | * certain memory range as reserved range for BIOS usage. |
| 177 | * For this SOC, the range will be from 0FD000000h till FE7FFFFFh" |
| 178 | * Hence, use FD000000h as PCR_BASE |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 179 | */ |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 180 | const u32 default_decode_base = PCH_PCR_BASE_ADDRESS; |
| 181 | |
| 182 | res = new_resource(dev, PCI_BASE_ADDRESS_0); |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 183 | res->base = default_decode_base; |
| 184 | res->size = 0 - default_decode_base; |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 185 | res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | |
| 186 | IORESOURCE_FIXED | IORESOURCE_RESERVE; |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 187 | |
| 188 | /* Check LPC Memory Decode register. */ |
| 189 | reg = pci_read_config32(dev, LGMR); |
| 190 | if (reg & 1) { |
| 191 | reg &= ~0xffff; |
| 192 | if (reg < default_decode_base) { |
| 193 | res = new_resource(dev, LGMR); |
| 194 | res->base = reg; |
| 195 | res->size = 16 * 1024; |
| 196 | res->flags = IORESOURCE_MEM | IORESOURCE_ASSIGNED | |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 197 | IORESOURCE_FIXED | IORESOURCE_RESERVE; |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 198 | } |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | /* Default IO range claimed by the LPC device. The upper bound is exclusive. */ |
| 203 | #define LPC_DEFAULT_IO_RANGE_LOWER 0 |
| 204 | #define LPC_DEFAULT_IO_RANGE_UPPER 0x1000 |
| 205 | |
| 206 | static inline int pch_io_range_in_default(u16 base, u16 size) |
| 207 | { |
| 208 | /* Does it start above the range? */ |
| 209 | if (base >= LPC_DEFAULT_IO_RANGE_UPPER) |
| 210 | return 0; |
| 211 | |
| 212 | /* Is it entirely contained? */ |
| 213 | if (base >= LPC_DEFAULT_IO_RANGE_LOWER && |
| 214 | (base + size) < LPC_DEFAULT_IO_RANGE_UPPER) |
| 215 | return 1; |
| 216 | |
| 217 | /* This will return not in range for partial overlaps. */ |
| 218 | return 0; |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | * Note: this function assumes there is no overlap with the default LPC device's |
| 223 | * claimed range: LPC_DEFAULT_IO_RANGE_LOWER -> LPC_DEFAULT_IO_RANGE_UPPER. |
| 224 | */ |
| 225 | static void pch_lpc_add_io_resource(device_t dev, u16 base, u16 size, int index) |
| 226 | { |
| 227 | struct resource *res; |
| 228 | |
| 229 | if (pch_io_range_in_default(base, size)) |
| 230 | return; |
| 231 | |
| 232 | res = new_resource(dev, index); |
| 233 | res->base = base; |
| 234 | res->size = size; |
| 235 | res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; |
| 236 | } |
| 237 | |
| 238 | static void pch_lpc_add_gen_io_resources(device_t dev, int reg_value, int index) |
| 239 | { |
| 240 | /* |
| 241 | * Check if the register is enabled. If so and the base exceeds the |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 242 | * device's deafult claim range add the resoure. |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 243 | */ |
| 244 | if (reg_value & 1) { |
| 245 | u16 base = reg_value & 0xfffc; |
| 246 | u16 size = (0x3 | ((reg_value >> 16) & 0xfc)) + 1; |
| 247 | pch_lpc_add_io_resource(dev, base, size, index); |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | static void pch_lpc_add_io_resources(device_t dev) |
| 252 | { |
| 253 | struct resource *res; |
| 254 | config_t *config = dev->chip_info; |
| 255 | |
| 256 | /* Add the default claimed IO range for the LPC device. */ |
| 257 | res = new_resource(dev, 0); |
| 258 | res->base = LPC_DEFAULT_IO_RANGE_LOWER; |
| 259 | res->size = LPC_DEFAULT_IO_RANGE_UPPER - LPC_DEFAULT_IO_RANGE_LOWER; |
| 260 | res->flags = IORESOURCE_IO | IORESOURCE_ASSIGNED | IORESOURCE_FIXED; |
| 261 | |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 262 | /* LPC Generic IO Decode range. */ |
| 263 | pch_lpc_add_gen_io_resources(dev, config->gen1_dec, LPC_GEN1_DEC); |
| 264 | pch_lpc_add_gen_io_resources(dev, config->gen2_dec, LPC_GEN2_DEC); |
| 265 | pch_lpc_add_gen_io_resources(dev, config->gen3_dec, LPC_GEN3_DEC); |
| 266 | pch_lpc_add_gen_io_resources(dev, config->gen4_dec, LPC_GEN4_DEC); |
| 267 | } |
| 268 | |
| 269 | static void pch_lpc_read_resources(device_t dev) |
| 270 | { |
| 271 | global_nvs_t *gnvs; |
| 272 | |
| 273 | /* Get the normal PCI resources of this device. */ |
| 274 | pci_dev_read_resources(dev); |
| 275 | |
| 276 | /* Add non-standard MMIO resources. */ |
| 277 | pch_lpc_add_mmio_resources(dev); |
| 278 | |
| 279 | /* Add IO resources. */ |
| 280 | pch_lpc_add_io_resources(dev); |
| 281 | |
| 282 | /* Allocate ACPI NVS in CBMEM */ |
| 283 | gnvs = cbmem_add(CBMEM_ID_ACPI_GNVS, sizeof(global_nvs_t)); |
| 284 | if (!acpi_is_wakeup_s3() && gnvs) |
| 285 | memset(gnvs, 0, sizeof(global_nvs_t)); |
| 286 | } |
| 287 | |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 288 | static struct device_operations device_ops = { |
| 289 | .read_resources = &pch_lpc_read_resources, |
| 290 | .set_resources = &pci_dev_set_resources, |
| 291 | .enable_resources = &pci_dev_enable_resources, |
| 292 | .acpi_inject_dsdt_generator = southcluster_inject_dsdt, |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 293 | .write_acpi_tables = southcluster_write_acpi_tables, |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 294 | .init = &lpc_init, |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 295 | .scan_bus = &scan_lpc_bus, |
| 296 | .ops_pci = &soc_pci_ops, |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 297 | }; |
| 298 | |
| 299 | static const unsigned short pci_device_ids[] = { |
Lee Leahy | 1d14b3e | 2015-05-12 18:23:27 -0700 | [diff] [blame] | 300 | PCH_SPT_LP_SAMPLE, |
| 301 | PCH_SPT_LP_U_BASE, |
| 302 | PCH_SPT_LP_U_PREMIUM, |
| 303 | PCH_SPT_LP_Y_PREMIUM, |
Lee Leahy | b000513 | 2015-05-12 18:19:47 -0700 | [diff] [blame] | 304 | 0 |
| 305 | }; |
| 306 | |
| 307 | static const struct pci_driver pch_lpc __pci_driver = { |
| 308 | .ops = &device_ops, |
| 309 | .vendor = PCI_VENDOR_ID_INTEL, |
| 310 | .devices = pci_device_ids, |
| 311 | }; |