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