xref: /openbmc/linux/drivers/pnp/quirks.c (revision 0509ad5e)
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