xref: /linux/drivers/pnp/quirks.c (revision 14b42963f64b98ab61fa9723c03d71aa5ef4f862)
1 /*
2  *  This file contains quirk handling code for PnP devices
3  *  Some devices do not report all their resources, and need to have extra
4  *  resources added. This is most easily accomplished at initialisation time
5  *  when building up the resource structure for the first time.
6  *
7  *  Copyright (c) 2000 Peter Denison <peterd@pnd-pc.demon.co.uk>
8  *
9  *  Heavily based on PCI quirks handling which is
10  *
11  *  Copyright (c) 1999 Martin Mares <mj@ucw.cz>
12  */
13 
14 #include <linux/types.h>
15 #include <linux/kernel.h>
16 #include <linux/string.h>
17 #include <linux/slab.h>
18 #include <linux/pnp.h>
19 #include "base.h"
20 
21 
22 static void quirk_awe32_resources(struct pnp_dev *dev)
23 {
24 	struct pnp_port *port, *port2, *port3;
25 	struct pnp_option *res = dev->dependent;
26 
27 	/*
28 	 * Unfortunately the isapnp_add_port_resource is too tightly bound
29 	 * into the PnP discovery sequence, and cannot be used. Link in the
30 	 * two extra ports (at offset 0x400 and 0x800 from the one given) by
31 	 * hand.
32 	 */
33 	for ( ; res ; res = res->next ) {
34 		port2 = pnp_alloc(sizeof(struct pnp_port));
35 		if (!port2)
36 			return;
37 		port3 = pnp_alloc(sizeof(struct pnp_port));
38 		if (!port3) {
39 			kfree(port2);
40 			return;
41 		}
42 		port = res->port;
43 		memcpy(port2, port, sizeof(struct pnp_port));
44 		memcpy(port3, port, sizeof(struct pnp_port));
45 		port->next = port2;
46 		port2->next = port3;
47 		port2->min += 0x400;
48 		port2->max += 0x400;
49 		port3->min += 0x800;
50 		port3->max += 0x800;
51 	}
52 	printk(KERN_INFO "pnp: AWE32 quirk - adding two ports\n");
53 }
54 
55 static void quirk_cmi8330_resources(struct pnp_dev *dev)
56 {
57 	struct pnp_option *res = dev->dependent;
58 	unsigned long tmp;
59 
60 	for ( ; res ; res = res->next ) {
61 
62 		struct pnp_irq *irq;
63 		struct pnp_dma *dma;
64 
65 		for( irq = res->irq; irq; irq = irq->next ) {	// Valid irqs are 5, 7, 10
66 			tmp = 0x04A0;
67 			bitmap_copy(irq->map, &tmp, 16);	// 0000 0100 1010 0000
68 		}
69 
70 		for( dma = res->dma; dma; dma = dma->next ) // Valid 8bit dma channels are 1,3
71 			if( ( dma->flags & IORESOURCE_DMA_TYPE_MASK ) == IORESOURCE_DMA_8BIT )
72 				dma->map = 0x000A;
73 	}
74 	printk(KERN_INFO "pnp: CMI8330 quirk - fixing interrupts and dma\n");
75 }
76 
77 static void quirk_sb16audio_resources(struct pnp_dev *dev)
78 {
79 	struct pnp_port *port;
80 	struct pnp_option *res = dev->dependent;
81 	int    changed = 0;
82 
83 	/*
84 	 * The default range on the mpu port for these devices is 0x388-0x388.
85 	 * Here we increase that range so that two such cards can be
86 	 * auto-configured.
87 	 */
88 
89 	for( ; res ; res = res->next ) {
90 		port = res->port;
91 		if(!port)
92 			continue;
93 		port = port->next;
94 		if(!port)
95 			continue;
96 		port = port->next;
97 		if(!port)
98 			continue;
99 		if(port->min != port->max)
100 			continue;
101 		port->max += 0x70;
102 		changed = 1;
103 	}
104 	if(changed)
105 		printk(KERN_INFO "pnp: SB audio device quirk - increasing port range\n");
106 	return;
107 }
108 
109 /*
110  *  PnP Quirks
111  *  Cards or devices that need some tweaking due to incomplete resource info
112  */
113 
114 static struct pnp_fixup pnp_fixups[] = {
115 	/* Soundblaster awe io port quirk */
116 	{ "CTL0021", quirk_awe32_resources },
117 	{ "CTL0022", quirk_awe32_resources },
118 	{ "CTL0023", quirk_awe32_resources },
119 	/* CMI 8330 interrupt and dma fix */
120 	{ "@X@0001", quirk_cmi8330_resources },
121 	/* Soundblaster audio device io port range quirk */
122 	{ "CTL0001", quirk_sb16audio_resources },
123 	{ "CTL0031", quirk_sb16audio_resources },
124 	{ "CTL0041", quirk_sb16audio_resources },
125 	{ "CTL0042", quirk_sb16audio_resources },
126 	{ "CTL0043", quirk_sb16audio_resources },
127 	{ "CTL0044", quirk_sb16audio_resources },
128 	{ "CTL0045", quirk_sb16audio_resources },
129 	{ "" }
130 };
131 
132 void pnp_fixup_device(struct pnp_dev *dev)
133 {
134 	int i = 0;
135 
136 	while (*pnp_fixups[i].id) {
137 		if (compare_pnp_id(dev->id,pnp_fixups[i].id)) {
138 			pnp_dbg("Calling quirk for %s",
139 		                  dev->dev.bus_id);
140 			pnp_fixups[i].quirk_function(dev);
141 		}
142 		i++;
143 	}
144 }
145