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; 521da177e4SLinus Torvalds } 531da177e4SLinus Torvalds printk(KERN_INFO "pnp: AWE32 quirk - adding two ports\n"); 541da177e4SLinus Torvalds } 551da177e4SLinus Torvalds 561da177e4SLinus Torvalds static void quirk_cmi8330_resources(struct pnp_dev *dev) 571da177e4SLinus Torvalds { 581da177e4SLinus Torvalds struct pnp_option *res = dev->dependent; 591da177e4SLinus Torvalds unsigned long tmp; 601da177e4SLinus Torvalds 611da177e4SLinus Torvalds for (; res; res = res->next) { 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds struct pnp_irq *irq; 641da177e4SLinus Torvalds struct pnp_dma *dma; 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds for (irq = res->irq; irq; irq = irq->next) { // Valid irqs are 5, 7, 10 671da177e4SLinus Torvalds tmp = 0x04A0; 681da177e4SLinus Torvalds bitmap_copy(irq->map, &tmp, 16); // 0000 0100 1010 0000 691da177e4SLinus Torvalds } 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds for (dma = res->dma; dma; dma = dma->next) // Valid 8bit dma channels are 1,3 729dd78466SBjorn Helgaas if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == 739dd78466SBjorn Helgaas IORESOURCE_DMA_8BIT) 741da177e4SLinus Torvalds dma->map = 0x000A; 751da177e4SLinus Torvalds } 761da177e4SLinus Torvalds printk(KERN_INFO "pnp: CMI8330 quirk - fixing interrupts and dma\n"); 771da177e4SLinus Torvalds } 781da177e4SLinus Torvalds 791da177e4SLinus Torvalds static void quirk_sb16audio_resources(struct pnp_dev *dev) 801da177e4SLinus Torvalds { 811da177e4SLinus Torvalds struct pnp_port *port; 821da177e4SLinus Torvalds struct pnp_option *res = dev->dependent; 831da177e4SLinus Torvalds int changed = 0; 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds /* 861da177e4SLinus Torvalds * The default range on the mpu port for these devices is 0x388-0x388. 871da177e4SLinus Torvalds * Here we increase that range so that two such cards can be 881da177e4SLinus Torvalds * auto-configured. 891da177e4SLinus Torvalds */ 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds for (; res; res = res->next) { 921da177e4SLinus Torvalds port = res->port; 931da177e4SLinus Torvalds if (!port) 941da177e4SLinus Torvalds continue; 951da177e4SLinus Torvalds port = port->next; 961da177e4SLinus Torvalds if (!port) 971da177e4SLinus Torvalds continue; 981da177e4SLinus Torvalds port = port->next; 991da177e4SLinus Torvalds if (!port) 1001da177e4SLinus Torvalds continue; 1011da177e4SLinus Torvalds if (port->min != port->max) 1021da177e4SLinus Torvalds continue; 1031da177e4SLinus Torvalds port->max += 0x70; 1041da177e4SLinus Torvalds changed = 1; 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds if (changed) 1079dd78466SBjorn Helgaas printk(KERN_INFO 1089dd78466SBjorn Helgaas "pnp: SB audio device quirk - increasing port range\n"); 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds 1110509ad5eSBjorn Helgaas 1120509ad5eSBjorn Helgaas #include <linux/pci.h> 1130509ad5eSBjorn Helgaas 1140509ad5eSBjorn Helgaas static void quirk_system_pci_resources(struct pnp_dev *dev) 1150509ad5eSBjorn Helgaas { 1160509ad5eSBjorn Helgaas struct pci_dev *pdev = NULL; 1170509ad5eSBjorn Helgaas resource_size_t pnp_start, pnp_end, pci_start, pci_end; 1180509ad5eSBjorn Helgaas int i, j; 1190509ad5eSBjorn Helgaas 1200509ad5eSBjorn Helgaas /* 1210509ad5eSBjorn Helgaas * Some BIOSes have PNP motherboard devices with resources that 1220509ad5eSBjorn Helgaas * partially overlap PCI BARs. The PNP system driver claims these 1230509ad5eSBjorn Helgaas * motherboard resources, which prevents the normal PCI driver from 1240509ad5eSBjorn Helgaas * requesting them later. 1250509ad5eSBjorn Helgaas * 1260509ad5eSBjorn Helgaas * This patch disables the PNP resources that conflict with PCI BARs 1270509ad5eSBjorn Helgaas * so they won't be claimed by the PNP system driver. 1280509ad5eSBjorn Helgaas */ 1290509ad5eSBjorn Helgaas for_each_pci_dev(pdev) { 1300509ad5eSBjorn Helgaas for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 1310509ad5eSBjorn Helgaas if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM) || 1320509ad5eSBjorn Helgaas pci_resource_len(pdev, i) == 0) 1330509ad5eSBjorn Helgaas continue; 1340509ad5eSBjorn Helgaas 1350509ad5eSBjorn Helgaas pci_start = pci_resource_start(pdev, i); 1360509ad5eSBjorn Helgaas pci_end = pci_resource_end(pdev, i); 1370509ad5eSBjorn Helgaas for (j = 0; j < PNP_MAX_MEM; j++) { 1380509ad5eSBjorn Helgaas if (!pnp_mem_valid(dev, j) || 1390509ad5eSBjorn Helgaas pnp_mem_len(dev, j) == 0) 1400509ad5eSBjorn Helgaas continue; 1410509ad5eSBjorn Helgaas 1420509ad5eSBjorn Helgaas pnp_start = pnp_mem_start(dev, j); 1430509ad5eSBjorn Helgaas pnp_end = pnp_mem_end(dev, j); 1440509ad5eSBjorn Helgaas 1450509ad5eSBjorn Helgaas /* 1460509ad5eSBjorn Helgaas * If the PNP region doesn't overlap the PCI 1470509ad5eSBjorn Helgaas * region at all, there's no problem. 1480509ad5eSBjorn Helgaas */ 1490509ad5eSBjorn Helgaas if (pnp_end < pci_start || pnp_start > pci_end) 1500509ad5eSBjorn Helgaas continue; 1510509ad5eSBjorn Helgaas 1520509ad5eSBjorn Helgaas /* 1530509ad5eSBjorn Helgaas * If the PNP region completely encloses (or is 1540509ad5eSBjorn Helgaas * at least as large as) the PCI region, that's 1550509ad5eSBjorn Helgaas * also OK. For example, this happens when the 1560509ad5eSBjorn Helgaas * PNP device describes a bridge with PCI 1570509ad5eSBjorn Helgaas * behind it. 1580509ad5eSBjorn Helgaas */ 1590509ad5eSBjorn Helgaas if (pnp_start <= pci_start && 1600509ad5eSBjorn Helgaas pnp_end >= pci_end) 1610509ad5eSBjorn Helgaas continue; 1620509ad5eSBjorn Helgaas 1630509ad5eSBjorn Helgaas /* 1640509ad5eSBjorn Helgaas * Otherwise, the PNP region overlaps *part* of 1650509ad5eSBjorn Helgaas * the PCI region, and that might prevent a PCI 1660509ad5eSBjorn Helgaas * driver from requesting its resources. 1670509ad5eSBjorn Helgaas */ 1680509ad5eSBjorn Helgaas dev_warn(&dev->dev, "mem resource " 1690509ad5eSBjorn Helgaas "(0x%llx-0x%llx) overlaps %s BAR %d " 1700509ad5eSBjorn Helgaas "(0x%llx-0x%llx), disabling\n", 1710509ad5eSBjorn Helgaas (unsigned long long) pnp_start, 1720509ad5eSBjorn Helgaas (unsigned long long) pnp_end, 1730509ad5eSBjorn Helgaas pci_name(pdev), i, 1740509ad5eSBjorn Helgaas (unsigned long long) pci_start, 1750509ad5eSBjorn Helgaas (unsigned long long) pci_end); 1760509ad5eSBjorn Helgaas pnp_mem_flags(dev, j) = 0; 1770509ad5eSBjorn Helgaas } 1780509ad5eSBjorn Helgaas } 1790509ad5eSBjorn Helgaas } 1800509ad5eSBjorn Helgaas } 1810509ad5eSBjorn Helgaas 1821da177e4SLinus Torvalds /* 1831da177e4SLinus Torvalds * PnP Quirks 1841da177e4SLinus Torvalds * Cards or devices that need some tweaking due to incomplete resource info 1851da177e4SLinus Torvalds */ 1861da177e4SLinus Torvalds 1871da177e4SLinus Torvalds static struct pnp_fixup pnp_fixups[] = { 1881da177e4SLinus Torvalds /* Soundblaster awe io port quirk */ 1891da177e4SLinus Torvalds {"CTL0021", quirk_awe32_resources}, 1901da177e4SLinus Torvalds {"CTL0022", quirk_awe32_resources}, 1911da177e4SLinus Torvalds {"CTL0023", quirk_awe32_resources}, 1921da177e4SLinus Torvalds /* CMI 8330 interrupt and dma fix */ 1931da177e4SLinus Torvalds {"@X@0001", quirk_cmi8330_resources}, 1941da177e4SLinus Torvalds /* Soundblaster audio device io port range quirk */ 1951da177e4SLinus Torvalds {"CTL0001", quirk_sb16audio_resources}, 1961da177e4SLinus Torvalds {"CTL0031", quirk_sb16audio_resources}, 1971da177e4SLinus Torvalds {"CTL0041", quirk_sb16audio_resources}, 1981da177e4SLinus Torvalds {"CTL0042", quirk_sb16audio_resources}, 1991da177e4SLinus Torvalds {"CTL0043", quirk_sb16audio_resources}, 2001da177e4SLinus Torvalds {"CTL0044", quirk_sb16audio_resources}, 2011da177e4SLinus Torvalds {"CTL0045", quirk_sb16audio_resources}, 2020509ad5eSBjorn Helgaas {"PNP0c01", quirk_system_pci_resources}, 2030509ad5eSBjorn Helgaas {"PNP0c02", quirk_system_pci_resources}, 2041da177e4SLinus Torvalds {""} 2051da177e4SLinus Torvalds }; 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds void pnp_fixup_device(struct pnp_dev *dev) 2081da177e4SLinus Torvalds { 2091da177e4SLinus Torvalds int i = 0; 210a05d0781SBjorn Helgaas void (*quirk)(struct pnp_dev *); 2111da177e4SLinus Torvalds 2121da177e4SLinus Torvalds while (*pnp_fixups[i].id) { 2131da177e4SLinus Torvalds if (compare_pnp_id(dev->id, pnp_fixups[i].id)) { 214a05d0781SBjorn Helgaas quirk = pnp_fixups[i].quirk_function; 215a05d0781SBjorn Helgaas 216a05d0781SBjorn Helgaas #ifdef DEBUG 217a05d0781SBjorn Helgaas dev_dbg(&dev->dev, "calling quirk 0x%p", quirk); 218a05d0781SBjorn Helgaas print_fn_descriptor_symbol(": %s()\n", 219a05d0781SBjorn Helgaas (unsigned long) *quirk); 220a05d0781SBjorn Helgaas #endif 221a05d0781SBjorn Helgaas (*quirk)(dev); 2221da177e4SLinus Torvalds } 2231da177e4SLinus Torvalds i++; 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds } 226