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 691da177e4SLinus Torvalds for (irq = res->irq; irq; irq = irq->next) { // Valid irqs are 5, 7, 10 701da177e4SLinus Torvalds tmp = 0x04A0; 711da177e4SLinus Torvalds bitmap_copy(irq->map, &tmp, 16); // 0000 0100 1010 0000 721da177e4SLinus Torvalds } 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds for (dma = res->dma; dma; dma = dma->next) // Valid 8bit dma channels are 1,3 759dd78466SBjorn Helgaas if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) == 769dd78466SBjorn Helgaas IORESOURCE_DMA_8BIT) 771da177e4SLinus Torvalds dma->map = 0x000A; 781da177e4SLinus Torvalds } 791e3832b0SBjorn Helgaas dev_info(&dev->dev, "CMI8330 quirk - forced possible IRQs to 5, 7, 10 " 801e3832b0SBjorn Helgaas "and DMA channels to 1, 3\n"); 811da177e4SLinus Torvalds } 821da177e4SLinus Torvalds 831da177e4SLinus Torvalds static void quirk_sb16audio_resources(struct pnp_dev *dev) 841da177e4SLinus Torvalds { 851da177e4SLinus Torvalds struct pnp_port *port; 861da177e4SLinus Torvalds struct pnp_option *res = dev->dependent; 871da177e4SLinus Torvalds int changed = 0; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds /* 901da177e4SLinus Torvalds * The default range on the mpu port for these devices is 0x388-0x388. 911da177e4SLinus Torvalds * Here we increase that range so that two such cards can be 921da177e4SLinus Torvalds * auto-configured. 931da177e4SLinus Torvalds */ 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds for (; res; res = res->next) { 961da177e4SLinus Torvalds port = res->port; 971da177e4SLinus Torvalds if (!port) 981da177e4SLinus Torvalds continue; 991da177e4SLinus Torvalds port = port->next; 1001da177e4SLinus Torvalds if (!port) 1011da177e4SLinus Torvalds continue; 1021da177e4SLinus Torvalds port = port->next; 1031da177e4SLinus Torvalds if (!port) 1041da177e4SLinus Torvalds continue; 1051da177e4SLinus Torvalds if (port->min != port->max) 1061da177e4SLinus Torvalds continue; 1071da177e4SLinus Torvalds port->max += 0x70; 1081da177e4SLinus Torvalds changed = 1; 1091da177e4SLinus Torvalds } 1101da177e4SLinus Torvalds if (changed) 1111e3832b0SBjorn Helgaas dev_info(&dev->dev, "SB audio device quirk - increased port range\n"); 1121da177e4SLinus Torvalds } 1131da177e4SLinus Torvalds 1140509ad5eSBjorn Helgaas 1150509ad5eSBjorn Helgaas #include <linux/pci.h> 1160509ad5eSBjorn Helgaas 1170509ad5eSBjorn Helgaas static void quirk_system_pci_resources(struct pnp_dev *dev) 1180509ad5eSBjorn Helgaas { 1190509ad5eSBjorn Helgaas struct pci_dev *pdev = NULL; 12053052febSBjorn Helgaas struct resource *res; 1210509ad5eSBjorn Helgaas resource_size_t pnp_start, pnp_end, pci_start, pci_end; 1220509ad5eSBjorn Helgaas int i, j; 1230509ad5eSBjorn Helgaas 1240509ad5eSBjorn Helgaas /* 1250509ad5eSBjorn Helgaas * Some BIOSes have PNP motherboard devices with resources that 1260509ad5eSBjorn Helgaas * partially overlap PCI BARs. The PNP system driver claims these 1270509ad5eSBjorn Helgaas * motherboard resources, which prevents the normal PCI driver from 1280509ad5eSBjorn Helgaas * requesting them later. 1290509ad5eSBjorn Helgaas * 1300509ad5eSBjorn Helgaas * This patch disables the PNP resources that conflict with PCI BARs 1310509ad5eSBjorn Helgaas * so they won't be claimed by the PNP system driver. 1320509ad5eSBjorn Helgaas */ 1330509ad5eSBjorn Helgaas for_each_pci_dev(pdev) { 1340509ad5eSBjorn Helgaas for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 1350509ad5eSBjorn Helgaas if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM) || 1360509ad5eSBjorn Helgaas pci_resource_len(pdev, i) == 0) 1370509ad5eSBjorn Helgaas continue; 1380509ad5eSBjorn Helgaas 1390509ad5eSBjorn Helgaas pci_start = pci_resource_start(pdev, i); 1400509ad5eSBjorn Helgaas pci_end = pci_resource_end(pdev, i); 14195ab3669SBjorn Helgaas for (j = 0; 14295ab3669SBjorn Helgaas (res = pnp_get_resource(dev, IORESOURCE_MEM, j)); 14395ab3669SBjorn Helgaas j++) { 14495ab3669SBjorn Helgaas if (res->flags & IORESOURCE_UNSET || 14595ab3669SBjorn Helgaas (res->start == 0 && res->end == 0)) 1460509ad5eSBjorn Helgaas continue; 1470509ad5eSBjorn Helgaas 14895ab3669SBjorn Helgaas pnp_start = res->start; 14995ab3669SBjorn Helgaas pnp_end = res->end; 1500509ad5eSBjorn Helgaas 1510509ad5eSBjorn Helgaas /* 1520509ad5eSBjorn Helgaas * If the PNP region doesn't overlap the PCI 1530509ad5eSBjorn Helgaas * region at all, there's no problem. 1540509ad5eSBjorn Helgaas */ 1550509ad5eSBjorn Helgaas if (pnp_end < pci_start || pnp_start > pci_end) 1560509ad5eSBjorn Helgaas continue; 1570509ad5eSBjorn Helgaas 1580509ad5eSBjorn Helgaas /* 1590509ad5eSBjorn Helgaas * If the PNP region completely encloses (or is 1600509ad5eSBjorn Helgaas * at least as large as) the PCI region, that's 1610509ad5eSBjorn Helgaas * also OK. For example, this happens when the 1620509ad5eSBjorn Helgaas * PNP device describes a bridge with PCI 1630509ad5eSBjorn Helgaas * behind it. 1640509ad5eSBjorn Helgaas */ 1650509ad5eSBjorn Helgaas if (pnp_start <= pci_start && 1660509ad5eSBjorn Helgaas pnp_end >= pci_end) 1670509ad5eSBjorn Helgaas continue; 1680509ad5eSBjorn Helgaas 1690509ad5eSBjorn Helgaas /* 1700509ad5eSBjorn Helgaas * Otherwise, the PNP region overlaps *part* of 1710509ad5eSBjorn Helgaas * the PCI region, and that might prevent a PCI 1720509ad5eSBjorn Helgaas * driver from requesting its resources. 1730509ad5eSBjorn Helgaas */ 1740509ad5eSBjorn Helgaas dev_warn(&dev->dev, "mem resource " 1750509ad5eSBjorn Helgaas "(0x%llx-0x%llx) overlaps %s BAR %d " 1760509ad5eSBjorn Helgaas "(0x%llx-0x%llx), disabling\n", 1770509ad5eSBjorn Helgaas (unsigned long long) pnp_start, 1780509ad5eSBjorn Helgaas (unsigned long long) pnp_end, 1790509ad5eSBjorn Helgaas pci_name(pdev), i, 1800509ad5eSBjorn Helgaas (unsigned long long) pci_start, 1810509ad5eSBjorn Helgaas (unsigned long long) pci_end); 18253052febSBjorn Helgaas res->flags = 0; 1830509ad5eSBjorn Helgaas } 1840509ad5eSBjorn Helgaas } 1850509ad5eSBjorn Helgaas } 1860509ad5eSBjorn Helgaas } 1870509ad5eSBjorn Helgaas 1881da177e4SLinus Torvalds /* 1891da177e4SLinus Torvalds * PnP Quirks 1901da177e4SLinus Torvalds * Cards or devices that need some tweaking due to incomplete resource info 1911da177e4SLinus Torvalds */ 1921da177e4SLinus Torvalds 1931da177e4SLinus Torvalds static struct pnp_fixup pnp_fixups[] = { 1941da177e4SLinus Torvalds /* Soundblaster awe io port quirk */ 1951da177e4SLinus Torvalds {"CTL0021", quirk_awe32_resources}, 1961da177e4SLinus Torvalds {"CTL0022", quirk_awe32_resources}, 1971da177e4SLinus Torvalds {"CTL0023", quirk_awe32_resources}, 1981da177e4SLinus Torvalds /* CMI 8330 interrupt and dma fix */ 1991da177e4SLinus Torvalds {"@X@0001", quirk_cmi8330_resources}, 2001da177e4SLinus Torvalds /* Soundblaster audio device io port range quirk */ 2011da177e4SLinus Torvalds {"CTL0001", quirk_sb16audio_resources}, 2021da177e4SLinus Torvalds {"CTL0031", quirk_sb16audio_resources}, 2031da177e4SLinus Torvalds {"CTL0041", quirk_sb16audio_resources}, 2041da177e4SLinus Torvalds {"CTL0042", quirk_sb16audio_resources}, 2051da177e4SLinus Torvalds {"CTL0043", quirk_sb16audio_resources}, 2061da177e4SLinus Torvalds {"CTL0044", quirk_sb16audio_resources}, 2071da177e4SLinus Torvalds {"CTL0045", quirk_sb16audio_resources}, 2080509ad5eSBjorn Helgaas {"PNP0c01", quirk_system_pci_resources}, 2090509ad5eSBjorn Helgaas {"PNP0c02", quirk_system_pci_resources}, 2101da177e4SLinus Torvalds {""} 2111da177e4SLinus Torvalds }; 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds void pnp_fixup_device(struct pnp_dev *dev) 2141da177e4SLinus Torvalds { 215726a7a3dSRene Herman struct pnp_fixup *f; 2161da177e4SLinus Torvalds 217726a7a3dSRene Herman for (f = pnp_fixups; *f->id; f++) { 218726a7a3dSRene Herman if (!compare_pnp_id(dev->id, f->id)) 219726a7a3dSRene Herman continue; 220a05d0781SBjorn Helgaas #ifdef DEBUG 221726a7a3dSRene Herman dev_dbg(&dev->dev, "%s: calling ", f->id); 222726a7a3dSRene Herman print_fn_descriptor_symbol("%s\n", 223726a7a3dSRene Herman (unsigned long) f->quirk_function); 224a05d0781SBjorn Helgaas #endif 225726a7a3dSRene Herman f->quirk_function(dev); 2261da177e4SLinus Torvalds } 2271da177e4SLinus Torvalds } 228