Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 1 | /* Various workarounds for chipset bugs. |
| 2 | This code runs very early and can't use the regular PCI subsystem |
| 3 | The entries are keyed to PCI bridges which usually identify chipsets |
| 4 | uniquely. |
| 5 | This is only for whole classes of chipsets with specific problems which |
| 6 | need early invasive action (e.g. before the timers are initialized). |
| 7 | Most PCI device specific workarounds can be done later and should be |
| 8 | in standard PCI quirks |
| 9 | Mainboard specific bugs should be handled by DMI entries. |
| 10 | CPU specific bugs in setup.c */ |
| 11 | |
| 12 | #include <linux/pci.h> |
| 13 | #include <linux/acpi.h> |
Lukas Wunner | abb2baf | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 14 | #include <linux/delay.h> |
| 15 | #include <linux/dmi.h> |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 16 | #include <linux/pci_ids.h> |
Lukas Wunner | abb2baf | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 17 | #include <linux/bcma/bcma.h> |
| 18 | #include <linux/bcma/bcma_regs.h> |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 19 | #include <drm/i915_drm.h> |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 20 | #include <asm/pci-direct.h> |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 21 | #include <asm/dma.h> |
Andi Kleen | 54ef340 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 22 | #include <asm/io_apic.h> |
| 23 | #include <asm/apic.h> |
Feng Tang | 6218791 | 2014-04-24 16:18:18 +0800 | [diff] [blame] | 24 | #include <asm/hpet.h> |
FUJITA Tomonori | 46a7fa2 | 2008-07-11 10:23:42 +0900 | [diff] [blame] | 25 | #include <asm/iommu.h> |
Joerg Roedel | 1d9b16d | 2008-11-27 18:39:15 +0100 | [diff] [blame] | 26 | #include <asm/gart.h> |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 27 | #include <asm/irq_remapping.h> |
Lukas Wunner | abb2baf | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 28 | #include <asm/early_ioremap.h> |
| 29 | |
| 30 | #define dev_err(msg) pr_err("pci 0000:%02x:%02x.%d: %s", bus, slot, func, msg) |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 31 | |
Neil Horman | c6b4832 | 2008-01-30 13:31:25 +0100 | [diff] [blame] | 32 | static void __init fix_hypertransport_config(int num, int slot, int func) |
| 33 | { |
| 34 | u32 htcfg; |
| 35 | /* |
| 36 | * we found a hypertransport bus |
| 37 | * make sure that we are broadcasting |
| 38 | * interrupts to all cpus on the ht bus |
| 39 | * if we're using extended apic ids |
| 40 | */ |
| 41 | htcfg = read_pci_config(num, slot, func, 0x68); |
| 42 | if (htcfg & (1 << 18)) { |
Neil Horman | 7bcbc78 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 43 | printk(KERN_INFO "Detected use of extended apic ids " |
| 44 | "on hypertransport bus\n"); |
Neil Horman | c6b4832 | 2008-01-30 13:31:25 +0100 | [diff] [blame] | 45 | if ((htcfg & (1 << 17)) == 0) { |
Neil Horman | 7bcbc78 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 46 | printk(KERN_INFO "Enabling hypertransport extended " |
| 47 | "apic interrupt broadcast\n"); |
| 48 | printk(KERN_INFO "Note this is a bios bug, " |
| 49 | "please contact your hw vendor\n"); |
Neil Horman | c6b4832 | 2008-01-30 13:31:25 +0100 | [diff] [blame] | 50 | htcfg |= (1 << 17); |
| 51 | write_pci_config(num, slot, func, 0x68, htcfg); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | |
| 56 | } |
| 57 | |
| 58 | static void __init via_bugs(int num, int slot, int func) |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 59 | { |
Joerg Roedel | 966396d | 2007-10-24 12:49:48 +0200 | [diff] [blame] | 60 | #ifdef CONFIG_GART_IOMMU |
Yinghai Lu | c987d12 | 2008-06-24 22:14:09 -0700 | [diff] [blame] | 61 | if ((max_pfn > MAX_DMA32_PFN || force_iommu) && |
Joerg Roedel | 0440d4c | 2007-10-24 12:49:50 +0200 | [diff] [blame] | 62 | !gart_iommu_aperture_allowed) { |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 63 | printk(KERN_INFO |
Andi Kleen | 54ef340 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 64 | "Looks like a VIA chipset. Disabling IOMMU." |
| 65 | " Override with iommu=allowed\n"); |
Joerg Roedel | 0440d4c | 2007-10-24 12:49:50 +0200 | [diff] [blame] | 66 | gart_iommu_aperture_disabled = 1; |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 67 | } |
| 68 | #endif |
| 69 | } |
| 70 | |
| 71 | #ifdef CONFIG_ACPI |
Jeff Garzik | 03d0d20 | 2007-10-27 20:57:43 +0200 | [diff] [blame] | 72 | #ifdef CONFIG_X86_IO_APIC |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 73 | |
Alexey Starikovskiy | 15a58ed | 2007-02-02 19:48:22 +0300 | [diff] [blame] | 74 | static int __init nvidia_hpet_check(struct acpi_table_header *header) |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 75 | { |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 76 | return 0; |
| 77 | } |
Jeff Garzik | 03d0d20 | 2007-10-27 20:57:43 +0200 | [diff] [blame] | 78 | #endif /* CONFIG_X86_IO_APIC */ |
| 79 | #endif /* CONFIG_ACPI */ |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 80 | |
Neil Horman | c6b4832 | 2008-01-30 13:31:25 +0100 | [diff] [blame] | 81 | static void __init nvidia_bugs(int num, int slot, int func) |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 82 | { |
| 83 | #ifdef CONFIG_ACPI |
Andi Kleen | 54ef340 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 84 | #ifdef CONFIG_X86_IO_APIC |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 85 | /* |
Lukas Wunner | 447d29d | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 86 | * Only applies to Nvidia root ports (bus 0) and not to |
| 87 | * Nvidia graphics cards with PCI ports on secondary buses. |
| 88 | */ |
| 89 | if (num) |
| 90 | return; |
| 91 | |
| 92 | /* |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 93 | * All timer overrides on Nvidia are |
| 94 | * wrong unless HPET is enabled. |
Andi Kleen | fa18f47 | 2006-11-14 16:57:46 +0100 | [diff] [blame] | 95 | * Unfortunately that's not true on many Asus boards. |
| 96 | * We don't know yet how to detect this automatically, but |
| 97 | * at least allow a command line override. |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 98 | */ |
Andi Kleen | fa18f47 | 2006-11-14 16:57:46 +0100 | [diff] [blame] | 99 | if (acpi_use_timer_override) |
| 100 | return; |
| 101 | |
Len Brown | fe69933 | 2007-03-08 18:28:32 -0500 | [diff] [blame] | 102 | if (acpi_table_parse(ACPI_SIG_HPET, nvidia_hpet_check)) { |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 103 | acpi_skip_timer_override = 1; |
| 104 | printk(KERN_INFO "Nvidia board " |
| 105 | "detected. Ignoring ACPI " |
| 106 | "timer override.\n"); |
Andi Kleen | fa18f47 | 2006-11-14 16:57:46 +0100 | [diff] [blame] | 107 | printk(KERN_INFO "If you got timer trouble " |
| 108 | "try acpi_use_timer_override\n"); |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 109 | } |
| 110 | #endif |
Andi Kleen | 54ef340 | 2007-10-19 20:35:03 +0200 | [diff] [blame] | 111 | #endif |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 112 | /* RED-PEN skip them on mptables too? */ |
| 113 | |
| 114 | } |
| 115 | |
Andreas Herrmann | 26adcfb | 2008-10-14 21:01:15 +0200 | [diff] [blame] | 116 | #if defined(CONFIG_ACPI) && defined(CONFIG_X86_IO_APIC) |
| 117 | static u32 __init ati_ixp4x0_rev(int num, int slot, int func) |
Andreas Herrmann | 33fb0e4 | 2008-10-07 00:11:22 +0200 | [diff] [blame] | 118 | { |
| 119 | u32 d; |
| 120 | u8 b; |
| 121 | |
| 122 | b = read_pci_config_byte(num, slot, func, 0xac); |
| 123 | b &= ~(1<<5); |
| 124 | write_pci_config_byte(num, slot, func, 0xac, b); |
| 125 | |
| 126 | d = read_pci_config(num, slot, func, 0x70); |
| 127 | d |= 1<<8; |
| 128 | write_pci_config(num, slot, func, 0x70, d); |
| 129 | |
| 130 | d = read_pci_config(num, slot, func, 0x8); |
| 131 | d &= 0xff; |
| 132 | return d; |
| 133 | } |
| 134 | |
| 135 | static void __init ati_bugs(int num, int slot, int func) |
| 136 | { |
Andreas Herrmann | 33fb0e4 | 2008-10-07 00:11:22 +0200 | [diff] [blame] | 137 | u32 d; |
| 138 | u8 b; |
| 139 | |
| 140 | if (acpi_use_timer_override) |
| 141 | return; |
| 142 | |
| 143 | d = ati_ixp4x0_rev(num, slot, func); |
| 144 | if (d < 0x82) |
| 145 | acpi_skip_timer_override = 1; |
| 146 | else { |
| 147 | /* check for IRQ0 interrupt swap */ |
| 148 | outb(0x72, 0xcd6); b = inb(0xcd7); |
| 149 | if (!(b & 0x2)) |
| 150 | acpi_skip_timer_override = 1; |
| 151 | } |
| 152 | |
| 153 | if (acpi_skip_timer_override) { |
| 154 | printk(KERN_INFO "SB4X0 revision 0x%x\n", d); |
| 155 | printk(KERN_INFO "Ignoring ACPI timer override.\n"); |
| 156 | printk(KERN_INFO "If you got timer trouble " |
| 157 | "try acpi_use_timer_override\n"); |
| 158 | } |
Andreas Herrmann | 33fb0e4 | 2008-10-07 00:11:22 +0200 | [diff] [blame] | 159 | } |
| 160 | |
Andreas Herrmann | 26adcfb | 2008-10-14 21:01:15 +0200 | [diff] [blame] | 161 | static u32 __init ati_sbx00_rev(int num, int slot, int func) |
| 162 | { |
Andreas Herrmann | 7f74f8f | 2011-02-24 15:53:46 +0100 | [diff] [blame] | 163 | u32 d; |
Andreas Herrmann | 26adcfb | 2008-10-14 21:01:15 +0200 | [diff] [blame] | 164 | |
Andreas Herrmann | 26adcfb | 2008-10-14 21:01:15 +0200 | [diff] [blame] | 165 | d = read_pci_config(num, slot, func, 0x8); |
| 166 | d &= 0xff; |
Andreas Herrmann | 26adcfb | 2008-10-14 21:01:15 +0200 | [diff] [blame] | 167 | |
| 168 | return d; |
| 169 | } |
| 170 | |
| 171 | static void __init ati_bugs_contd(int num, int slot, int func) |
| 172 | { |
| 173 | u32 d, rev; |
| 174 | |
Andreas Herrmann | 7f74f8f | 2011-02-24 15:53:46 +0100 | [diff] [blame] | 175 | rev = ati_sbx00_rev(num, slot, func); |
| 176 | if (rev >= 0x40) |
| 177 | acpi_fix_pin2_polarity = 1; |
| 178 | |
Andreas Herrmann | 1d3e09a | 2011-03-15 15:31:37 +0100 | [diff] [blame] | 179 | /* |
| 180 | * SB600: revisions 0x11, 0x12, 0x13, 0x14, ... |
| 181 | * SB700: revisions 0x39, 0x3a, ... |
| 182 | * SB800: revisions 0x40, 0x41, ... |
| 183 | */ |
| 184 | if (rev >= 0x39) |
Andreas Herrmann | 26adcfb | 2008-10-14 21:01:15 +0200 | [diff] [blame] | 185 | return; |
| 186 | |
Andreas Herrmann | 7f74f8f | 2011-02-24 15:53:46 +0100 | [diff] [blame] | 187 | if (acpi_use_timer_override) |
Andreas Herrmann | 26adcfb | 2008-10-14 21:01:15 +0200 | [diff] [blame] | 188 | return; |
| 189 | |
| 190 | /* check for IRQ0 interrupt swap */ |
| 191 | d = read_pci_config(num, slot, func, 0x64); |
| 192 | if (!(d & (1<<14))) |
| 193 | acpi_skip_timer_override = 1; |
| 194 | |
| 195 | if (acpi_skip_timer_override) { |
| 196 | printk(KERN_INFO "SB600 revision 0x%x\n", rev); |
| 197 | printk(KERN_INFO "Ignoring ACPI timer override.\n"); |
| 198 | printk(KERN_INFO "If you got timer trouble " |
| 199 | "try acpi_use_timer_override\n"); |
| 200 | } |
| 201 | } |
| 202 | #else |
| 203 | static void __init ati_bugs(int num, int slot, int func) |
| 204 | { |
| 205 | } |
| 206 | |
| 207 | static void __init ati_bugs_contd(int num, int slot, int func) |
| 208 | { |
| 209 | } |
| 210 | #endif |
| 211 | |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 212 | static void __init intel_remapping_check(int num, int slot, int func) |
| 213 | { |
| 214 | u8 revision; |
Neil Horman | 803075d | 2013-07-17 07:13:59 -0400 | [diff] [blame] | 215 | u16 device; |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 216 | |
Neil Horman | 803075d | 2013-07-17 07:13:59 -0400 | [diff] [blame] | 217 | device = read_pci_config_16(num, slot, func, PCI_DEVICE_ID); |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 218 | revision = read_pci_config_byte(num, slot, func, PCI_REVISION_ID); |
| 219 | |
| 220 | /* |
Neil Horman | 6f8a1b3 | 2014-03-12 14:44:33 -0400 | [diff] [blame] | 221 | * Revision <= 13 of all triggering devices id in this quirk |
| 222 | * have a problem draining interrupts when irq remapping is |
| 223 | * enabled, and should be flagged as broken. Additionally |
| 224 | * revision 0x22 of device id 0x3405 has this problem. |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 225 | */ |
Neil Horman | 6f8a1b3 | 2014-03-12 14:44:33 -0400 | [diff] [blame] | 226 | if (revision <= 0x13) |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 227 | set_irq_remapping_broken(); |
Neil Horman | 6f8a1b3 | 2014-03-12 14:44:33 -0400 | [diff] [blame] | 228 | else if (device == 0x3405 && revision == 0x22) |
Neil Horman | 803075d | 2013-07-17 07:13:59 -0400 | [diff] [blame] | 229 | set_irq_remapping_broken(); |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 230 | } |
| 231 | |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 232 | /* |
| 233 | * Systems with Intel graphics controllers set aside memory exclusively |
| 234 | * for gfx driver use. This memory is not marked in the E820 as reserved |
| 235 | * or as RAM, and so is subject to overlap from E820 manipulation later |
| 236 | * in the boot process. On some systems, MMIO space is allocated on top, |
| 237 | * despite the efforts of the "RAM buffer" approach, which simply rounds |
| 238 | * memory boundaries up to 64M to try to catch space that may decode |
| 239 | * as RAM and so is not suitable for MMIO. |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 240 | */ |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 241 | |
Ville Syrjälä | 86e5876 | 2014-04-13 12:45:03 +0300 | [diff] [blame] | 242 | #define KB(x) ((x) * 1024UL) |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 243 | #define MB(x) (KB (KB (x))) |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 244 | |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 245 | static size_t __init i830_tseg_size(void) |
| 246 | { |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 247 | u8 esmramc = read_pci_config_byte(0, 0, 0, I830_ESMRAMC); |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 248 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 249 | if (!(esmramc & TSEG_ENABLE)) |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 250 | return 0; |
| 251 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 252 | if (esmramc & I830_TSEG_SIZE_1M) |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 253 | return MB(1); |
| 254 | else |
| 255 | return KB(512); |
| 256 | } |
| 257 | |
| 258 | static size_t __init i845_tseg_size(void) |
| 259 | { |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 260 | u8 esmramc = read_pci_config_byte(0, 0, 0, I845_ESMRAMC); |
| 261 | u8 tseg_size = esmramc & I845_TSEG_SIZE_MASK; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 262 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 263 | if (!(esmramc & TSEG_ENABLE)) |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 264 | return 0; |
| 265 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 266 | switch (tseg_size) { |
| 267 | case I845_TSEG_SIZE_512K: return KB(512); |
| 268 | case I845_TSEG_SIZE_1M: return MB(1); |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 269 | default: |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 270 | WARN(1, "Unknown ESMRAMC value: %x!\n", esmramc); |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 271 | } |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 272 | return 0; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 273 | } |
| 274 | |
| 275 | static size_t __init i85x_tseg_size(void) |
| 276 | { |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 277 | u8 esmramc = read_pci_config_byte(0, 0, 0, I85X_ESMRAMC); |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 278 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 279 | if (!(esmramc & TSEG_ENABLE)) |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 280 | return 0; |
| 281 | |
| 282 | return MB(1); |
| 283 | } |
| 284 | |
| 285 | static size_t __init i830_mem_size(void) |
| 286 | { |
| 287 | return read_pci_config_byte(0, 0, 0, I830_DRB3) * MB(32); |
| 288 | } |
| 289 | |
| 290 | static size_t __init i85x_mem_size(void) |
| 291 | { |
| 292 | return read_pci_config_byte(0, 0, 1, I85X_DRB3) * MB(32); |
| 293 | } |
| 294 | |
| 295 | /* |
| 296 | * On 830/845/85x the stolen memory base isn't available in any |
| 297 | * register. We need to calculate it as TOM-TSEG_SIZE-stolen_size. |
| 298 | */ |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 299 | static phys_addr_t __init i830_stolen_base(int num, int slot, int func, |
| 300 | size_t stolen_size) |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 301 | { |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 302 | return (phys_addr_t)i830_mem_size() - i830_tseg_size() - stolen_size; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 303 | } |
| 304 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 305 | static phys_addr_t __init i845_stolen_base(int num, int slot, int func, |
| 306 | size_t stolen_size) |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 307 | { |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 308 | return (phys_addr_t)i830_mem_size() - i845_tseg_size() - stolen_size; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 309 | } |
| 310 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 311 | static phys_addr_t __init i85x_stolen_base(int num, int slot, int func, |
| 312 | size_t stolen_size) |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 313 | { |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 314 | return (phys_addr_t)i85x_mem_size() - i85x_tseg_size() - stolen_size; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 315 | } |
| 316 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 317 | static phys_addr_t __init i865_stolen_base(int num, int slot, int func, |
| 318 | size_t stolen_size) |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 319 | { |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 320 | u16 toud; |
| 321 | |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 322 | /* |
| 323 | * FIXME is the graphics stolen memory region |
| 324 | * always at TOUD? Ie. is it always the last |
| 325 | * one to be allocated by the BIOS? |
| 326 | */ |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 327 | toud = read_pci_config_16(0, 0, 0, I865_TOUD); |
| 328 | |
| 329 | return (phys_addr_t)toud << 16; |
| 330 | } |
| 331 | |
| 332 | static phys_addr_t __init gen3_stolen_base(int num, int slot, int func, |
| 333 | size_t stolen_size) |
| 334 | { |
| 335 | u32 bsm; |
| 336 | |
| 337 | /* Almost universally we can find the Graphics Base of Stolen Memory |
| 338 | * at register BSM (0x5c) in the igfx configuration space. On a few |
| 339 | * (desktop) machines this is also mirrored in the bridge device at |
| 340 | * different locations, or in the MCHBAR. |
| 341 | */ |
| 342 | bsm = read_pci_config(num, slot, func, INTEL_BSM); |
| 343 | |
| 344 | return (phys_addr_t)bsm & INTEL_BSM_MASK; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 345 | } |
| 346 | |
| 347 | static size_t __init i830_stolen_size(int num, int slot, int func) |
| 348 | { |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 349 | u16 gmch_ctrl; |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 350 | u16 gms; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 351 | |
| 352 | gmch_ctrl = read_pci_config_16(0, 0, 0, I830_GMCH_CTRL); |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 353 | gms = gmch_ctrl & I830_GMCH_GMS_MASK; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 354 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 355 | switch (gms) { |
| 356 | case I830_GMCH_GMS_STOLEN_512: return KB(512); |
| 357 | case I830_GMCH_GMS_STOLEN_1024: return MB(1); |
| 358 | case I830_GMCH_GMS_STOLEN_8192: return MB(8); |
| 359 | /* local memory isn't part of the normal address space */ |
| 360 | case I830_GMCH_GMS_LOCAL: return 0; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 361 | default: |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 362 | WARN(1, "Unknown GMCH_CTRL value: %x!\n", gmch_ctrl); |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 363 | } |
| 364 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 365 | return 0; |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 366 | } |
| 367 | |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 368 | static size_t __init gen3_stolen_size(int num, int slot, int func) |
| 369 | { |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 370 | u16 gmch_ctrl; |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 371 | u16 gms; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 372 | |
| 373 | gmch_ctrl = read_pci_config_16(0, 0, 0, I830_GMCH_CTRL); |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 374 | gms = gmch_ctrl & I855_GMCH_GMS_MASK; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 375 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 376 | switch (gms) { |
| 377 | case I855_GMCH_GMS_STOLEN_1M: return MB(1); |
| 378 | case I855_GMCH_GMS_STOLEN_4M: return MB(4); |
| 379 | case I855_GMCH_GMS_STOLEN_8M: return MB(8); |
| 380 | case I855_GMCH_GMS_STOLEN_16M: return MB(16); |
| 381 | case I855_GMCH_GMS_STOLEN_32M: return MB(32); |
| 382 | case I915_GMCH_GMS_STOLEN_48M: return MB(48); |
| 383 | case I915_GMCH_GMS_STOLEN_64M: return MB(64); |
| 384 | case G33_GMCH_GMS_STOLEN_128M: return MB(128); |
| 385 | case G33_GMCH_GMS_STOLEN_256M: return MB(256); |
| 386 | case INTEL_GMCH_GMS_STOLEN_96M: return MB(96); |
| 387 | case INTEL_GMCH_GMS_STOLEN_160M:return MB(160); |
| 388 | case INTEL_GMCH_GMS_STOLEN_224M:return MB(224); |
| 389 | case INTEL_GMCH_GMS_STOLEN_352M:return MB(352); |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 390 | default: |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 391 | WARN(1, "Unknown GMCH_CTRL value: %x!\n", gmch_ctrl); |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 392 | } |
| 393 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 394 | return 0; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 395 | } |
| 396 | |
| 397 | static size_t __init gen6_stolen_size(int num, int slot, int func) |
| 398 | { |
| 399 | u16 gmch_ctrl; |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 400 | u16 gms; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 401 | |
| 402 | gmch_ctrl = read_pci_config_16(num, slot, func, SNB_GMCH_CTRL); |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 403 | gms = (gmch_ctrl >> SNB_GMCH_GMS_SHIFT) & SNB_GMCH_GMS_MASK; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 404 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 405 | return (size_t)gms * MB(32); |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 406 | } |
| 407 | |
Ville Syrjälä | 36dfcea | 2014-05-08 22:19:42 +0300 | [diff] [blame] | 408 | static size_t __init gen8_stolen_size(int num, int slot, int func) |
Ben Widawsky | 9459d25 | 2013-11-03 16:53:55 -0800 | [diff] [blame] | 409 | { |
| 410 | u16 gmch_ctrl; |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 411 | u16 gms; |
Ben Widawsky | 9459d25 | 2013-11-03 16:53:55 -0800 | [diff] [blame] | 412 | |
| 413 | gmch_ctrl = read_pci_config_16(num, slot, func, SNB_GMCH_CTRL); |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 414 | gms = (gmch_ctrl >> BDW_GMCH_GMS_SHIFT) & BDW_GMCH_GMS_MASK; |
| 415 | |
| 416 | return (size_t)gms * MB(32); |
Ben Widawsky | 9459d25 | 2013-11-03 16:53:55 -0800 | [diff] [blame] | 417 | } |
| 418 | |
Damien Lespiau | 3e3b2c3 | 2014-05-08 22:19:41 +0300 | [diff] [blame] | 419 | static size_t __init chv_stolen_size(int num, int slot, int func) |
| 420 | { |
| 421 | u16 gmch_ctrl; |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 422 | u16 gms; |
Damien Lespiau | 3e3b2c3 | 2014-05-08 22:19:41 +0300 | [diff] [blame] | 423 | |
| 424 | gmch_ctrl = read_pci_config_16(num, slot, func, SNB_GMCH_CTRL); |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 425 | gms = (gmch_ctrl >> SNB_GMCH_GMS_SHIFT) & SNB_GMCH_GMS_MASK; |
Damien Lespiau | 3e3b2c3 | 2014-05-08 22:19:41 +0300 | [diff] [blame] | 426 | |
| 427 | /* |
| 428 | * 0x0 to 0x10: 32MB increments starting at 0MB |
| 429 | * 0x11 to 0x16: 4MB increments starting at 8MB |
| 430 | * 0x17 to 0x1d: 4MB increments start at 36MB |
| 431 | */ |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 432 | if (gms < 0x11) |
| 433 | return (size_t)gms * MB(32); |
| 434 | else if (gms < 0x17) |
| 435 | return (size_t)(gms - 0x11 + 2) * MB(4); |
Damien Lespiau | 3e3b2c3 | 2014-05-08 22:19:41 +0300 | [diff] [blame] | 436 | else |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 437 | return (size_t)(gms - 0x17 + 9) * MB(4); |
Damien Lespiau | 3e3b2c3 | 2014-05-08 22:19:41 +0300 | [diff] [blame] | 438 | } |
Ville Syrjälä | 52ca704 | 2014-02-05 21:28:58 +0200 | [diff] [blame] | 439 | |
Damien Lespiau | 6637501 | 2014-01-09 18:02:46 +0000 | [diff] [blame] | 440 | static size_t __init gen9_stolen_size(int num, int slot, int func) |
| 441 | { |
| 442 | u16 gmch_ctrl; |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 443 | u16 gms; |
Damien Lespiau | 6637501 | 2014-01-09 18:02:46 +0000 | [diff] [blame] | 444 | |
| 445 | gmch_ctrl = read_pci_config_16(num, slot, func, SNB_GMCH_CTRL); |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 446 | gms = (gmch_ctrl >> BDW_GMCH_GMS_SHIFT) & BDW_GMCH_GMS_MASK; |
Damien Lespiau | 6637501 | 2014-01-09 18:02:46 +0000 | [diff] [blame] | 447 | |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 448 | /* 0x0 to 0xef: 32MB increments starting at 0MB */ |
| 449 | /* 0xf0 to 0xfe: 4MB increments starting at 4MB */ |
| 450 | if (gms < 0xf0) |
| 451 | return (size_t)gms * MB(32); |
Damien Lespiau | 6637501 | 2014-01-09 18:02:46 +0000 | [diff] [blame] | 452 | else |
Joonas Lahtinen | c0dd346 | 2016-04-22 13:29:26 +0300 | [diff] [blame] | 453 | return (size_t)(gms - 0xf0 + 1) * MB(4); |
Damien Lespiau | 6637501 | 2014-01-09 18:02:46 +0000 | [diff] [blame] | 454 | } |
| 455 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 456 | struct intel_early_ops { |
| 457 | size_t (*stolen_size)(int num, int slot, int func); |
| 458 | phys_addr_t (*stolen_base)(int num, int slot, int func, size_t size); |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 459 | }; |
| 460 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 461 | static const struct intel_early_ops i830_early_ops __initconst = { |
| 462 | .stolen_base = i830_stolen_base, |
| 463 | .stolen_size = i830_stolen_size, |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 464 | }; |
| 465 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 466 | static const struct intel_early_ops i845_early_ops __initconst = { |
| 467 | .stolen_base = i845_stolen_base, |
| 468 | .stolen_size = i830_stolen_size, |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 469 | }; |
| 470 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 471 | static const struct intel_early_ops i85x_early_ops __initconst = { |
| 472 | .stolen_base = i85x_stolen_base, |
| 473 | .stolen_size = gen3_stolen_size, |
Ville Syrjälä | a4dff76 | 2014-02-05 21:28:59 +0200 | [diff] [blame] | 474 | }; |
| 475 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 476 | static const struct intel_early_ops i865_early_ops __initconst = { |
| 477 | .stolen_base = i865_stolen_base, |
| 478 | .stolen_size = gen3_stolen_size, |
Ville Syrjälä | 52ca704 | 2014-02-05 21:28:58 +0200 | [diff] [blame] | 479 | }; |
| 480 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 481 | static const struct intel_early_ops gen3_early_ops __initconst = { |
| 482 | .stolen_base = gen3_stolen_base, |
| 483 | .stolen_size = gen3_stolen_size, |
Ville Syrjälä | 52ca704 | 2014-02-05 21:28:58 +0200 | [diff] [blame] | 484 | }; |
| 485 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 486 | static const struct intel_early_ops gen6_early_ops __initconst = { |
| 487 | .stolen_base = gen3_stolen_base, |
| 488 | .stolen_size = gen6_stolen_size, |
Ville Syrjälä | 52ca704 | 2014-02-05 21:28:58 +0200 | [diff] [blame] | 489 | }; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 490 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 491 | static const struct intel_early_ops gen8_early_ops __initconst = { |
| 492 | .stolen_base = gen3_stolen_base, |
| 493 | .stolen_size = gen8_stolen_size, |
Damien Lespiau | 6637501 | 2014-01-09 18:02:46 +0000 | [diff] [blame] | 494 | }; |
| 495 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 496 | static const struct intel_early_ops gen9_early_ops __initconst = { |
| 497 | .stolen_base = gen3_stolen_base, |
| 498 | .stolen_size = gen9_stolen_size, |
Damien Lespiau | 3e3b2c3 | 2014-05-08 22:19:41 +0300 | [diff] [blame] | 499 | }; |
| 500 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 501 | static const struct intel_early_ops chv_early_ops __initconst = { |
| 502 | .stolen_base = gen3_stolen_base, |
| 503 | .stolen_size = chv_stolen_size, |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 504 | }; |
| 505 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 506 | static const struct pci_device_id intel_early_ids[] __initconst = { |
| 507 | INTEL_I830_IDS(&i830_early_ops), |
| 508 | INTEL_I845G_IDS(&i845_early_ops), |
| 509 | INTEL_I85X_IDS(&i85x_early_ops), |
| 510 | INTEL_I865G_IDS(&i865_early_ops), |
| 511 | INTEL_I915G_IDS(&gen3_early_ops), |
| 512 | INTEL_I915GM_IDS(&gen3_early_ops), |
| 513 | INTEL_I945G_IDS(&gen3_early_ops), |
| 514 | INTEL_I945GM_IDS(&gen3_early_ops), |
| 515 | INTEL_VLV_M_IDS(&gen6_early_ops), |
| 516 | INTEL_VLV_D_IDS(&gen6_early_ops), |
| 517 | INTEL_PINEVIEW_IDS(&gen3_early_ops), |
| 518 | INTEL_I965G_IDS(&gen3_early_ops), |
| 519 | INTEL_G33_IDS(&gen3_early_ops), |
| 520 | INTEL_I965GM_IDS(&gen3_early_ops), |
| 521 | INTEL_GM45_IDS(&gen3_early_ops), |
| 522 | INTEL_G45_IDS(&gen3_early_ops), |
| 523 | INTEL_IRONLAKE_D_IDS(&gen3_early_ops), |
| 524 | INTEL_IRONLAKE_M_IDS(&gen3_early_ops), |
| 525 | INTEL_SNB_D_IDS(&gen6_early_ops), |
| 526 | INTEL_SNB_M_IDS(&gen6_early_ops), |
| 527 | INTEL_IVB_M_IDS(&gen6_early_ops), |
| 528 | INTEL_IVB_D_IDS(&gen6_early_ops), |
| 529 | INTEL_HSW_D_IDS(&gen6_early_ops), |
| 530 | INTEL_HSW_M_IDS(&gen6_early_ops), |
| 531 | INTEL_BDW_M_IDS(&gen8_early_ops), |
| 532 | INTEL_BDW_D_IDS(&gen8_early_ops), |
| 533 | INTEL_CHV_IDS(&chv_early_ops), |
| 534 | INTEL_SKL_IDS(&gen9_early_ops), |
| 535 | INTEL_BXT_IDS(&gen9_early_ops), |
| 536 | INTEL_KBL_IDS(&gen9_early_ops), |
| 537 | }; |
| 538 | |
| 539 | static void __init |
| 540 | intel_graphics_stolen(int num, int slot, int func, |
| 541 | const struct intel_early_ops *early_ops) |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 542 | { |
Chris Wilson | 01e5d3b | 2016-05-09 17:39:42 +0100 | [diff] [blame] | 543 | phys_addr_t base, end; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 544 | size_t size; |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 545 | |
| 546 | size = early_ops->stolen_size(num, slot, func); |
| 547 | base = early_ops->stolen_base(num, slot, func, size); |
| 548 | |
| 549 | if (!size || !base) |
| 550 | return; |
| 551 | |
Chris Wilson | 01e5d3b | 2016-05-09 17:39:42 +0100 | [diff] [blame] | 552 | end = base + size - 1; |
| 553 | printk(KERN_INFO "Reserving Intel graphics memory at %pa-%pa\n", |
| 554 | &base, &end); |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 555 | |
| 556 | /* Mark this space as reserved */ |
| 557 | e820_add_region(base, size, E820_RESERVED); |
Denys Vlasenko | 4753396 | 2016-09-17 23:39:26 +0200 | [diff] [blame] | 558 | sanitize_e820_map(e820->map, ARRAY_SIZE(e820->map), &e820->nr_map); |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 559 | } |
| 560 | |
| 561 | static void __init intel_graphics_quirks(int num, int slot, int func) |
| 562 | { |
| 563 | const struct intel_early_ops *early_ops; |
| 564 | u16 device; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 565 | int i; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 566 | |
| 567 | device = read_pci_config_16(num, slot, func, PCI_DEVICE_ID); |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 568 | |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 569 | for (i = 0; i < ARRAY_SIZE(intel_early_ids); i++) { |
| 570 | kernel_ulong_t driver_data = intel_early_ids[i].driver_data; |
| 571 | |
| 572 | if (intel_early_ids[i].device != device) |
| 573 | continue; |
| 574 | |
| 575 | early_ops = (typeof(early_ops))driver_data; |
| 576 | |
| 577 | intel_graphics_stolen(num, slot, func, early_ops); |
| 578 | |
| 579 | return; |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 580 | } |
| 581 | } |
| 582 | |
Feng Tang | 6218791 | 2014-04-24 16:18:18 +0800 | [diff] [blame] | 583 | static void __init force_disable_hpet(int num, int slot, int func) |
| 584 | { |
| 585 | #ifdef CONFIG_HPET_TIMER |
Jan Beulich | 3d45ac4 | 2015-10-19 04:35:44 -0600 | [diff] [blame] | 586 | boot_hpet_disable = true; |
Feng Tang | 6218791 | 2014-04-24 16:18:18 +0800 | [diff] [blame] | 587 | pr_info("x86/hpet: Will disable the HPET for this platform because it's not reliable\n"); |
| 588 | #endif |
| 589 | } |
| 590 | |
Lukas Wunner | abb2baf | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 591 | #define BCM4331_MMIO_SIZE 16384 |
| 592 | #define BCM4331_PM_CAP 0x40 |
| 593 | #define bcma_aread32(reg) ioread32(mmio + 1 * BCMA_CORE_SIZE + reg) |
| 594 | #define bcma_awrite32(reg, val) iowrite32(val, mmio + 1 * BCMA_CORE_SIZE + reg) |
| 595 | |
| 596 | static void __init apple_airport_reset(int bus, int slot, int func) |
| 597 | { |
| 598 | void __iomem *mmio; |
| 599 | u16 pmcsr; |
| 600 | u64 addr; |
| 601 | int i; |
| 602 | |
| 603 | if (!dmi_match(DMI_SYS_VENDOR, "Apple Inc.")) |
| 604 | return; |
| 605 | |
| 606 | /* Card may have been put into PCI_D3hot by grub quirk */ |
| 607 | pmcsr = read_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL); |
| 608 | |
| 609 | if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) { |
| 610 | pmcsr &= ~PCI_PM_CTRL_STATE_MASK; |
| 611 | write_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL, pmcsr); |
| 612 | mdelay(10); |
| 613 | |
| 614 | pmcsr = read_pci_config_16(bus, slot, func, BCM4331_PM_CAP + PCI_PM_CTRL); |
| 615 | if ((pmcsr & PCI_PM_CTRL_STATE_MASK) != PCI_D0) { |
| 616 | dev_err("Cannot power up Apple AirPort card\n"); |
| 617 | return; |
| 618 | } |
| 619 | } |
| 620 | |
| 621 | addr = read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_0); |
| 622 | addr |= (u64)read_pci_config(bus, slot, func, PCI_BASE_ADDRESS_1) << 32; |
| 623 | addr &= PCI_BASE_ADDRESS_MEM_MASK; |
| 624 | |
| 625 | mmio = early_ioremap(addr, BCM4331_MMIO_SIZE); |
| 626 | if (!mmio) { |
| 627 | dev_err("Cannot iomap Apple AirPort card\n"); |
| 628 | return; |
| 629 | } |
| 630 | |
| 631 | pr_info("Resetting Apple AirPort card (left enabled by EFI)\n"); |
| 632 | |
| 633 | for (i = 0; bcma_aread32(BCMA_RESET_ST) && i < 30; i++) |
| 634 | udelay(10); |
| 635 | |
| 636 | bcma_awrite32(BCMA_RESET_CTL, BCMA_RESET_CTL_RESET); |
| 637 | bcma_aread32(BCMA_RESET_CTL); |
| 638 | udelay(1); |
| 639 | |
| 640 | bcma_awrite32(BCMA_RESET_CTL, 0); |
| 641 | bcma_aread32(BCMA_RESET_CTL); |
| 642 | udelay(10); |
| 643 | |
| 644 | early_iounmap(mmio, BCM4331_MMIO_SIZE); |
| 645 | } |
Feng Tang | 6218791 | 2014-04-24 16:18:18 +0800 | [diff] [blame] | 646 | |
Neil Horman | c6b4832 | 2008-01-30 13:31:25 +0100 | [diff] [blame] | 647 | #define QFLAG_APPLY_ONCE 0x1 |
| 648 | #define QFLAG_APPLIED 0x2 |
| 649 | #define QFLAG_DONE (QFLAG_APPLY_ONCE|QFLAG_APPLIED) |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 650 | struct chipset { |
Neil Horman | c6b4832 | 2008-01-30 13:31:25 +0100 | [diff] [blame] | 651 | u32 vendor; |
| 652 | u32 device; |
| 653 | u32 class; |
| 654 | u32 class_mask; |
| 655 | u32 flags; |
| 656 | void (*f)(int num, int slot, int func); |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 657 | }; |
| 658 | |
Andrew Morton | c993c73 | 2007-04-08 16:04:03 -0700 | [diff] [blame] | 659 | static struct chipset early_qrk[] __initdata = { |
Neil Horman | c6b4832 | 2008-01-30 13:31:25 +0100 | [diff] [blame] | 660 | { PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, |
| 661 | PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, nvidia_bugs }, |
| 662 | { PCI_VENDOR_ID_VIA, PCI_ANY_ID, |
| 663 | PCI_CLASS_BRIDGE_PCI, PCI_ANY_ID, QFLAG_APPLY_ONCE, via_bugs }, |
Neil Horman | c6b4832 | 2008-01-30 13:31:25 +0100 | [diff] [blame] | 664 | { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB, |
| 665 | PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, fix_hypertransport_config }, |
Andreas Herrmann | 33fb0e4 | 2008-10-07 00:11:22 +0200 | [diff] [blame] | 666 | { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_SMBUS, |
| 667 | PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs }, |
Andreas Herrmann | 26adcfb | 2008-10-14 21:01:15 +0200 | [diff] [blame] | 668 | { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, |
| 669 | PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs_contd }, |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 670 | { PCI_VENDOR_ID_INTEL, 0x3403, PCI_CLASS_BRIDGE_HOST, |
| 671 | PCI_BASE_CLASS_BRIDGE, 0, intel_remapping_check }, |
Neil Horman | 803075d | 2013-07-17 07:13:59 -0400 | [diff] [blame] | 672 | { PCI_VENDOR_ID_INTEL, 0x3405, PCI_CLASS_BRIDGE_HOST, |
| 673 | PCI_BASE_CLASS_BRIDGE, 0, intel_remapping_check }, |
Neil Horman | 03bbcb2 | 2013-04-16 16:38:32 -0400 | [diff] [blame] | 674 | { PCI_VENDOR_ID_INTEL, 0x3406, PCI_CLASS_BRIDGE_HOST, |
| 675 | PCI_BASE_CLASS_BRIDGE, 0, intel_remapping_check }, |
Jesse Barnes | 814c5f1 | 2013-07-26 13:32:52 -0700 | [diff] [blame] | 676 | { PCI_VENDOR_ID_INTEL, PCI_ANY_ID, PCI_CLASS_DISPLAY_VGA, PCI_ANY_ID, |
Joonas Lahtinen | ee0629c | 2016-04-22 13:45:49 +0300 | [diff] [blame] | 677 | QFLAG_APPLY_ONCE, intel_graphics_quirks }, |
Feng Tang | 6218791 | 2014-04-24 16:18:18 +0800 | [diff] [blame] | 678 | /* |
Feng Tang | b58d930 | 2015-06-15 17:40:01 +0800 | [diff] [blame] | 679 | * HPET on the current version of the Baytrail platform has accuracy |
| 680 | * problems: it will halt in deep idle state - so we disable it. |
| 681 | * |
| 682 | * More details can be found in section 18.10.1.3 of the datasheet: |
| 683 | * |
| 684 | * http://www.intel.com/content/dam/www/public/us/en/documents/datasheets/atom-z8000-datasheet-vol-1.pdf |
Feng Tang | 6218791 | 2014-04-24 16:18:18 +0800 | [diff] [blame] | 685 | */ |
| 686 | { PCI_VENDOR_ID_INTEL, 0x0f00, |
| 687 | PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, force_disable_hpet}, |
Lukas Wunner | abb2baf | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 688 | { PCI_VENDOR_ID_BROADCOM, 0x4331, |
| 689 | PCI_CLASS_NETWORK_OTHER, PCI_ANY_ID, 0, apple_airport_reset}, |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 690 | {} |
| 691 | }; |
| 692 | |
Lukas Wunner | 850c321 | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 693 | static void __init early_pci_scan_bus(int bus); |
| 694 | |
Jesse Barnes | 15650a2 | 2008-06-16 15:29:45 -0700 | [diff] [blame] | 695 | /** |
| 696 | * check_dev_quirk - apply early quirks to a given PCI device |
| 697 | * @num: bus number |
| 698 | * @slot: slot number |
| 699 | * @func: PCI function |
| 700 | * |
| 701 | * Check the vendor & device ID against the early quirks table. |
| 702 | * |
Lukas Wunner | 850c321 | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 703 | * If the device is single function, let early_pci_scan_bus() know so we don't |
Jesse Barnes | 15650a2 | 2008-06-16 15:29:45 -0700 | [diff] [blame] | 704 | * poke at this device again. |
| 705 | */ |
| 706 | static int __init check_dev_quirk(int num, int slot, int func) |
Neil Horman | 7bcbc78 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 707 | { |
| 708 | u16 class; |
| 709 | u16 vendor; |
| 710 | u16 device; |
| 711 | u8 type; |
Lukas Wunner | 850c321 | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 712 | u8 sec; |
Neil Horman | 7bcbc78 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 713 | int i; |
| 714 | |
| 715 | class = read_pci_config_16(num, slot, func, PCI_CLASS_DEVICE); |
| 716 | |
| 717 | if (class == 0xffff) |
Jesse Barnes | 15650a2 | 2008-06-16 15:29:45 -0700 | [diff] [blame] | 718 | return -1; /* no class, treat as single function */ |
Neil Horman | 7bcbc78 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 719 | |
| 720 | vendor = read_pci_config_16(num, slot, func, PCI_VENDOR_ID); |
| 721 | |
| 722 | device = read_pci_config_16(num, slot, func, PCI_DEVICE_ID); |
| 723 | |
| 724 | for (i = 0; early_qrk[i].f != NULL; i++) { |
| 725 | if (((early_qrk[i].vendor == PCI_ANY_ID) || |
| 726 | (early_qrk[i].vendor == vendor)) && |
| 727 | ((early_qrk[i].device == PCI_ANY_ID) || |
| 728 | (early_qrk[i].device == device)) && |
| 729 | (!((early_qrk[i].class ^ class) & |
| 730 | early_qrk[i].class_mask))) { |
| 731 | if ((early_qrk[i].flags & |
| 732 | QFLAG_DONE) != QFLAG_DONE) |
| 733 | early_qrk[i].f(num, slot, func); |
| 734 | early_qrk[i].flags |= QFLAG_APPLIED; |
| 735 | } |
| 736 | } |
| 737 | |
| 738 | type = read_pci_config_byte(num, slot, func, |
| 739 | PCI_HEADER_TYPE); |
Lukas Wunner | 850c321 | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 740 | |
| 741 | if ((type & 0x7f) == PCI_HEADER_TYPE_BRIDGE) { |
| 742 | sec = read_pci_config_byte(num, slot, func, PCI_SECONDARY_BUS); |
| 743 | if (sec > num) |
| 744 | early_pci_scan_bus(sec); |
| 745 | } |
| 746 | |
Neil Horman | 7bcbc78 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 747 | if (!(type & 0x80)) |
Jesse Barnes | 15650a2 | 2008-06-16 15:29:45 -0700 | [diff] [blame] | 748 | return -1; |
| 749 | |
| 750 | return 0; |
Neil Horman | 7bcbc78 | 2008-01-30 13:31:26 +0100 | [diff] [blame] | 751 | } |
| 752 | |
Lukas Wunner | 850c321 | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 753 | static void __init early_pci_scan_bus(int bus) |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 754 | { |
Andi Kleen | 8659c40 | 2009-01-09 12:17:39 -0800 | [diff] [blame] | 755 | int slot, func; |
Andi Kleen | 0637a70 | 2006-09-26 10:52:41 +0200 | [diff] [blame] | 756 | |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 757 | /* Poor man's PCI discovery */ |
Andi Kleen | 8659c40 | 2009-01-09 12:17:39 -0800 | [diff] [blame] | 758 | for (slot = 0; slot < 32; slot++) |
| 759 | for (func = 0; func < 8; func++) { |
| 760 | /* Only probe function 0 on single fn devices */ |
Lukas Wunner | 850c321 | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 761 | if (check_dev_quirk(bus, slot, func)) |
Andi Kleen | 8659c40 | 2009-01-09 12:17:39 -0800 | [diff] [blame] | 762 | break; |
| 763 | } |
Andi Kleen | dfa4698 | 2006-09-26 10:52:30 +0200 | [diff] [blame] | 764 | } |
Lukas Wunner | 850c321 | 2016-06-12 12:31:53 +0200 | [diff] [blame] | 765 | |
| 766 | void __init early_quirks(void) |
| 767 | { |
| 768 | if (!early_pci_allowed()) |
| 769 | return; |
| 770 | |
| 771 | early_pci_scan_bus(0); |
| 772 | } |