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> 81da177e4SLinus Torvalds * 91da177e4SLinus Torvalds * Heavily based on PCI quirks handling which is 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * Copyright (c) 1999 Martin Mares <mj@ucw.cz> 121da177e4SLinus Torvalds */ 131da177e4SLinus Torvalds 141da177e4SLinus Torvalds #include <linux/types.h> 151da177e4SLinus Torvalds #include <linux/kernel.h> 161da177e4SLinus Torvalds #include <linux/string.h> 171da177e4SLinus Torvalds #include <linux/slab.h> 181da177e4SLinus Torvalds #include <linux/pnp.h> 19a1e7e636SBjorn Helgaas #include <linux/io.h> 20a05d0781SBjorn Helgaas #include <linux/kallsyms.h> 211da177e4SLinus Torvalds #include "base.h" 221da177e4SLinus Torvalds 231da177e4SLinus Torvalds static void quirk_awe32_resources(struct pnp_dev *dev) 241da177e4SLinus Torvalds { 251da177e4SLinus Torvalds struct pnp_port *port, *port2, *port3; 261da177e4SLinus Torvalds struct pnp_option *res = dev->dependent; 271da177e4SLinus Torvalds 281da177e4SLinus Torvalds /* 291da177e4SLinus Torvalds * Unfortunately the isapnp_add_port_resource is too tightly bound 301da177e4SLinus Torvalds * into the PnP discovery sequence, and cannot be used. Link in the 311da177e4SLinus Torvalds * two extra ports (at offset 0x400 and 0x800 from the one given) by 321da177e4SLinus Torvalds * hand. 331da177e4SLinus Torvalds */ 341da177e4SLinus Torvalds for (; res; res = res->next) { 351da177e4SLinus Torvalds port2 = pnp_alloc(sizeof(struct pnp_port)); 361da177e4SLinus Torvalds if (!port2) 371da177e4SLinus Torvalds return; 381da177e4SLinus Torvalds port3 = pnp_alloc(sizeof(struct pnp_port)); 391da177e4SLinus Torvalds if (!port3) { 401da177e4SLinus Torvalds kfree(port2); 411da177e4SLinus Torvalds return; 421da177e4SLinus Torvalds } 431da177e4SLinus Torvalds port = res->port; 441da177e4SLinus Torvalds memcpy(port2, port, sizeof(struct pnp_port)); 451da177e4SLinus Torvalds memcpy(port3, port, sizeof(struct pnp_port)); 461da177e4SLinus Torvalds port->next = port2; 471da177e4SLinus Torvalds port2->next = port3; 481da177e4SLinus Torvalds port2->min += 0x400; 491da177e4SLinus Torvalds port2->max += 0x400; 501da177e4SLinus Torvalds port3->min += 0x800; 511da177e4SLinus Torvalds port3->max += 0x800; 521e3832b0SBjorn Helgaas dev_info(&dev->dev, 531e3832b0SBjorn Helgaas "AWE32 quirk - added ioports 0x%lx and 0x%lx\n", 541e3832b0SBjorn Helgaas (unsigned long)port2->min, 551e3832b0SBjorn Helgaas (unsigned long)port3->min); 561da177e4SLinus Torvalds } 571da177e4SLinus Torvalds } 581da177e4SLinus Torvalds 591da177e4SLinus Torvalds static void quirk_cmi8330_resources(struct pnp_dev *dev) 601da177e4SLinus Torvalds { 611da177e4SLinus Torvalds struct pnp_option *res = dev->dependent; 621da177e4SLinus Torvalds unsigned long tmp; 631da177e4SLinus Torvalds 641da177e4SLinus Torvalds for (; res; res = res->next) { 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds struct pnp_irq *irq; 671da177e4SLinus Torvalds struct pnp_dma *dma; 681da177e4SLinus Torvalds 697aefff51SBjorn Helgaas for (irq = res->irq; irq; irq = irq->next) { 707aefff51SBjorn Helgaas /* Valid irqs are 5, 7, 10 */ 711da177e4SLinus Torvalds tmp = 0x04A0; 727aefff51SBjorn Helgaas bitmap_copy(irq->map.bits, &tmp, 16); 731da177e4SLinus Torvalds } 741da177e4SLinus Torvalds 757aefff51SBjorn Helgaas for (dma = res->dma; dma; dma = dma->next) { 767aefff51SBjorn Helgaas /* Valid 8bit dma channels are 1,3 */ 779dd78466SBjorn Helgaas if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == 789dd78466SBjorn Helgaas IORESOURCE_DMA_8BIT) 791da177e4SLinus Torvalds dma->map = 0x000A; 801da177e4SLinus Torvalds } 817aefff51SBjorn Helgaas } 821e3832b0SBjorn Helgaas dev_info(&dev->dev, "CMI8330 quirk - forced possible IRQs to 5, 7, 10 " 831e3832b0SBjorn Helgaas "and DMA channels to 1, 3\n"); 841da177e4SLinus Torvalds } 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static void quirk_sb16audio_resources(struct pnp_dev *dev) 871da177e4SLinus Torvalds { 881da177e4SLinus Torvalds struct pnp_port *port; 891da177e4SLinus Torvalds struct pnp_option *res = dev->dependent; 901da177e4SLinus Torvalds int changed = 0; 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds /* 931da177e4SLinus Torvalds * The default range on the mpu port for these devices is 0x388-0x388. 941da177e4SLinus Torvalds * Here we increase that range so that two such cards can be 951da177e4SLinus Torvalds * auto-configured. 961da177e4SLinus Torvalds */ 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds for (; res; res = res->next) { 991da177e4SLinus Torvalds port = res->port; 1001da177e4SLinus Torvalds if (!port) 1011da177e4SLinus Torvalds continue; 1021da177e4SLinus Torvalds port = port->next; 1031da177e4SLinus Torvalds if (!port) 1041da177e4SLinus Torvalds continue; 1051da177e4SLinus Torvalds port = port->next; 1061da177e4SLinus Torvalds if (!port) 1071da177e4SLinus Torvalds continue; 1081da177e4SLinus Torvalds if (port->min != port->max) 1091da177e4SLinus Torvalds continue; 1101da177e4SLinus Torvalds port->max += 0x70; 1111da177e4SLinus Torvalds changed = 1; 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds if (changed) 1141e3832b0SBjorn Helgaas dev_info(&dev->dev, "SB audio device quirk - increased port range\n"); 1151da177e4SLinus Torvalds } 1161da177e4SLinus Torvalds 1173b73a223SRene Herman static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) 1183b73a223SRene Herman { 1193b73a223SRene Herman struct pnp_option *head = NULL; 1203b73a223SRene Herman struct pnp_option *prev = NULL; 1213b73a223SRene Herman struct pnp_option *res; 1223b73a223SRene Herman 1233b73a223SRene Herman /* 124d5ebde6eSBjorn Helgaas * Build a functional IRQ-optional variant of each MPU option. 1253b73a223SRene Herman */ 1263b73a223SRene Herman 1273b73a223SRene Herman for (res = dev->dependent; res; res = res->next) { 1283b73a223SRene Herman struct pnp_option *curr; 1293b73a223SRene Herman struct pnp_port *port; 130d5ebde6eSBjorn Helgaas struct pnp_port *copy_port; 131d5ebde6eSBjorn Helgaas struct pnp_irq *irq; 132d5ebde6eSBjorn Helgaas struct pnp_irq *copy_irq; 1333b73a223SRene Herman 1343b73a223SRene Herman port = res->port; 135d5ebde6eSBjorn Helgaas irq = res->irq; 136d5ebde6eSBjorn Helgaas if (!port || !irq) 1373b73a223SRene Herman continue; 1383b73a223SRene Herman 139d5ebde6eSBjorn Helgaas copy_port = pnp_alloc(sizeof *copy_port); 140d5ebde6eSBjorn Helgaas if (!copy_port) 1413b73a223SRene Herman break; 1423b73a223SRene Herman 143d5ebde6eSBjorn Helgaas copy_irq = pnp_alloc(sizeof *copy_irq); 144d5ebde6eSBjorn Helgaas if (!copy_irq) { 145d5ebde6eSBjorn Helgaas kfree(copy_port); 146d5ebde6eSBjorn Helgaas break; 147d5ebde6eSBjorn Helgaas } 148d5ebde6eSBjorn Helgaas 149d5ebde6eSBjorn Helgaas *copy_port = *port; 150d5ebde6eSBjorn Helgaas copy_port->next = NULL; 151d5ebde6eSBjorn Helgaas 152d5ebde6eSBjorn Helgaas *copy_irq = *irq; 153d5ebde6eSBjorn Helgaas copy_irq->flags |= IORESOURCE_IRQ_OPTIONAL; 154d5ebde6eSBjorn Helgaas copy_irq->next = NULL; 1553b73a223SRene Herman 1563b73a223SRene Herman curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL); 1573b73a223SRene Herman if (!curr) { 158d5ebde6eSBjorn Helgaas kfree(copy_port); 159d5ebde6eSBjorn Helgaas kfree(copy_irq); 1603b73a223SRene Herman break; 1613b73a223SRene Herman } 162d5ebde6eSBjorn Helgaas curr->port = copy_port; 163d5ebde6eSBjorn Helgaas curr->irq = copy_irq; 1643b73a223SRene Herman 1653b73a223SRene Herman if (prev) 1663b73a223SRene Herman prev->next = curr; 1673b73a223SRene Herman else 1683b73a223SRene Herman head = curr; 1693b73a223SRene Herman prev = curr; 1703b73a223SRene Herman } 1713b73a223SRene Herman if (head) 172d5ebde6eSBjorn Helgaas dev_info(&dev->dev, "adding IRQ-optional MPU options\n"); 1733b73a223SRene Herman 1743b73a223SRene Herman return head; 1753b73a223SRene Herman } 1763b73a223SRene Herman 1773b73a223SRene Herman static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) 1783b73a223SRene Herman { 1793b73a223SRene Herman struct pnp_option *res; 1803b73a223SRene Herman struct pnp_irq *irq; 1813b73a223SRene Herman 1823b73a223SRene Herman res = dev->independent; 1833b73a223SRene Herman if (!res) 1843b73a223SRene Herman return; 1853b73a223SRene Herman 1863b73a223SRene Herman irq = res->irq; 1873b73a223SRene Herman if (!irq || irq->next) 1883b73a223SRene Herman return; 1893b73a223SRene Herman 190d5ebde6eSBjorn Helgaas irq->flags |= IORESOURCE_IRQ_OPTIONAL; 191d5ebde6eSBjorn Helgaas dev_info(&dev->dev, "made independent IRQ optional\n"); 1923b73a223SRene Herman } 1933b73a223SRene Herman 1943b73a223SRene Herman static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) 1953b73a223SRene Herman { 1963b73a223SRene Herman struct pnp_option *res; 1973b73a223SRene Herman 1983b73a223SRene Herman res = dev->dependent; 1993b73a223SRene Herman if (!res) 2003b73a223SRene Herman return; 2013b73a223SRene Herman 2023b73a223SRene Herman while (res->next) 2033b73a223SRene Herman res = res->next; 2043b73a223SRene Herman 2053b73a223SRene Herman res->next = quirk_isapnp_mpu_options(dev); 2063b73a223SRene Herman } 2070509ad5eSBjorn Helgaas 2080509ad5eSBjorn Helgaas #include <linux/pci.h> 2090509ad5eSBjorn Helgaas 2100509ad5eSBjorn Helgaas static void quirk_system_pci_resources(struct pnp_dev *dev) 2110509ad5eSBjorn Helgaas { 2120509ad5eSBjorn Helgaas struct pci_dev *pdev = NULL; 21353052febSBjorn Helgaas struct resource *res; 2140509ad5eSBjorn Helgaas resource_size_t pnp_start, pnp_end, pci_start, pci_end; 2150509ad5eSBjorn Helgaas int i, j; 2160509ad5eSBjorn Helgaas 2170509ad5eSBjorn Helgaas /* 2180509ad5eSBjorn Helgaas * Some BIOSes have PNP motherboard devices with resources that 2190509ad5eSBjorn Helgaas * partially overlap PCI BARs. The PNP system driver claims these 2200509ad5eSBjorn Helgaas * motherboard resources, which prevents the normal PCI driver from 2210509ad5eSBjorn Helgaas * requesting them later. 2220509ad5eSBjorn Helgaas * 2230509ad5eSBjorn Helgaas * This patch disables the PNP resources that conflict with PCI BARs 2240509ad5eSBjorn Helgaas * so they won't be claimed by the PNP system driver. 2250509ad5eSBjorn Helgaas */ 2260509ad5eSBjorn Helgaas for_each_pci_dev(pdev) { 2270509ad5eSBjorn Helgaas for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 2280509ad5eSBjorn Helgaas if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM) || 2290509ad5eSBjorn Helgaas pci_resource_len(pdev, i) == 0) 2300509ad5eSBjorn Helgaas continue; 2310509ad5eSBjorn Helgaas 2320509ad5eSBjorn Helgaas pci_start = pci_resource_start(pdev, i); 2330509ad5eSBjorn Helgaas pci_end = pci_resource_end(pdev, i); 23495ab3669SBjorn Helgaas for (j = 0; 23595ab3669SBjorn Helgaas (res = pnp_get_resource(dev, IORESOURCE_MEM, j)); 23695ab3669SBjorn Helgaas j++) { 237aee3ad81SBjorn Helgaas if (res->start == 0 && res->end == 0) 2380509ad5eSBjorn Helgaas continue; 2390509ad5eSBjorn Helgaas 24095ab3669SBjorn Helgaas pnp_start = res->start; 24195ab3669SBjorn Helgaas pnp_end = res->end; 2420509ad5eSBjorn Helgaas 2430509ad5eSBjorn Helgaas /* 2440509ad5eSBjorn Helgaas * If the PNP region doesn't overlap the PCI 2450509ad5eSBjorn Helgaas * region at all, there's no problem. 2460509ad5eSBjorn Helgaas */ 2470509ad5eSBjorn Helgaas if (pnp_end < pci_start || pnp_start > pci_end) 2480509ad5eSBjorn Helgaas continue; 2490509ad5eSBjorn Helgaas 2500509ad5eSBjorn Helgaas /* 2510509ad5eSBjorn Helgaas * If the PNP region completely encloses (or is 2520509ad5eSBjorn Helgaas * at least as large as) the PCI region, that's 2530509ad5eSBjorn Helgaas * also OK. For example, this happens when the 2540509ad5eSBjorn Helgaas * PNP device describes a bridge with PCI 2550509ad5eSBjorn Helgaas * behind it. 2560509ad5eSBjorn Helgaas */ 2570509ad5eSBjorn Helgaas if (pnp_start <= pci_start && 2580509ad5eSBjorn Helgaas pnp_end >= pci_end) 2590509ad5eSBjorn Helgaas continue; 2600509ad5eSBjorn Helgaas 2610509ad5eSBjorn Helgaas /* 2620509ad5eSBjorn Helgaas * Otherwise, the PNP region overlaps *part* of 2630509ad5eSBjorn Helgaas * the PCI region, and that might prevent a PCI 2640509ad5eSBjorn Helgaas * driver from requesting its resources. 2650509ad5eSBjorn Helgaas */ 2660509ad5eSBjorn Helgaas dev_warn(&dev->dev, "mem resource " 2670509ad5eSBjorn Helgaas "(0x%llx-0x%llx) overlaps %s BAR %d " 2680509ad5eSBjorn Helgaas "(0x%llx-0x%llx), disabling\n", 2690509ad5eSBjorn Helgaas (unsigned long long) pnp_start, 2700509ad5eSBjorn Helgaas (unsigned long long) pnp_end, 2710509ad5eSBjorn Helgaas pci_name(pdev), i, 2720509ad5eSBjorn Helgaas (unsigned long long) pci_start, 2730509ad5eSBjorn Helgaas (unsigned long long) pci_end); 2744b34fe15SBjorn Helgaas res->flags |= IORESOURCE_DISABLED; 2750509ad5eSBjorn Helgaas } 2760509ad5eSBjorn Helgaas } 2770509ad5eSBjorn Helgaas } 2780509ad5eSBjorn Helgaas } 2790509ad5eSBjorn Helgaas 2801da177e4SLinus Torvalds /* 2811da177e4SLinus Torvalds * PnP Quirks 2821da177e4SLinus Torvalds * Cards or devices that need some tweaking due to incomplete resource info 2831da177e4SLinus Torvalds */ 2841da177e4SLinus Torvalds 2851da177e4SLinus Torvalds static struct pnp_fixup pnp_fixups[] = { 2861da177e4SLinus Torvalds /* Soundblaster awe io port quirk */ 2871da177e4SLinus Torvalds {"CTL0021", quirk_awe32_resources}, 2881da177e4SLinus Torvalds {"CTL0022", quirk_awe32_resources}, 2891da177e4SLinus Torvalds {"CTL0023", quirk_awe32_resources}, 2901da177e4SLinus Torvalds /* CMI 8330 interrupt and dma fix */ 2911da177e4SLinus Torvalds {"@X@0001", quirk_cmi8330_resources}, 2921da177e4SLinus Torvalds /* Soundblaster audio device io port range quirk */ 2931da177e4SLinus Torvalds {"CTL0001", quirk_sb16audio_resources}, 2941da177e4SLinus Torvalds {"CTL0031", quirk_sb16audio_resources}, 2951da177e4SLinus Torvalds {"CTL0041", quirk_sb16audio_resources}, 2961da177e4SLinus Torvalds {"CTL0042", quirk_sb16audio_resources}, 2971da177e4SLinus Torvalds {"CTL0043", quirk_sb16audio_resources}, 2981da177e4SLinus Torvalds {"CTL0044", quirk_sb16audio_resources}, 2991da177e4SLinus Torvalds {"CTL0045", quirk_sb16audio_resources}, 3003b73a223SRene Herman /* Add IRQ-less MPU options */ 3013b73a223SRene Herman {"ADS7151", quirk_ad1815_mpu_resources}, 3023b73a223SRene Herman {"ADS7181", quirk_isapnp_mpu_resources}, 3033b73a223SRene Herman {"AZT0002", quirk_isapnp_mpu_resources}, 3043b73a223SRene Herman /* PnP resources that might overlap PCI BARs */ 3050509ad5eSBjorn Helgaas {"PNP0c01", quirk_system_pci_resources}, 3060509ad5eSBjorn Helgaas {"PNP0c02", quirk_system_pci_resources}, 3071da177e4SLinus Torvalds {""} 3081da177e4SLinus Torvalds }; 3091da177e4SLinus Torvalds 3101da177e4SLinus Torvalds void pnp_fixup_device(struct pnp_dev *dev) 3111da177e4SLinus Torvalds { 312726a7a3dSRene Herman struct pnp_fixup *f; 3131da177e4SLinus Torvalds 314726a7a3dSRene Herman for (f = pnp_fixups; *f->id; f++) { 315726a7a3dSRene Herman if (!compare_pnp_id(dev->id, f->id)) 316726a7a3dSRene Herman continue; 317a05d0781SBjorn Helgaas #ifdef DEBUG 318726a7a3dSRene Herman dev_dbg(&dev->dev, "%s: calling ", f->id); 319a442ac51SLinus Torvalds print_fn_descriptor_symbol("%s\n", f->quirk_function); 320a05d0781SBjorn Helgaas #endif 321726a7a3dSRene Herman f->quirk_function(dev); 3221da177e4SLinus Torvalds } 3231da177e4SLinus Torvalds } 324