18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file contains quirk handling code for PnP devices 48c2ecf20Sopenharmony_ci * Some devices do not report all their resources, and need to have extra 58c2ecf20Sopenharmony_ci * resources added. This is most easily accomplished at initialisation time 68c2ecf20Sopenharmony_ci * when building up the resource structure for the first time. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2000 Peter Denison <peterd@pnd-pc.demon.co.uk> 98c2ecf20Sopenharmony_ci * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. 108c2ecf20Sopenharmony_ci * Bjorn Helgaas <bjorn.helgaas@hp.com> 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Heavily based on PCI quirks handling which is 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * Copyright (c) 1999 Martin Mares <mj@ucw.cz> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/types.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/pci.h> 208c2ecf20Sopenharmony_ci#include <linux/string.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/pnp.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include "base.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic void quirk_awe32_add_ports(struct pnp_dev *dev, 278c2ecf20Sopenharmony_ci struct pnp_option *option, 288c2ecf20Sopenharmony_ci unsigned int offset) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci struct pnp_option *new_option; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci new_option = kmalloc(sizeof(struct pnp_option), GFP_KERNEL); 338c2ecf20Sopenharmony_ci if (!new_option) { 348c2ecf20Sopenharmony_ci dev_err(&dev->dev, "couldn't add ioport region to option set " 358c2ecf20Sopenharmony_ci "%d\n", pnp_option_set(option)); 368c2ecf20Sopenharmony_ci return; 378c2ecf20Sopenharmony_ci } 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci *new_option = *option; 408c2ecf20Sopenharmony_ci new_option->u.port.min += offset; 418c2ecf20Sopenharmony_ci new_option->u.port.max += offset; 428c2ecf20Sopenharmony_ci list_add(&new_option->list, &option->list); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci dev_info(&dev->dev, "added ioport region %#llx-%#llx to set %d\n", 458c2ecf20Sopenharmony_ci (unsigned long long) new_option->u.port.min, 468c2ecf20Sopenharmony_ci (unsigned long long) new_option->u.port.max, 478c2ecf20Sopenharmony_ci pnp_option_set(option)); 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void quirk_awe32_resources(struct pnp_dev *dev) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct pnp_option *option; 538c2ecf20Sopenharmony_ci unsigned int set = ~0; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* 568c2ecf20Sopenharmony_ci * Add two extra ioport regions (at offset 0x400 and 0x800 from the 578c2ecf20Sopenharmony_ci * one given) to every dependent option set. 588c2ecf20Sopenharmony_ci */ 598c2ecf20Sopenharmony_ci list_for_each_entry(option, &dev->options, list) { 608c2ecf20Sopenharmony_ci if (pnp_option_is_dependent(option) && 618c2ecf20Sopenharmony_ci pnp_option_set(option) != set) { 628c2ecf20Sopenharmony_ci set = pnp_option_set(option); 638c2ecf20Sopenharmony_ci quirk_awe32_add_ports(dev, option, 0x800); 648c2ecf20Sopenharmony_ci quirk_awe32_add_ports(dev, option, 0x400); 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void quirk_cmi8330_resources(struct pnp_dev *dev) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci struct pnp_option *option; 728c2ecf20Sopenharmony_ci struct pnp_irq *irq; 738c2ecf20Sopenharmony_ci struct pnp_dma *dma; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci list_for_each_entry(option, &dev->options, list) { 768c2ecf20Sopenharmony_ci if (!pnp_option_is_dependent(option)) 778c2ecf20Sopenharmony_ci continue; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci if (option->type == IORESOURCE_IRQ) { 808c2ecf20Sopenharmony_ci irq = &option->u.irq; 818c2ecf20Sopenharmony_ci bitmap_zero(irq->map.bits, PNP_IRQ_NR); 828c2ecf20Sopenharmony_ci __set_bit(5, irq->map.bits); 838c2ecf20Sopenharmony_ci __set_bit(7, irq->map.bits); 848c2ecf20Sopenharmony_ci __set_bit(10, irq->map.bits); 858c2ecf20Sopenharmony_ci dev_info(&dev->dev, "set possible IRQs in " 868c2ecf20Sopenharmony_ci "option set %d to 5, 7, 10\n", 878c2ecf20Sopenharmony_ci pnp_option_set(option)); 888c2ecf20Sopenharmony_ci } else if (option->type == IORESOURCE_DMA) { 898c2ecf20Sopenharmony_ci dma = &option->u.dma; 908c2ecf20Sopenharmony_ci if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == 918c2ecf20Sopenharmony_ci IORESOURCE_DMA_8BIT && 928c2ecf20Sopenharmony_ci dma->map != 0x0A) { 938c2ecf20Sopenharmony_ci dev_info(&dev->dev, "changing possible " 948c2ecf20Sopenharmony_ci "DMA channel mask in option set %d " 958c2ecf20Sopenharmony_ci "from %#02x to 0x0A (1, 3)\n", 968c2ecf20Sopenharmony_ci pnp_option_set(option), dma->map); 978c2ecf20Sopenharmony_ci dma->map = 0x0A; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void quirk_sb16audio_resources(struct pnp_dev *dev) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct pnp_option *option; 1068c2ecf20Sopenharmony_ci unsigned int prev_option_flags = ~0, n = 0; 1078c2ecf20Sopenharmony_ci struct pnp_port *port; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* 1108c2ecf20Sopenharmony_ci * The default range on the OPL port for these devices is 0x388-0x388. 1118c2ecf20Sopenharmony_ci * Here we increase that range so that two such cards can be 1128c2ecf20Sopenharmony_ci * auto-configured. 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_ci list_for_each_entry(option, &dev->options, list) { 1158c2ecf20Sopenharmony_ci if (prev_option_flags != option->flags) { 1168c2ecf20Sopenharmony_ci prev_option_flags = option->flags; 1178c2ecf20Sopenharmony_ci n = 0; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (pnp_option_is_dependent(option) && 1218c2ecf20Sopenharmony_ci option->type == IORESOURCE_IO) { 1228c2ecf20Sopenharmony_ci n++; 1238c2ecf20Sopenharmony_ci port = &option->u.port; 1248c2ecf20Sopenharmony_ci if (n == 3 && port->min == port->max) { 1258c2ecf20Sopenharmony_ci port->max += 0x70; 1268c2ecf20Sopenharmony_ci dev_info(&dev->dev, "increased option port " 1278c2ecf20Sopenharmony_ci "range from %#llx-%#llx to " 1288c2ecf20Sopenharmony_ci "%#llx-%#llx\n", 1298c2ecf20Sopenharmony_ci (unsigned long long) port->min, 1308c2ecf20Sopenharmony_ci (unsigned long long) port->min, 1318c2ecf20Sopenharmony_ci (unsigned long long) port->min, 1328c2ecf20Sopenharmony_ci (unsigned long long) port->max); 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic struct pnp_option *pnp_clone_dependent_set(struct pnp_dev *dev, 1398c2ecf20Sopenharmony_ci unsigned int set) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct pnp_option *tail = NULL, *first_new_option = NULL; 1428c2ecf20Sopenharmony_ci struct pnp_option *option, *new_option; 1438c2ecf20Sopenharmony_ci unsigned int flags; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci list_for_each_entry(option, &dev->options, list) { 1468c2ecf20Sopenharmony_ci if (pnp_option_is_dependent(option)) 1478c2ecf20Sopenharmony_ci tail = option; 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci if (!tail) { 1508c2ecf20Sopenharmony_ci dev_err(&dev->dev, "no dependent option sets\n"); 1518c2ecf20Sopenharmony_ci return NULL; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci flags = pnp_new_dependent_set(dev, PNP_RES_PRIORITY_FUNCTIONAL); 1558c2ecf20Sopenharmony_ci list_for_each_entry(option, &dev->options, list) { 1568c2ecf20Sopenharmony_ci if (pnp_option_is_dependent(option) && 1578c2ecf20Sopenharmony_ci pnp_option_set(option) == set) { 1588c2ecf20Sopenharmony_ci new_option = kmalloc(sizeof(struct pnp_option), 1598c2ecf20Sopenharmony_ci GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!new_option) { 1618c2ecf20Sopenharmony_ci dev_err(&dev->dev, "couldn't clone dependent " 1628c2ecf20Sopenharmony_ci "set %d\n", set); 1638c2ecf20Sopenharmony_ci return NULL; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci *new_option = *option; 1678c2ecf20Sopenharmony_ci new_option->flags = flags; 1688c2ecf20Sopenharmony_ci if (!first_new_option) 1698c2ecf20Sopenharmony_ci first_new_option = new_option; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci list_add(&new_option->list, &tail->list); 1728c2ecf20Sopenharmony_ci tail = new_option; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return first_new_option; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct pnp_option *new_option; 1838c2ecf20Sopenharmony_ci unsigned int num_sets, i, set; 1848c2ecf20Sopenharmony_ci struct pnp_irq *irq; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci num_sets = dev->num_dependent_sets; 1878c2ecf20Sopenharmony_ci for (i = 0; i < num_sets; i++) { 1888c2ecf20Sopenharmony_ci new_option = pnp_clone_dependent_set(dev, i); 1898c2ecf20Sopenharmony_ci if (!new_option) 1908c2ecf20Sopenharmony_ci return; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci set = pnp_option_set(new_option); 1938c2ecf20Sopenharmony_ci while (new_option && pnp_option_set(new_option) == set) { 1948c2ecf20Sopenharmony_ci if (new_option->type == IORESOURCE_IRQ) { 1958c2ecf20Sopenharmony_ci irq = &new_option->u.irq; 1968c2ecf20Sopenharmony_ci irq->flags |= IORESOURCE_IRQ_OPTIONAL; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci dbg_pnp_show_option(dev, new_option); 1998c2ecf20Sopenharmony_ci new_option = list_entry(new_option->list.next, 2008c2ecf20Sopenharmony_ci struct pnp_option, list); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci dev_info(&dev->dev, "added dependent option set %d (same as " 2048c2ecf20Sopenharmony_ci "set %d except IRQ optional)\n", set, i); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void quirk_ad1815_mpu_resources(struct pnp_dev *dev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct pnp_option *option; 2118c2ecf20Sopenharmony_ci struct pnp_irq *irq = NULL; 2128c2ecf20Sopenharmony_ci unsigned int independent_irqs = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci list_for_each_entry(option, &dev->options, list) { 2158c2ecf20Sopenharmony_ci if (option->type == IORESOURCE_IRQ && 2168c2ecf20Sopenharmony_ci !pnp_option_is_dependent(option)) { 2178c2ecf20Sopenharmony_ci independent_irqs++; 2188c2ecf20Sopenharmony_ci irq = &option->u.irq; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (independent_irqs != 1) 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci irq->flags |= IORESOURCE_IRQ_OPTIONAL; 2268c2ecf20Sopenharmony_ci dev_info(&dev->dev, "made independent IRQ optional\n"); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void quirk_system_pci_resources(struct pnp_dev *dev) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 2328c2ecf20Sopenharmony_ci struct resource *res; 2338c2ecf20Sopenharmony_ci resource_size_t pnp_start, pnp_end, pci_start, pci_end; 2348c2ecf20Sopenharmony_ci int i, j; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* 2378c2ecf20Sopenharmony_ci * Some BIOSes have PNP motherboard devices with resources that 2388c2ecf20Sopenharmony_ci * partially overlap PCI BARs. The PNP system driver claims these 2398c2ecf20Sopenharmony_ci * motherboard resources, which prevents the normal PCI driver from 2408c2ecf20Sopenharmony_ci * requesting them later. 2418c2ecf20Sopenharmony_ci * 2428c2ecf20Sopenharmony_ci * This patch disables the PNP resources that conflict with PCI BARs 2438c2ecf20Sopenharmony_ci * so they won't be claimed by the PNP system driver. 2448c2ecf20Sopenharmony_ci */ 2458c2ecf20Sopenharmony_ci for_each_pci_dev(pdev) { 2468c2ecf20Sopenharmony_ci for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 2478c2ecf20Sopenharmony_ci unsigned long flags, type; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci flags = pci_resource_flags(pdev, i); 2508c2ecf20Sopenharmony_ci type = flags & (IORESOURCE_IO | IORESOURCE_MEM); 2518c2ecf20Sopenharmony_ci if (!type || pci_resource_len(pdev, i) == 0) 2528c2ecf20Sopenharmony_ci continue; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (flags & IORESOURCE_UNSET) 2558c2ecf20Sopenharmony_ci continue; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci pci_start = pci_resource_start(pdev, i); 2588c2ecf20Sopenharmony_ci pci_end = pci_resource_end(pdev, i); 2598c2ecf20Sopenharmony_ci for (j = 0; 2608c2ecf20Sopenharmony_ci (res = pnp_get_resource(dev, type, j)); j++) { 2618c2ecf20Sopenharmony_ci if (res->start == 0 && res->end == 0) 2628c2ecf20Sopenharmony_ci continue; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci pnp_start = res->start; 2658c2ecf20Sopenharmony_ci pnp_end = res->end; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* 2688c2ecf20Sopenharmony_ci * If the PNP region doesn't overlap the PCI 2698c2ecf20Sopenharmony_ci * region at all, there's no problem. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci if (pnp_end < pci_start || pnp_start > pci_end) 2728c2ecf20Sopenharmony_ci continue; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * If the PNP region completely encloses (or is 2768c2ecf20Sopenharmony_ci * at least as large as) the PCI region, that's 2778c2ecf20Sopenharmony_ci * also OK. For example, this happens when the 2788c2ecf20Sopenharmony_ci * PNP device describes a bridge with PCI 2798c2ecf20Sopenharmony_ci * behind it. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci if (pnp_start <= pci_start && 2828c2ecf20Sopenharmony_ci pnp_end >= pci_end) 2838c2ecf20Sopenharmony_ci continue; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * Otherwise, the PNP region overlaps *part* of 2878c2ecf20Sopenharmony_ci * the PCI region, and that might prevent a PCI 2888c2ecf20Sopenharmony_ci * driver from requesting its resources. 2898c2ecf20Sopenharmony_ci */ 2908c2ecf20Sopenharmony_ci dev_warn(&dev->dev, 2918c2ecf20Sopenharmony_ci "disabling %pR because it overlaps " 2928c2ecf20Sopenharmony_ci "%s BAR %d %pR\n", res, 2938c2ecf20Sopenharmony_ci pci_name(pdev), i, &pdev->resource[i]); 2948c2ecf20Sopenharmony_ci res->flags |= IORESOURCE_DISABLED; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#ifdef CONFIG_AMD_NB 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci#include <asm/amd_nb.h> 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic void quirk_amd_mmconfig_area(struct pnp_dev *dev) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci resource_size_t start, end; 3078c2ecf20Sopenharmony_ci struct pnp_resource *pnp_res; 3088c2ecf20Sopenharmony_ci struct resource *res; 3098c2ecf20Sopenharmony_ci struct resource mmconfig_res, *mmconfig; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci mmconfig = amd_get_mmconfig_range(&mmconfig_res); 3128c2ecf20Sopenharmony_ci if (!mmconfig) 3138c2ecf20Sopenharmony_ci return; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci list_for_each_entry(pnp_res, &dev->resources, list) { 3168c2ecf20Sopenharmony_ci res = &pnp_res->res; 3178c2ecf20Sopenharmony_ci if (res->end < mmconfig->start || res->start > mmconfig->end || 3188c2ecf20Sopenharmony_ci (res->start == mmconfig->start && res->end == mmconfig->end)) 3198c2ecf20Sopenharmony_ci continue; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci dev_info(&dev->dev, FW_BUG 3228c2ecf20Sopenharmony_ci "%pR covers only part of AMD MMCONFIG area %pR; adding more reservations\n", 3238c2ecf20Sopenharmony_ci res, mmconfig); 3248c2ecf20Sopenharmony_ci if (mmconfig->start < res->start) { 3258c2ecf20Sopenharmony_ci start = mmconfig->start; 3268c2ecf20Sopenharmony_ci end = res->start - 1; 3278c2ecf20Sopenharmony_ci pnp_add_mem_resource(dev, start, end, 0); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci if (mmconfig->end > res->end) { 3308c2ecf20Sopenharmony_ci start = res->end + 1; 3318c2ecf20Sopenharmony_ci end = mmconfig->end; 3328c2ecf20Sopenharmony_ci pnp_add_mem_resource(dev, start, end, 0); 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci break; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci#endif 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 3408c2ecf20Sopenharmony_ci/* Device IDs of parts that have 32KB MCH space */ 3418c2ecf20Sopenharmony_cistatic const unsigned int mch_quirk_devices[] = { 3428c2ecf20Sopenharmony_ci 0x0154, /* Ivy Bridge */ 3438c2ecf20Sopenharmony_ci 0x0a04, /* Haswell-ULT */ 3448c2ecf20Sopenharmony_ci 0x0c00, /* Haswell */ 3458c2ecf20Sopenharmony_ci 0x1604, /* Broadwell */ 3468c2ecf20Sopenharmony_ci}; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic struct pci_dev *get_intel_host(void) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci int i; 3518c2ecf20Sopenharmony_ci struct pci_dev *host; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mch_quirk_devices); i++) { 3548c2ecf20Sopenharmony_ci host = pci_get_device(PCI_VENDOR_ID_INTEL, mch_quirk_devices[i], 3558c2ecf20Sopenharmony_ci NULL); 3568c2ecf20Sopenharmony_ci if (host) 3578c2ecf20Sopenharmony_ci return host; 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci return NULL; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void quirk_intel_mch(struct pnp_dev *dev) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct pci_dev *host; 3658c2ecf20Sopenharmony_ci u32 addr_lo, addr_hi; 3668c2ecf20Sopenharmony_ci struct pci_bus_region region; 3678c2ecf20Sopenharmony_ci struct resource mch; 3688c2ecf20Sopenharmony_ci struct pnp_resource *pnp_res; 3698c2ecf20Sopenharmony_ci struct resource *res; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci host = get_intel_host(); 3728c2ecf20Sopenharmony_ci if (!host) 3738c2ecf20Sopenharmony_ci return; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* 3768c2ecf20Sopenharmony_ci * MCHBAR is not an architected PCI BAR, so MCH space is usually 3778c2ecf20Sopenharmony_ci * reported as a PNP0C02 resource. The MCH space was originally 3788c2ecf20Sopenharmony_ci * 16KB, but is 32KB in newer parts. Some BIOSes still report a 3798c2ecf20Sopenharmony_ci * PNP0C02 resource that is only 16KB, which means the rest of the 3808c2ecf20Sopenharmony_ci * MCH space is consumed but unreported. 3818c2ecf20Sopenharmony_ci */ 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * Read MCHBAR for Host Member Mapped Register Range Base 3858c2ecf20Sopenharmony_ci * https://www-ssl.intel.com/content/www/us/en/processors/core/4th-gen-core-family-desktop-vol-2-datasheet 3868c2ecf20Sopenharmony_ci * Sec 3.1.12. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_ci pci_read_config_dword(host, 0x48, &addr_lo); 3898c2ecf20Sopenharmony_ci region.start = addr_lo & ~0x7fff; 3908c2ecf20Sopenharmony_ci pci_read_config_dword(host, 0x4c, &addr_hi); 3918c2ecf20Sopenharmony_ci region.start |= (u64) addr_hi << 32; 3928c2ecf20Sopenharmony_ci region.end = region.start + 32*1024 - 1; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci memset(&mch, 0, sizeof(mch)); 3958c2ecf20Sopenharmony_ci mch.flags = IORESOURCE_MEM; 3968c2ecf20Sopenharmony_ci pcibios_bus_to_resource(host->bus, &mch, ®ion); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci list_for_each_entry(pnp_res, &dev->resources, list) { 3998c2ecf20Sopenharmony_ci res = &pnp_res->res; 4008c2ecf20Sopenharmony_ci if (res->end < mch.start || res->start > mch.end) 4018c2ecf20Sopenharmony_ci continue; /* no overlap */ 4028c2ecf20Sopenharmony_ci if (res->start == mch.start && res->end == mch.end) 4038c2ecf20Sopenharmony_ci continue; /* exact match */ 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci dev_info(&dev->dev, FW_BUG "PNP resource %pR covers only part of %s Intel MCH; extending to %pR\n", 4068c2ecf20Sopenharmony_ci res, pci_name(host), &mch); 4078c2ecf20Sopenharmony_ci res->start = mch.start; 4088c2ecf20Sopenharmony_ci res->end = mch.end; 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci pci_dev_put(host); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci#endif 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci/* 4178c2ecf20Sopenharmony_ci * PnP Quirks 4188c2ecf20Sopenharmony_ci * Cards or devices that need some tweaking due to incomplete resource info 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic struct pnp_fixup pnp_fixups[] = { 4228c2ecf20Sopenharmony_ci /* Soundblaster awe io port quirk */ 4238c2ecf20Sopenharmony_ci {"CTL0021", quirk_awe32_resources}, 4248c2ecf20Sopenharmony_ci {"CTL0022", quirk_awe32_resources}, 4258c2ecf20Sopenharmony_ci {"CTL0023", quirk_awe32_resources}, 4268c2ecf20Sopenharmony_ci /* CMI 8330 interrupt and dma fix */ 4278c2ecf20Sopenharmony_ci {"@X@0001", quirk_cmi8330_resources}, 4288c2ecf20Sopenharmony_ci /* Soundblaster audio device io port range quirk */ 4298c2ecf20Sopenharmony_ci {"CTL0001", quirk_sb16audio_resources}, 4308c2ecf20Sopenharmony_ci {"CTL0031", quirk_sb16audio_resources}, 4318c2ecf20Sopenharmony_ci {"CTL0041", quirk_sb16audio_resources}, 4328c2ecf20Sopenharmony_ci {"CTL0042", quirk_sb16audio_resources}, 4338c2ecf20Sopenharmony_ci {"CTL0043", quirk_sb16audio_resources}, 4348c2ecf20Sopenharmony_ci {"CTL0044", quirk_sb16audio_resources}, 4358c2ecf20Sopenharmony_ci {"CTL0045", quirk_sb16audio_resources}, 4368c2ecf20Sopenharmony_ci /* Add IRQ-optional MPU options */ 4378c2ecf20Sopenharmony_ci {"ADS7151", quirk_ad1815_mpu_resources}, 4388c2ecf20Sopenharmony_ci {"ADS7181", quirk_add_irq_optional_dependent_sets}, 4398c2ecf20Sopenharmony_ci {"AZT0002", quirk_add_irq_optional_dependent_sets}, 4408c2ecf20Sopenharmony_ci /* PnP resources that might overlap PCI BARs */ 4418c2ecf20Sopenharmony_ci {"PNP0c01", quirk_system_pci_resources}, 4428c2ecf20Sopenharmony_ci {"PNP0c02", quirk_system_pci_resources}, 4438c2ecf20Sopenharmony_ci#ifdef CONFIG_AMD_NB 4448c2ecf20Sopenharmony_ci {"PNP0c01", quirk_amd_mmconfig_area}, 4458c2ecf20Sopenharmony_ci#endif 4468c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI 4478c2ecf20Sopenharmony_ci {"PNP0c02", quirk_intel_mch}, 4488c2ecf20Sopenharmony_ci#endif 4498c2ecf20Sopenharmony_ci {""} 4508c2ecf20Sopenharmony_ci}; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_civoid pnp_fixup_device(struct pnp_dev *dev) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct pnp_fixup *f; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci for (f = pnp_fixups; *f->id; f++) { 4578c2ecf20Sopenharmony_ci if (!compare_pnp_id(dev->id, f->id)) 4588c2ecf20Sopenharmony_ci continue; 4598c2ecf20Sopenharmony_ci pnp_dbg(&dev->dev, "%s: calling %pS\n", f->id, 4608c2ecf20Sopenharmony_ci f->quirk_function); 4618c2ecf20Sopenharmony_ci f->quirk_function(dev); 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci} 464