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 1143b73a223SRene Herman static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev) 1153b73a223SRene Herman { 1163b73a223SRene Herman struct pnp_option *head = NULL; 1173b73a223SRene Herman struct pnp_option *prev = NULL; 1183b73a223SRene Herman struct pnp_option *res; 1193b73a223SRene Herman 1203b73a223SRene Herman /* 1213b73a223SRene Herman * Build a functional IRQ-less variant of each MPU option. 1223b73a223SRene Herman */ 1233b73a223SRene Herman 1243b73a223SRene Herman for (res = dev->dependent; res; res = res->next) { 1253b73a223SRene Herman struct pnp_option *curr; 1263b73a223SRene Herman struct pnp_port *port; 1273b73a223SRene Herman struct pnp_port *copy; 1283b73a223SRene Herman 1293b73a223SRene Herman port = res->port; 1303b73a223SRene Herman if (!port || !res->irq) 1313b73a223SRene Herman continue; 1323b73a223SRene Herman 1333b73a223SRene Herman copy = pnp_alloc(sizeof *copy); 1343b73a223SRene Herman if (!copy) 1353b73a223SRene Herman break; 1363b73a223SRene Herman 1373b73a223SRene Herman copy->min = port->min; 1383b73a223SRene Herman copy->max = port->max; 1393b73a223SRene Herman copy->align = port->align; 1403b73a223SRene Herman copy->size = port->size; 1413b73a223SRene Herman copy->flags = port->flags; 1423b73a223SRene Herman 1433b73a223SRene Herman curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL); 1443b73a223SRene Herman if (!curr) { 1453b73a223SRene Herman kfree(copy); 1463b73a223SRene Herman break; 1473b73a223SRene Herman } 1483b73a223SRene Herman curr->port = copy; 1493b73a223SRene Herman 1503b73a223SRene Herman if (prev) 1513b73a223SRene Herman prev->next = curr; 1523b73a223SRene Herman else 1533b73a223SRene Herman head = curr; 1543b73a223SRene Herman prev = curr; 1553b73a223SRene Herman } 1563b73a223SRene Herman if (head) 1573b73a223SRene Herman dev_info(&dev->dev, "adding IRQ-less MPU options\n"); 1583b73a223SRene Herman 1593b73a223SRene Herman return head; 1603b73a223SRene Herman } 1613b73a223SRene Herman 1623b73a223SRene Herman static void quirk_ad1815_mpu_resources(struct pnp_dev *dev) 1633b73a223SRene Herman { 1643b73a223SRene Herman struct pnp_option *res; 1653b73a223SRene Herman struct pnp_irq *irq; 1663b73a223SRene Herman 1673b73a223SRene Herman /* 1683b73a223SRene Herman * Distribute the independent IRQ over the dependent options 1693b73a223SRene Herman */ 1703b73a223SRene Herman 1713b73a223SRene Herman res = dev->independent; 1723b73a223SRene Herman if (!res) 1733b73a223SRene Herman return; 1743b73a223SRene Herman 1753b73a223SRene Herman irq = res->irq; 1763b73a223SRene Herman if (!irq || irq->next) 1773b73a223SRene Herman return; 1783b73a223SRene Herman 1793b73a223SRene Herman res = dev->dependent; 1803b73a223SRene Herman if (!res) 1813b73a223SRene Herman return; 1823b73a223SRene Herman 1833b73a223SRene Herman while (1) { 1843b73a223SRene Herman struct pnp_irq *copy; 1853b73a223SRene Herman 1863b73a223SRene Herman copy = pnp_alloc(sizeof *copy); 1873b73a223SRene Herman if (!copy) 1883b73a223SRene Herman break; 1893b73a223SRene Herman 1903b73a223SRene Herman memcpy(copy->map, irq->map, sizeof copy->map); 1913b73a223SRene Herman copy->flags = irq->flags; 1923b73a223SRene Herman 1933b73a223SRene Herman copy->next = res->irq; /* Yes, this is NULL */ 1943b73a223SRene Herman res->irq = copy; 1953b73a223SRene Herman 1963b73a223SRene Herman if (!res->next) 1973b73a223SRene Herman break; 1983b73a223SRene Herman res = res->next; 1993b73a223SRene Herman } 2003b73a223SRene Herman kfree(irq); 2013b73a223SRene Herman 2023b73a223SRene Herman res->next = quirk_isapnp_mpu_options(dev); 2033b73a223SRene Herman 2043b73a223SRene Herman res = dev->independent; 2053b73a223SRene Herman res->irq = NULL; 2063b73a223SRene Herman } 2073b73a223SRene Herman 2083b73a223SRene Herman static void quirk_isapnp_mpu_resources(struct pnp_dev *dev) 2093b73a223SRene Herman { 2103b73a223SRene Herman struct pnp_option *res; 2113b73a223SRene Herman 2123b73a223SRene Herman res = dev->dependent; 2133b73a223SRene Herman if (!res) 2143b73a223SRene Herman return; 2153b73a223SRene Herman 2163b73a223SRene Herman while (res->next) 2173b73a223SRene Herman res = res->next; 2183b73a223SRene Herman 2193b73a223SRene Herman res->next = quirk_isapnp_mpu_options(dev); 2203b73a223SRene Herman } 2210509ad5eSBjorn Helgaas 2220509ad5eSBjorn Helgaas #include <linux/pci.h> 2230509ad5eSBjorn Helgaas 2240509ad5eSBjorn Helgaas static void quirk_system_pci_resources(struct pnp_dev *dev) 2250509ad5eSBjorn Helgaas { 2260509ad5eSBjorn Helgaas struct pci_dev *pdev = NULL; 22753052febSBjorn Helgaas struct resource *res; 2280509ad5eSBjorn Helgaas resource_size_t pnp_start, pnp_end, pci_start, pci_end; 2290509ad5eSBjorn Helgaas int i, j; 2300509ad5eSBjorn Helgaas 2310509ad5eSBjorn Helgaas /* 2320509ad5eSBjorn Helgaas * Some BIOSes have PNP motherboard devices with resources that 2330509ad5eSBjorn Helgaas * partially overlap PCI BARs. The PNP system driver claims these 2340509ad5eSBjorn Helgaas * motherboard resources, which prevents the normal PCI driver from 2350509ad5eSBjorn Helgaas * requesting them later. 2360509ad5eSBjorn Helgaas * 2370509ad5eSBjorn Helgaas * This patch disables the PNP resources that conflict with PCI BARs 2380509ad5eSBjorn Helgaas * so they won't be claimed by the PNP system driver. 2390509ad5eSBjorn Helgaas */ 2400509ad5eSBjorn Helgaas for_each_pci_dev(pdev) { 2410509ad5eSBjorn Helgaas for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { 2420509ad5eSBjorn Helgaas if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM) || 2430509ad5eSBjorn Helgaas pci_resource_len(pdev, i) == 0) 2440509ad5eSBjorn Helgaas continue; 2450509ad5eSBjorn Helgaas 2460509ad5eSBjorn Helgaas pci_start = pci_resource_start(pdev, i); 2470509ad5eSBjorn Helgaas pci_end = pci_resource_end(pdev, i); 24895ab3669SBjorn Helgaas for (j = 0; 24995ab3669SBjorn Helgaas (res = pnp_get_resource(dev, IORESOURCE_MEM, j)); 25095ab3669SBjorn Helgaas j++) { 25195ab3669SBjorn Helgaas if (res->flags & IORESOURCE_UNSET || 25295ab3669SBjorn Helgaas (res->start == 0 && res->end == 0)) 2530509ad5eSBjorn Helgaas continue; 2540509ad5eSBjorn Helgaas 25595ab3669SBjorn Helgaas pnp_start = res->start; 25695ab3669SBjorn Helgaas pnp_end = res->end; 2570509ad5eSBjorn Helgaas 2580509ad5eSBjorn Helgaas /* 2590509ad5eSBjorn Helgaas * If the PNP region doesn't overlap the PCI 2600509ad5eSBjorn Helgaas * region at all, there's no problem. 2610509ad5eSBjorn Helgaas */ 2620509ad5eSBjorn Helgaas if (pnp_end < pci_start || pnp_start > pci_end) 2630509ad5eSBjorn Helgaas continue; 2640509ad5eSBjorn Helgaas 2650509ad5eSBjorn Helgaas /* 2660509ad5eSBjorn Helgaas * If the PNP region completely encloses (or is 2670509ad5eSBjorn Helgaas * at least as large as) the PCI region, that's 2680509ad5eSBjorn Helgaas * also OK. For example, this happens when the 2690509ad5eSBjorn Helgaas * PNP device describes a bridge with PCI 2700509ad5eSBjorn Helgaas * behind it. 2710509ad5eSBjorn Helgaas */ 2720509ad5eSBjorn Helgaas if (pnp_start <= pci_start && 2730509ad5eSBjorn Helgaas pnp_end >= pci_end) 2740509ad5eSBjorn Helgaas continue; 2750509ad5eSBjorn Helgaas 2760509ad5eSBjorn Helgaas /* 2770509ad5eSBjorn Helgaas * Otherwise, the PNP region overlaps *part* of 2780509ad5eSBjorn Helgaas * the PCI region, and that might prevent a PCI 2790509ad5eSBjorn Helgaas * driver from requesting its resources. 2800509ad5eSBjorn Helgaas */ 2810509ad5eSBjorn Helgaas dev_warn(&dev->dev, "mem resource " 2820509ad5eSBjorn Helgaas "(0x%llx-0x%llx) overlaps %s BAR %d " 2830509ad5eSBjorn Helgaas "(0x%llx-0x%llx), disabling\n", 2840509ad5eSBjorn Helgaas (unsigned long long) pnp_start, 2850509ad5eSBjorn Helgaas (unsigned long long) pnp_end, 2860509ad5eSBjorn Helgaas pci_name(pdev), i, 2870509ad5eSBjorn Helgaas (unsigned long long) pci_start, 2880509ad5eSBjorn Helgaas (unsigned long long) pci_end); 28953052febSBjorn Helgaas res->flags = 0; 2900509ad5eSBjorn Helgaas } 2910509ad5eSBjorn Helgaas } 2920509ad5eSBjorn Helgaas } 2930509ad5eSBjorn Helgaas } 2940509ad5eSBjorn Helgaas 2951da177e4SLinus Torvalds /* 2961da177e4SLinus Torvalds * PnP Quirks 2971da177e4SLinus Torvalds * Cards or devices that need some tweaking due to incomplete resource info 2981da177e4SLinus Torvalds */ 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds static struct pnp_fixup pnp_fixups[] = { 3011da177e4SLinus Torvalds /* Soundblaster awe io port quirk */ 3021da177e4SLinus Torvalds {"CTL0021", quirk_awe32_resources}, 3031da177e4SLinus Torvalds {"CTL0022", quirk_awe32_resources}, 3041da177e4SLinus Torvalds {"CTL0023", quirk_awe32_resources}, 3051da177e4SLinus Torvalds /* CMI 8330 interrupt and dma fix */ 3061da177e4SLinus Torvalds {"@X@0001", quirk_cmi8330_resources}, 3071da177e4SLinus Torvalds /* Soundblaster audio device io port range quirk */ 3081da177e4SLinus Torvalds {"CTL0001", quirk_sb16audio_resources}, 3091da177e4SLinus Torvalds {"CTL0031", quirk_sb16audio_resources}, 3101da177e4SLinus Torvalds {"CTL0041", quirk_sb16audio_resources}, 3111da177e4SLinus Torvalds {"CTL0042", quirk_sb16audio_resources}, 3121da177e4SLinus Torvalds {"CTL0043", quirk_sb16audio_resources}, 3131da177e4SLinus Torvalds {"CTL0044", quirk_sb16audio_resources}, 3141da177e4SLinus Torvalds {"CTL0045", quirk_sb16audio_resources}, 3153b73a223SRene Herman /* Add IRQ-less MPU options */ 3163b73a223SRene Herman {"ADS7151", quirk_ad1815_mpu_resources}, 3173b73a223SRene Herman {"ADS7181", quirk_isapnp_mpu_resources}, 3183b73a223SRene Herman {"AZT0002", quirk_isapnp_mpu_resources}, 3193b73a223SRene Herman /* PnP resources that might overlap PCI BARs */ 3200509ad5eSBjorn Helgaas {"PNP0c01", quirk_system_pci_resources}, 3210509ad5eSBjorn Helgaas {"PNP0c02", quirk_system_pci_resources}, 3221da177e4SLinus Torvalds {""} 3231da177e4SLinus Torvalds }; 3241da177e4SLinus Torvalds 3251da177e4SLinus Torvalds void pnp_fixup_device(struct pnp_dev *dev) 3261da177e4SLinus Torvalds { 327726a7a3dSRene Herman struct pnp_fixup *f; 3281da177e4SLinus Torvalds 329726a7a3dSRene Herman for (f = pnp_fixups; *f->id; f++) { 330726a7a3dSRene Herman if (!compare_pnp_id(dev->id, f->id)) 331726a7a3dSRene Herman continue; 332a05d0781SBjorn Helgaas #ifdef DEBUG 333726a7a3dSRene Herman dev_dbg(&dev->dev, "%s: calling ", f->id); 334a442ac51SLinus Torvalds print_fn_descriptor_symbol("%s\n", f->quirk_function); 335a05d0781SBjorn Helgaas #endif 336726a7a3dSRene Herman f->quirk_function(dev); 3371da177e4SLinus Torvalds } 3381da177e4SLinus Torvalds } 339