11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * This file contains quirk handling code for PnP devices 31da177e4SLinus Torvalds * Some devices do not report all their resources, and need to have extra 41da177e4SLinus Torvalds * resources added. This is most easily accomplished at initialisation time 51da177e4SLinus Torvalds * when building up the resource structure for the first time. 61da177e4SLinus Torvalds * 71da177e4SLinus Torvalds * Copyright (c) 2000 Peter Denison <peterd@pnd-pc.demon.co.uk> 81f32ca31SBjorn Helgaas * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. 91f32ca31SBjorn Helgaas * Bjorn Helgaas <bjorn.helgaas@hp.com> 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * Heavily based on PCI quirks handling which is 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * Copyright (c) 1999 Martin Mares <mj@ucw.cz> 141da177e4SLinus Torvalds */ 151da177e4SLinus Torvalds 161da177e4SLinus Torvalds #include <linux/types.h> 171da177e4SLinus Torvalds #include <linux/kernel.h> 18cb171f7aSBjorn Helgaas #include <linux/pci.h> 191da177e4SLinus Torvalds #include <linux/string.h> 201da177e4SLinus Torvalds #include <linux/slab.h> 211da177e4SLinus Torvalds #include <linux/pnp.h> 22a1e7e636SBjorn Helgaas #include <linux/io.h> 23a05d0781SBjorn Helgaas #include <linux/kallsyms.h> 241da177e4SLinus Torvalds #include "base.h" 251da177e4SLinus Torvalds 261f32ca31SBjorn Helgaas static void quirk_awe32_add_ports(struct pnp_dev *dev, 271f32ca31SBjorn Helgaas struct pnp_option *option, 281f32ca31SBjorn Helgaas unsigned int offset) 291da177e4SLinus Torvalds { 301f32ca31SBjorn Helgaas struct pnp_option *new_option; 311da177e4SLinus Torvalds 321f32ca31SBjorn Helgaas new_option = kmalloc(sizeof(struct pnp_option), GFP_KERNEL); 331f32ca31SBjorn Helgaas if (!new_option) { 341f32ca31SBjorn Helgaas dev_err(&dev->dev, "couldn't add ioport region to option set " 351f32ca31SBjorn Helgaas "%d\n", pnp_option_set(option)); 361da177e4SLinus Torvalds return; 371da177e4SLinus Torvalds } 381f32ca31SBjorn Helgaas 391f32ca31SBjorn Helgaas *new_option = *option; 401f32ca31SBjorn Helgaas new_option->u.port.min += offset; 411f32ca31SBjorn Helgaas new_option->u.port.max += offset; 421f32ca31SBjorn Helgaas list_add(&new_option->list, &option->list); 431f32ca31SBjorn Helgaas 441f32ca31SBjorn Helgaas dev_info(&dev->dev, "added ioport region %#llx-%#llx to set %d\n", 451f32ca31SBjorn Helgaas (unsigned long long) new_option->u.port.min, 461f32ca31SBjorn Helgaas (unsigned long long) new_option->u.port.max, 471f32ca31SBjorn Helgaas pnp_option_set(option)); 481f32ca31SBjorn Helgaas } 491f32ca31SBjorn Helgaas 501f32ca31SBjorn Helgaas static void quirk_awe32_resources(struct pnp_dev *dev) 511f32ca31SBjorn Helgaas { 521f32ca31SBjorn Helgaas struct pnp_option *option; 531f32ca31SBjorn Helgaas unsigned int set = ~0; 541f32ca31SBjorn Helgaas 551f32ca31SBjorn Helgaas /* 561f32ca31SBjorn Helgaas * Add two extra ioport regions (at offset 0x400 and 0x800 from the 571f32ca31SBjorn Helgaas * one given) to every dependent option set. 581f32ca31SBjorn Helgaas */ 591f32ca31SBjorn Helgaas list_for_each_entry(option, &dev->options, list) { 601f32ca31SBjorn Helgaas if (pnp_option_is_dependent(option) && 611f32ca31SBjorn Helgaas pnp_option_set(option) != set) { 621f32ca31SBjorn Helgaas set = pnp_option_set(option); 631f32ca31SBjorn Helgaas quirk_awe32_add_ports(dev, option, 0x800); 641f32ca31SBjorn Helgaas quirk_awe32_add_ports(dev, option, 0x400); 651f32ca31SBjorn Helgaas } 661da177e4SLinus Torvalds } 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds static void quirk_cmi8330_resources(struct pnp_dev *dev) 701da177e4SLinus Torvalds { 711f32ca31SBjorn Helgaas struct pnp_option *option; 721da177e4SLinus Torvalds struct pnp_irq *irq; 731da177e4SLinus Torvalds struct pnp_dma *dma; 741da177e4SLinus Torvalds 751f32ca31SBjorn Helgaas list_for_each_entry(option, &dev->options, list) { 761f32ca31SBjorn Helgaas if (!pnp_option_is_dependent(option)) 771f32ca31SBjorn Helgaas continue; 781da177e4SLinus Torvalds 791f32ca31SBjorn Helgaas if (option->type == IORESOURCE_IRQ) { 801f32ca31SBjorn Helgaas irq = &option->u.irq; 811f32ca31SBjorn Helgaas bitmap_zero(irq->map.bits, PNP_IRQ_NR); 821f32ca31SBjorn Helgaas __set_bit(5, irq->map.bits); 831f32ca31SBjorn Helgaas __set_bit(7, irq->map.bits); 841f32ca31SBjorn Helgaas __set_bit(10, irq->map.bits); 851f32ca31SBjorn Helgaas dev_info(&dev->dev, "set possible IRQs in " 861f32ca31SBjorn Helgaas "option set %d to 5, 7, 10\n", 871f32ca31SBjorn Helgaas pnp_option_set(option)); 881f32ca31SBjorn Helgaas } else if (option->type == IORESOURCE_DMA) { 891f32ca31SBjorn Helgaas dma = &option->u.dma; 909dd78466SBjorn Helgaas if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == 911f32ca31SBjorn Helgaas IORESOURCE_DMA_8BIT && 921f32ca31SBjorn Helgaas dma->map != 0x0A) { 931f32ca31SBjorn Helgaas dev_info(&dev->dev, "changing possible " 941f32ca31SBjorn Helgaas "DMA channel mask in option set %d " 951f32ca31SBjorn Helgaas "from %#02x to 0x0A (1, 3)\n", 961f32ca31SBjorn Helgaas pnp_option_set(option), dma->map); 971f32ca31SBjorn Helgaas dma->map = 0x0A; 981da177e4SLinus Torvalds } 997aefff51SBjorn Helgaas } 1001f32ca31SBjorn Helgaas } 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds 1031da177e4SLinus Torvalds static void quirk_sb16audio_resources(struct pnp_dev *dev) 1041da177e4SLinus Torvalds { 1051f32ca31SBjorn Helgaas struct pnp_option *option; 1061f32ca31SBjorn Helgaas unsigned int prev_option_flags = ~0, n = 0; 1071da177e4SLinus Torvalds struct pnp_port *port; 1081da177e4SLinus Torvalds 1091da177e4SLinus Torvalds /* 1101f32ca31SBjorn Helgaas * The default range on the OPL port for these devices is 0x388-0x388. 1111da177e4SLinus Torvalds * Here we increase that range so that two such cards can be 1121da177e4SLinus Torvalds * auto-configured. 1131da177e4SLinus Torvalds */ 1141f32ca31SBjorn Helgaas list_for_each_entry(option, &dev->options, list) { 1151f32ca31SBjorn Helgaas if (prev_option_flags != option->flags) { 1161f32ca31SBjorn Helgaas prev_option_flags = option->flags; 1171f32ca31SBjorn Helgaas n = 0; 1181f32ca31SBjorn Helgaas } 1191da177e4SLinus Torvalds 1201f32ca31SBjorn Helgaas if (pnp_option_is_dependent(option) && 1211f32ca31SBjorn Helgaas option->type == IORESOURCE_IO) { 1221f32ca31SBjorn Helgaas n++; 1231f32ca31SBjorn Helgaas port = &option->u.port; 1241f32ca31SBjorn Helgaas if (n == 3 && port->min == port->max) { 1251da177e4SLinus Torvalds port->max += 0x70; 1261f32ca31SBjorn Helgaas dev_info(&dev->dev, "increased option port " 1271f32ca31SBjorn Helgaas "range from %#llx-%#llx to " 1281f32ca31SBjorn Helgaas "%#llx-%#llx\n", 1291f32ca31SBjorn Helgaas (unsigned long long) port->min, 1301f32ca31SBjorn Helgaas (unsigned long long) port->min, 1311f32ca31SBjorn Helgaas (unsigned long long) port->min, 1321f32ca31SBjorn Helgaas (unsigned long long) port->max); 1331da177e4SLinus Torvalds } 1341f32ca31SBjorn Helgaas } 1351f32ca31SBjorn Helgaas } 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381f32ca31SBjorn Helgaas static struct pnp_option *pnp_clone_dependent_set(struct pnp_dev *dev, 1391f32ca31SBjorn Helgaas unsigned int set) 1403b73a223SRene Herman { 1411f32ca31SBjorn Helgaas struct pnp_option *tail = NULL, *first_new_option = NULL; 1421f32ca31SBjorn Helgaas struct pnp_option *option, *new_option; 1431f32ca31SBjorn Helgaas unsigned int flags; 1443b73a223SRene Herman 1451f32ca31SBjorn Helgaas list_for_each_entry(option, &dev->options, list) { 1461f32ca31SBjorn Helgaas if (pnp_option_is_dependent(option)) 1471f32ca31SBjorn Helgaas tail = option; 1481f32ca31SBjorn Helgaas } 1491f32ca31SBjorn Helgaas if (!tail) { 1501f32ca31SBjorn Helgaas dev_err(&dev->dev, "no dependent option sets\n"); 1511f32ca31SBjorn Helgaas return NULL; 1521f32ca31SBjorn Helgaas } 1533b73a223SRene Herman 1541f32ca31SBjorn Helgaas flags = pnp_new_dependent_set(dev, PNP_RES_PRIORITY_FUNCTIONAL); 1551f32ca31SBjorn Helgaas list_for_each_entry(option, &dev->options, list) { 1561f32ca31SBjorn Helgaas if (pnp_option_is_dependent(option) && 1571f32ca31SBjorn Helgaas pnp_option_set(option) == set) { 1581f32ca31SBjorn Helgaas new_option = kmalloc(sizeof(struct pnp_option), 1591f32ca31SBjorn Helgaas GFP_KERNEL); 1601f32ca31SBjorn Helgaas if (!new_option) { 1611f32ca31SBjorn Helgaas dev_err(&dev->dev, "couldn't clone dependent " 1621f32ca31SBjorn Helgaas "set %d\n", set); 1631f32ca31SBjorn Helgaas return NULL; 1641f32ca31SBjorn Helgaas } 1651f32ca31SBjorn Helgaas 1661f32ca31SBjorn Helgaas *new_option = *option; 1671f32ca31SBjorn Helgaas new_option->flags = flags; 1681f32ca31SBjorn Helgaas if (!first_new_option) 1691f32ca31SBjorn Helgaas first_new_option = new_option; 1701f32ca31SBjorn Helgaas 1711f32ca31SBjorn Helgaas list_add(&new_option->list, &tail->list); 1721f32ca31SBjorn Helgaas tail = new_option; 1731f32ca31SBjorn Helgaas } 1741f32ca31SBjorn Helgaas } 1751f32ca31SBjorn Helgaas 1761f32ca31SBjorn Helgaas return first_new_option; 1771f32ca31SBjorn Helgaas } 1781f32ca31SBjorn Helgaas 1791f32ca31SBjorn Helgaas 1801f32ca31SBjorn Helgaas static void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev) 1811f32ca31SBjorn Helgaas { 1821f32ca31SBjorn Helgaas struct pnp_option *new_option; 1831f32ca31SBjorn Helgaas unsigned int num_sets, i, set; 184d5ebde6eSBjorn Helgaas struct pnp_irq *irq; 1853b73a223SRene Herman 1861f32ca31SBjorn Helgaas num_sets = dev->num_dependent_sets; 1871f32ca31SBjorn Helgaas for (i = 0; i < num_sets; i++) { 1881f32ca31SBjorn Helgaas new_option = pnp_clone_dependent_set(dev, i); 1891f32ca31SBjorn Helgaas if (!new_option) 1901f32ca31SBjorn Helgaas return; 1913b73a223SRene Herman 1921f32ca31SBjorn Helgaas set = pnp_option_set(new_option); 1931f32ca31SBjorn Helgaas while (new_option && pnp_option_set(new_option) == set) { 1941f32ca31SBjorn Helgaas if (new_option->type == IORESOURCE_IRQ) { 1951f32ca31SBjorn Helgaas irq = &new_option->u.irq; 1961f32ca31SBjorn Helgaas irq->flags |= IORESOURCE_IRQ_OPTIONAL; 1971f32ca31SBjorn Helgaas } 1981f32ca31SBjorn Helgaas dbg_pnp_show_option(dev, new_option); 1991f32ca31SBjorn Helgaas new_option = list_entry(new_option->list.next, 2001f32ca31SBjorn Helgaas struct pnp_option, list); 201d5ebde6eSBjorn Helgaas } 202d5ebde6eSBjorn Helgaas 2031f32ca31SBjorn Helgaas dev_info(&dev->dev, "added dependent option set %d (same as " 2041f32ca31SBjorn Helgaas "set %d except IRQ optional)\n", set, i); 2053b73a223SRene Herman } 2063b73a223SRene Herman } 2073b73a223SRene Herman 2083b73a223SRene Herman static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) 2093b73a223SRene Herman { 2101f32ca31SBjorn Helgaas struct pnp_option *option; 2111f32ca31SBjorn Helgaas struct pnp_irq *irq = NULL; 2121f32ca31SBjorn Helgaas unsigned int independent_irqs = 0; 2133b73a223SRene Herman 2141f32ca31SBjorn Helgaas list_for_each_entry(option, &dev->options, list) { 2151f32ca31SBjorn Helgaas if (option->type == IORESOURCE_IRQ && 2161f32ca31SBjorn Helgaas !pnp_option_is_dependent(option)) { 2171f32ca31SBjorn Helgaas independent_irqs++; 2181f32ca31SBjorn Helgaas irq = &option->u.irq; 2191f32ca31SBjorn Helgaas } 2201f32ca31SBjorn Helgaas } 2213b73a223SRene Herman 2221f32ca31SBjorn Helgaas if (independent_irqs != 1) 2233b73a223SRene Herman return; 2243b73a223SRene Herman 225d5ebde6eSBjorn Helgaas irq->flags |= IORESOURCE_IRQ_OPTIONAL; 226d5ebde6eSBjorn Helgaas dev_info(&dev->dev, "made independent IRQ optional\n"); 2273b73a223SRene Herman } 2283b73a223SRene Herman 2290509ad5eSBjorn Helgaas #include <linux/pci.h> 2300509ad5eSBjorn Helgaas 2310509ad5eSBjorn Helgaas static void quirk_system_pci_resources(struct pnp_dev *dev) 2320509ad5eSBjorn Helgaas { 2330509ad5eSBjorn Helgaas struct pci_dev *pdev = NULL; 23453052febSBjorn Helgaas struct resource *res; 2350509ad5eSBjorn Helgaas resource_size_t pnp_start, pnp_end, pci_start, pci_end; 2360509ad5eSBjorn Helgaas int i, j; 2370509ad5eSBjorn Helgaas 2380509ad5eSBjorn Helgaas /* 2390509ad5eSBjorn Helgaas * Some BIOSes have PNP motherboard devices with resources that 2400509ad5eSBjorn Helgaas * partially overlap PCI BARs. The PNP system driver claims these 2410509ad5eSBjorn Helgaas * motherboard resources, which prevents the normal PCI driver from 2420509ad5eSBjorn Helgaas * requesting them later. 2430509ad5eSBjorn Helgaas * 2440509ad5eSBjorn Helgaas * This patch disables the PNP resources that conflict with PCI BARs 2450509ad5eSBjorn Helgaas * so they won't be claimed by the PNP system driver. 2460509ad5eSBjorn Helgaas */ 2470509ad5eSBjorn Helgaas for_each_pci_dev(pdev) { 2480509ad5eSBjorn Helgaas for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 249f7834c09SBjorn Helgaas unsigned long flags, type; 250999ed65aSRene Herman 251f7834c09SBjorn Helgaas flags = pci_resource_flags(pdev, i); 252f7834c09SBjorn Helgaas type = flags & (IORESOURCE_IO | IORESOURCE_MEM); 253999ed65aSRene Herman if (!type || pci_resource_len(pdev, i) == 0) 2540509ad5eSBjorn Helgaas continue; 2550509ad5eSBjorn Helgaas 256f7834c09SBjorn Helgaas if (flags & IORESOURCE_UNSET) 257f7834c09SBjorn Helgaas continue; 258f7834c09SBjorn Helgaas 2590509ad5eSBjorn Helgaas pci_start = pci_resource_start(pdev, i); 2600509ad5eSBjorn Helgaas pci_end = pci_resource_end(pdev, i); 26195ab3669SBjorn Helgaas for (j = 0; 262999ed65aSRene Herman (res = pnp_get_resource(dev, type, j)); j++) { 263aee3ad81SBjorn Helgaas if (res->start == 0 && res->end == 0) 2640509ad5eSBjorn Helgaas continue; 2650509ad5eSBjorn Helgaas 26695ab3669SBjorn Helgaas pnp_start = res->start; 26795ab3669SBjorn Helgaas pnp_end = res->end; 2680509ad5eSBjorn Helgaas 2690509ad5eSBjorn Helgaas /* 2700509ad5eSBjorn Helgaas * If the PNP region doesn't overlap the PCI 2710509ad5eSBjorn Helgaas * region at all, there's no problem. 2720509ad5eSBjorn Helgaas */ 2730509ad5eSBjorn Helgaas if (pnp_end < pci_start || pnp_start > pci_end) 2740509ad5eSBjorn Helgaas continue; 2750509ad5eSBjorn Helgaas 2760509ad5eSBjorn Helgaas /* 2770509ad5eSBjorn Helgaas * If the PNP region completely encloses (or is 2780509ad5eSBjorn Helgaas * at least as large as) the PCI region, that's 2790509ad5eSBjorn Helgaas * also OK. For example, this happens when the 2800509ad5eSBjorn Helgaas * PNP device describes a bridge with PCI 2810509ad5eSBjorn Helgaas * behind it. 2820509ad5eSBjorn Helgaas */ 2830509ad5eSBjorn Helgaas if (pnp_start <= pci_start && 2840509ad5eSBjorn Helgaas pnp_end >= pci_end) 2850509ad5eSBjorn Helgaas continue; 2860509ad5eSBjorn Helgaas 2870509ad5eSBjorn Helgaas /* 2880509ad5eSBjorn Helgaas * Otherwise, the PNP region overlaps *part* of 2890509ad5eSBjorn Helgaas * the PCI region, and that might prevent a PCI 2900509ad5eSBjorn Helgaas * driver from requesting its resources. 2910509ad5eSBjorn Helgaas */ 292c7dabef8SBjorn Helgaas dev_warn(&dev->dev, 293c7dabef8SBjorn Helgaas "disabling %pR because it overlaps " 294c7dabef8SBjorn Helgaas "%s BAR %d %pR\n", res, 2959a007b37SBjorn Helgaas pci_name(pdev), i, &pdev->resource[i]); 2964b34fe15SBjorn Helgaas res->flags |= IORESOURCE_DISABLED; 2970509ad5eSBjorn Helgaas } 2980509ad5eSBjorn Helgaas } 2990509ad5eSBjorn Helgaas } 3000509ad5eSBjorn Helgaas } 3010509ad5eSBjorn Helgaas 302eb31aae8SBjorn Helgaas #ifdef CONFIG_AMD_NB 303eb31aae8SBjorn Helgaas 304eb31aae8SBjorn Helgaas #include <asm/amd_nb.h> 305eb31aae8SBjorn Helgaas 306eb31aae8SBjorn Helgaas static void quirk_amd_mmconfig_area(struct pnp_dev *dev) 307eb31aae8SBjorn Helgaas { 308eb31aae8SBjorn Helgaas resource_size_t start, end; 309eb31aae8SBjorn Helgaas struct pnp_resource *pnp_res; 310eb31aae8SBjorn Helgaas struct resource *res; 311eb31aae8SBjorn Helgaas struct resource mmconfig_res, *mmconfig; 312eb31aae8SBjorn Helgaas 313eb31aae8SBjorn Helgaas mmconfig = amd_get_mmconfig_range(&mmconfig_res); 314eb31aae8SBjorn Helgaas if (!mmconfig) 315eb31aae8SBjorn Helgaas return; 316eb31aae8SBjorn Helgaas 317eb31aae8SBjorn Helgaas list_for_each_entry(pnp_res, &dev->resources, list) { 318eb31aae8SBjorn Helgaas res = &pnp_res->res; 319eb31aae8SBjorn Helgaas if (res->end < mmconfig->start || res->start > mmconfig->end || 320eb31aae8SBjorn Helgaas (res->start == mmconfig->start && res->end == mmconfig->end)) 321eb31aae8SBjorn Helgaas continue; 322eb31aae8SBjorn Helgaas 323eb31aae8SBjorn Helgaas dev_info(&dev->dev, FW_BUG 324eb31aae8SBjorn Helgaas "%pR covers only part of AMD MMCONFIG area %pR; adding more reservations\n", 325eb31aae8SBjorn Helgaas res, mmconfig); 326eb31aae8SBjorn Helgaas if (mmconfig->start < res->start) { 327eb31aae8SBjorn Helgaas start = mmconfig->start; 328eb31aae8SBjorn Helgaas end = res->start - 1; 329eb31aae8SBjorn Helgaas pnp_add_mem_resource(dev, start, end, 0); 330eb31aae8SBjorn Helgaas } 331eb31aae8SBjorn Helgaas if (mmconfig->end > res->end) { 332eb31aae8SBjorn Helgaas start = res->end + 1; 333eb31aae8SBjorn Helgaas end = mmconfig->end; 334eb31aae8SBjorn Helgaas pnp_add_mem_resource(dev, start, end, 0); 335eb31aae8SBjorn Helgaas } 336eb31aae8SBjorn Helgaas break; 337eb31aae8SBjorn Helgaas } 338eb31aae8SBjorn Helgaas } 339eb31aae8SBjorn Helgaas #endif 340eb31aae8SBjorn Helgaas 341b3413afbSBjorn Helgaas #ifdef CONFIG_PCI 342cb171f7aSBjorn Helgaas /* Device IDs of parts that have 32KB MCH space */ 343cb171f7aSBjorn Helgaas static const unsigned int mch_quirk_devices[] = { 344cb171f7aSBjorn Helgaas 0x0154, /* Ivy Bridge */ 345cb171f7aSBjorn Helgaas 0x0c00, /* Haswell */ 346a77060f0SChristophe Le Roy 0x1604, /* Broadwell */ 347cb171f7aSBjorn Helgaas }; 348cb171f7aSBjorn Helgaas 349cb171f7aSBjorn Helgaas static struct pci_dev *get_intel_host(void) 350cb171f7aSBjorn Helgaas { 351cb171f7aSBjorn Helgaas int i; 352cb171f7aSBjorn Helgaas struct pci_dev *host; 353cb171f7aSBjorn Helgaas 354cb171f7aSBjorn Helgaas for (i = 0; i < ARRAY_SIZE(mch_quirk_devices); i++) { 355cb171f7aSBjorn Helgaas host = pci_get_device(PCI_VENDOR_ID_INTEL, mch_quirk_devices[i], 356cb171f7aSBjorn Helgaas NULL); 357cb171f7aSBjorn Helgaas if (host) 358cb171f7aSBjorn Helgaas return host; 359cb171f7aSBjorn Helgaas } 360cb171f7aSBjorn Helgaas return NULL; 361cb171f7aSBjorn Helgaas } 362cb171f7aSBjorn Helgaas 363cb171f7aSBjorn Helgaas static void quirk_intel_mch(struct pnp_dev *dev) 364cb171f7aSBjorn Helgaas { 365cb171f7aSBjorn Helgaas struct pci_dev *host; 366cb171f7aSBjorn Helgaas u32 addr_lo, addr_hi; 367cb171f7aSBjorn Helgaas struct pci_bus_region region; 368cb171f7aSBjorn Helgaas struct resource mch; 369cb171f7aSBjorn Helgaas struct pnp_resource *pnp_res; 370cb171f7aSBjorn Helgaas struct resource *res; 371cb171f7aSBjorn Helgaas 372cb171f7aSBjorn Helgaas host = get_intel_host(); 373cb171f7aSBjorn Helgaas if (!host) 374cb171f7aSBjorn Helgaas return; 375cb171f7aSBjorn Helgaas 376cb171f7aSBjorn Helgaas /* 377cb171f7aSBjorn Helgaas * MCHBAR is not an architected PCI BAR, so MCH space is usually 378cb171f7aSBjorn Helgaas * reported as a PNP0C02 resource. The MCH space was originally 379cb171f7aSBjorn Helgaas * 16KB, but is 32KB in newer parts. Some BIOSes still report a 380cb171f7aSBjorn Helgaas * PNP0C02 resource that is only 16KB, which means the rest of the 381cb171f7aSBjorn Helgaas * MCH space is consumed but unreported. 382cb171f7aSBjorn Helgaas */ 383cb171f7aSBjorn Helgaas 384cb171f7aSBjorn Helgaas /* 385cb171f7aSBjorn Helgaas * Read MCHBAR for Host Member Mapped Register Range Base 386cb171f7aSBjorn Helgaas * https://www-ssl.intel.com/content/www/us/en/processors/core/4th-gen-core-family-desktop-vol-2-datasheet 387cb171f7aSBjorn Helgaas * Sec 3.1.12. 388cb171f7aSBjorn Helgaas */ 389cb171f7aSBjorn Helgaas pci_read_config_dword(host, 0x48, &addr_lo); 390cb171f7aSBjorn Helgaas region.start = addr_lo & ~0x7fff; 391cb171f7aSBjorn Helgaas pci_read_config_dword(host, 0x4c, &addr_hi); 392cb171f7aSBjorn Helgaas region.start |= (u64) addr_hi << 32; 393cb171f7aSBjorn Helgaas region.end = region.start + 32*1024 - 1; 394cb171f7aSBjorn Helgaas 395cb171f7aSBjorn Helgaas memset(&mch, 0, sizeof(mch)); 396cb171f7aSBjorn Helgaas mch.flags = IORESOURCE_MEM; 397cb171f7aSBjorn Helgaas pcibios_bus_to_resource(host->bus, &mch, ®ion); 398cb171f7aSBjorn Helgaas 399cb171f7aSBjorn Helgaas list_for_each_entry(pnp_res, &dev->resources, list) { 400cb171f7aSBjorn Helgaas res = &pnp_res->res; 401cb171f7aSBjorn Helgaas if (res->end < mch.start || res->start > mch.end) 402cb171f7aSBjorn Helgaas continue; /* no overlap */ 403cb171f7aSBjorn Helgaas if (res->start == mch.start && res->end == mch.end) 404cb171f7aSBjorn Helgaas continue; /* exact match */ 405cb171f7aSBjorn Helgaas 406cb171f7aSBjorn Helgaas dev_info(&dev->dev, FW_BUG "PNP resource %pR covers only part of %s Intel MCH; extending to %pR\n", 407cb171f7aSBjorn Helgaas res, pci_name(host), &mch); 408cb171f7aSBjorn Helgaas res->start = mch.start; 409cb171f7aSBjorn Helgaas res->end = mch.end; 410cb171f7aSBjorn Helgaas break; 411cb171f7aSBjorn Helgaas } 412cb171f7aSBjorn Helgaas 413cb171f7aSBjorn Helgaas pci_dev_put(host); 414cb171f7aSBjorn Helgaas } 415cb171f7aSBjorn Helgaas #endif 416cb171f7aSBjorn Helgaas 4171da177e4SLinus Torvalds /* 4181da177e4SLinus Torvalds * PnP Quirks 4191da177e4SLinus Torvalds * Cards or devices that need some tweaking due to incomplete resource info 4201da177e4SLinus Torvalds */ 4211da177e4SLinus Torvalds 4221da177e4SLinus Torvalds static struct pnp_fixup pnp_fixups[] = { 4231da177e4SLinus Torvalds /* Soundblaster awe io port quirk */ 4241da177e4SLinus Torvalds {"CTL0021", quirk_awe32_resources}, 4251da177e4SLinus Torvalds {"CTL0022", quirk_awe32_resources}, 4261da177e4SLinus Torvalds {"CTL0023", quirk_awe32_resources}, 4271da177e4SLinus Torvalds /* CMI 8330 interrupt and dma fix */ 4281da177e4SLinus Torvalds {"@X@0001", quirk_cmi8330_resources}, 4291da177e4SLinus Torvalds /* Soundblaster audio device io port range quirk */ 4301da177e4SLinus Torvalds {"CTL0001", quirk_sb16audio_resources}, 4311da177e4SLinus Torvalds {"CTL0031", quirk_sb16audio_resources}, 4321da177e4SLinus Torvalds {"CTL0041", quirk_sb16audio_resources}, 4331da177e4SLinus Torvalds {"CTL0042", quirk_sb16audio_resources}, 4341da177e4SLinus Torvalds {"CTL0043", quirk_sb16audio_resources}, 4351da177e4SLinus Torvalds {"CTL0044", quirk_sb16audio_resources}, 4361da177e4SLinus Torvalds {"CTL0045", quirk_sb16audio_resources}, 4371f32ca31SBjorn Helgaas /* Add IRQ-optional MPU options */ 4383b73a223SRene Herman {"ADS7151", quirk_ad1815_mpu_resources}, 4391f32ca31SBjorn Helgaas {"ADS7181", quirk_add_irq_optional_dependent_sets}, 4401f32ca31SBjorn Helgaas {"AZT0002", quirk_add_irq_optional_dependent_sets}, 4413b73a223SRene Herman /* PnP resources that might overlap PCI BARs */ 4420509ad5eSBjorn Helgaas {"PNP0c01", quirk_system_pci_resources}, 4430509ad5eSBjorn Helgaas {"PNP0c02", quirk_system_pci_resources}, 444eb31aae8SBjorn Helgaas #ifdef CONFIG_AMD_NB 445eb31aae8SBjorn Helgaas {"PNP0c01", quirk_amd_mmconfig_area}, 446eb31aae8SBjorn Helgaas #endif 447b3413afbSBjorn Helgaas #ifdef CONFIG_PCI 448cb171f7aSBjorn Helgaas {"PNP0c02", quirk_intel_mch}, 449cb171f7aSBjorn Helgaas #endif 4501da177e4SLinus Torvalds {""} 4511da177e4SLinus Torvalds }; 4521da177e4SLinus Torvalds 4531da177e4SLinus Torvalds void pnp_fixup_device(struct pnp_dev *dev) 4541da177e4SLinus Torvalds { 455726a7a3dSRene Herman struct pnp_fixup *f; 4561da177e4SLinus Torvalds 457726a7a3dSRene Herman for (f = pnp_fixups; *f->id; f++) { 458726a7a3dSRene Herman if (!compare_pnp_id(dev->id, f->id)) 459726a7a3dSRene Herman continue; 4602f53432cSBjorn Helgaas pnp_dbg(&dev->dev, "%s: calling %pF\n", f->id, 461668b21deSBjorn Helgaas f->quirk_function); 462726a7a3dSRene Herman f->quirk_function(dev); 4631da177e4SLinus Torvalds } 4641da177e4SLinus Torvalds } 465