Blame view

drivers/pnp/quirks.c 9.09 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   *  This file contains quirk handling code for PnP devices
   *  Some devices do not report all their resources, and need to have extra
   *  resources added. This is most easily accomplished at initialisation time
   *  when building up the resource structure for the first time.
   *
   *  Copyright (c) 2000 Peter Denison <peterd@pnd-pc.demon.co.uk>
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
8
9
   *  Copyright (C) 2008 Hewlett-Packard Development Company, L.P.
   *	Bjorn Helgaas <bjorn.helgaas@hp.com>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10
11
12
13
14
   *
   *  Heavily based on PCI quirks handling which is
   *
   *  Copyright (c) 1999 Martin Mares <mj@ucw.cz>
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
17
18
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/string.h>
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19
  #include <linux/pnp.h>
a1e7e636f   Bjorn Helgaas   PNP: workaround H...
20
  #include <linux/io.h>
a05d07816   Bjorn Helgaas   PNP: use dev_info...
21
  #include <linux/kallsyms.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
22
  #include "base.h"
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  static void quirk_awe32_add_ports(struct pnp_dev *dev,
  				  struct pnp_option *option,
  				  unsigned int offset)
  {
  	struct pnp_option *new_option;
  
  	new_option = kmalloc(sizeof(struct pnp_option), GFP_KERNEL);
  	if (!new_option) {
  		dev_err(&dev->dev, "couldn't add ioport region to option set "
  			"%d
  ", pnp_option_set(option));
  		return;
  	}
  
  	*new_option = *option;
  	new_option->u.port.min += offset;
  	new_option->u.port.max += offset;
  	list_add(&new_option->list, &option->list);
  
  	dev_info(&dev->dev, "added ioport region %#llx-%#llx to set %d
  ",
  		(unsigned long long) new_option->u.port.min,
  		(unsigned long long) new_option->u.port.max,
  		pnp_option_set(option));
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
  static void quirk_awe32_resources(struct pnp_dev *dev)
  {
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
50
51
  	struct pnp_option *option;
  	unsigned int set = ~0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
52
53
  
  	/*
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
54
55
  	 * Add two extra ioport regions (at offset 0x400 and 0x800 from the
  	 * one given) to every dependent option set.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
56
  	 */
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
57
58
59
60
61
62
  	list_for_each_entry(option, &dev->options, list) {
  		if (pnp_option_is_dependent(option) &&
  		    pnp_option_set(option) != set) {
  			set = pnp_option_set(option);
  			quirk_awe32_add_ports(dev, option, 0x800);
  			quirk_awe32_add_ports(dev, option, 0x400);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
63
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
65
66
67
68
  }
  
  static void quirk_cmi8330_resources(struct pnp_dev *dev)
  {
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
69
70
71
  	struct pnp_option *option;
  	struct pnp_irq *irq;
  	struct pnp_dma *dma;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
72

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
73
74
75
  	list_for_each_entry(option, &dev->options, list) {
  		if (!pnp_option_is_dependent(option))
  			continue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
77
78
79
80
81
82
83
84
85
86
87
88
  		if (option->type == IORESOURCE_IRQ) {
  			irq = &option->u.irq;
  			bitmap_zero(irq->map.bits, PNP_IRQ_NR);
  			__set_bit(5, irq->map.bits);
  			__set_bit(7, irq->map.bits);
  			__set_bit(10, irq->map.bits);
  			dev_info(&dev->dev, "set possible IRQs in "
  				 "option set %d to 5, 7, 10
  ",
  				 pnp_option_set(option));
  		} else if (option->type == IORESOURCE_DMA) {
  			dma = &option->u.dma;
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
89
  			if ((dma->flags & IORESOURCE_DMA_TYPE_MASK) ==
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
90
91
92
93
94
95
96
97
98
  						IORESOURCE_DMA_8BIT &&
  			    dma->map != 0x0A) {
  				dev_info(&dev->dev, "changing possible "
  					 "DMA channel mask in option set %d "
  					 "from %#02x to 0x0A (1, 3)
  ",
  					 pnp_option_set(option), dma->map);
  				dma->map = 0x0A;
  			}
7aefff518   Bjorn Helgaas   PNP: introduce pn...
99
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
101
102
103
104
  }
  
  static void quirk_sb16audio_resources(struct pnp_dev *dev)
  {
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
105
106
  	struct pnp_option *option;
  	unsigned int prev_option_flags = ~0, n = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  	struct pnp_port *port;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
108
109
  
  	/*
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
110
  	 * The default range on the OPL port for these devices is 0x388-0x388.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
111
112
113
  	 * Here we increase that range so that two such cards can be
  	 * auto-configured.
  	 */
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
114
115
116
117
118
  	list_for_each_entry(option, &dev->options, list) {
  		if (prev_option_flags != option->flags) {
  			prev_option_flags = option->flags;
  			n = 0;
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
119

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
  		if (pnp_option_is_dependent(option) &&
  		    option->type == IORESOURCE_IO) {
  			n++;
  			port = &option->u.port;
  			if (n == 3 && port->min == port->max) {
  				port->max += 0x70;
  				dev_info(&dev->dev, "increased option port "
  					 "range from %#llx-%#llx to "
  					 "%#llx-%#llx
  ",
  					 (unsigned long long) port->min,
  					 (unsigned long long) port->min,
  					 (unsigned long long) port->min,
  					 (unsigned long long) port->max);
  			}
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
136
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
137
  }
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
138
139
  static struct pnp_option *pnp_clone_dependent_set(struct pnp_dev *dev,
  						  unsigned int set)
3b73a2236   Rene Herman   pnp: add ISAPnP M...
140
  {
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
141
142
143
  	struct pnp_option *tail = NULL, *first_new_option = NULL;
  	struct pnp_option *option, *new_option;
  	unsigned int flags;
3b73a2236   Rene Herman   pnp: add ISAPnP M...
144

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
145
146
147
148
149
150
151
152
153
  	list_for_each_entry(option, &dev->options, list) {
  		if (pnp_option_is_dependent(option))
  			tail = option;
  	}
  	if (!tail) {
  		dev_err(&dev->dev, "no dependent option sets
  ");
  		return NULL;
  	}
d5ebde6ef   Bjorn Helgaas   PNP: support opti...
154

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
155
156
157
158
159
160
161
162
163
164
165
166
  	flags = pnp_new_dependent_set(dev, PNP_RES_PRIORITY_FUNCTIONAL);
  	list_for_each_entry(option, &dev->options, list) {
  		if (pnp_option_is_dependent(option) &&
  		    pnp_option_set(option) == set) {
  			new_option = kmalloc(sizeof(struct pnp_option),
  					     GFP_KERNEL);
  			if (!new_option) {
  				dev_err(&dev->dev, "couldn't clone dependent "
  					"set %d
  ", set);
  				return NULL;
  			}
3b73a2236   Rene Herman   pnp: add ISAPnP M...
167

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
168
169
170
171
  			*new_option = *option;
  			new_option->flags = flags;
  			if (!first_new_option)
  				first_new_option = new_option;
3b73a2236   Rene Herman   pnp: add ISAPnP M...
172

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
173
174
  			list_add(&new_option->list, &tail->list);
  			tail = new_option;
3b73a2236   Rene Herman   pnp: add ISAPnP M...
175
  		}
3b73a2236   Rene Herman   pnp: add ISAPnP M...
176
  	}
3b73a2236   Rene Herman   pnp: add ISAPnP M...
177

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
178
  	return first_new_option;
3b73a2236   Rene Herman   pnp: add ISAPnP M...
179
  }
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
180
181
  
  static void quirk_add_irq_optional_dependent_sets(struct pnp_dev *dev)
3b73a2236   Rene Herman   pnp: add ISAPnP M...
182
  {
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
183
184
  	struct pnp_option *new_option;
  	unsigned int num_sets, i, set;
3b73a2236   Rene Herman   pnp: add ISAPnP M...
185
  	struct pnp_irq *irq;
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
186
187
188
189
190
  	num_sets = dev->num_dependent_sets;
  	for (i = 0; i < num_sets; i++) {
  		new_option = pnp_clone_dependent_set(dev, i);
  		if (!new_option)
  			return;
3b73a2236   Rene Herman   pnp: add ISAPnP M...
191

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
192
193
194
195
196
197
198
199
200
201
  		set = pnp_option_set(new_option);
  		while (new_option && pnp_option_set(new_option) == set) {
  			if (new_option->type == IORESOURCE_IRQ) {
  				irq = &new_option->u.irq;
  				irq->flags |= IORESOURCE_IRQ_OPTIONAL;
  			}
  			dbg_pnp_show_option(dev, new_option);
  			new_option = list_entry(new_option->list.next,
  						struct pnp_option, list);
  		}
3b73a2236   Rene Herman   pnp: add ISAPnP M...
202

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
203
204
205
206
  		dev_info(&dev->dev, "added dependent option set %d (same as "
  			 "set %d except IRQ optional)
  ", set, i);
  	}
3b73a2236   Rene Herman   pnp: add ISAPnP M...
207
  }
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
208
  static void quirk_ad1815_mpu_resources(struct pnp_dev *dev)
3b73a2236   Rene Herman   pnp: add ISAPnP M...
209
  {
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
210
211
212
213
214
215
216
217
218
219
220
  	struct pnp_option *option;
  	struct pnp_irq *irq = NULL;
  	unsigned int independent_irqs = 0;
  
  	list_for_each_entry(option, &dev->options, list) {
  		if (option->type == IORESOURCE_IRQ &&
  		    !pnp_option_is_dependent(option)) {
  			independent_irqs++;
  			irq = &option->u.irq;
  		}
  	}
3b73a2236   Rene Herman   pnp: add ISAPnP M...
221

1f32ca31e   Bjorn Helgaas   PNP: convert reso...
222
  	if (independent_irqs != 1)
3b73a2236   Rene Herman   pnp: add ISAPnP M...
223
  		return;
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
224
225
226
  	irq->flags |= IORESOURCE_IRQ_OPTIONAL;
  	dev_info(&dev->dev, "made independent IRQ optional
  ");
3b73a2236   Rene Herman   pnp: add ISAPnP M...
227
  }
0509ad5e1   Bjorn Helgaas   PNP: disable PNP ...
228
229
230
231
232
233
  
  #include <linux/pci.h>
  
  static void quirk_system_pci_resources(struct pnp_dev *dev)
  {
  	struct pci_dev *pdev = NULL;
53052feb6   Bjorn Helgaas   PNP: remove pnp_m...
234
  	struct resource *res;
0509ad5e1   Bjorn Helgaas   PNP: disable PNP ...
235
236
237
238
239
240
241
242
243
244
245
246
247
248
  	resource_size_t pnp_start, pnp_end, pci_start, pci_end;
  	int i, j;
  
  	/*
  	 * Some BIOSes have PNP motherboard devices with resources that
  	 * partially overlap PCI BARs.  The PNP system driver claims these
  	 * motherboard resources, which prevents the normal PCI driver from
  	 * requesting them later.
  	 *
  	 * This patch disables the PNP resources that conflict with PCI BARs
  	 * so they won't be claimed by the PNP system driver.
  	 */
  	for_each_pci_dev(pdev) {
  		for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
b563cf59c   Rene Herman   pnp: make the res...
249
  			unsigned long type;
999ed65ad   Rene Herman   pnp: have quirk_s...
250
251
252
253
  
  			type = pci_resource_flags(pdev, i) &
  					(IORESOURCE_IO | IORESOURCE_MEM);
  			if (!type || pci_resource_len(pdev, i) == 0)
0509ad5e1   Bjorn Helgaas   PNP: disable PNP ...
254
255
256
257
  				continue;
  
  			pci_start = pci_resource_start(pdev, i);
  			pci_end = pci_resource_end(pdev, i);
95ab3669f   Bjorn Helgaas   PNP: remove PNP_M...
258
  			for (j = 0;
999ed65ad   Rene Herman   pnp: have quirk_s...
259
  			     (res = pnp_get_resource(dev, type, j)); j++) {
aee3ad815   Bjorn Helgaas   PNP: replace pnp_...
260
  				if (res->start == 0 && res->end == 0)
0509ad5e1   Bjorn Helgaas   PNP: disable PNP ...
261
  					continue;
95ab3669f   Bjorn Helgaas   PNP: remove PNP_M...
262
263
  				pnp_start = res->start;
  				pnp_end = res->end;
0509ad5e1   Bjorn Helgaas   PNP: disable PNP ...
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
  
  				/*
  				 * If the PNP region doesn't overlap the PCI
  				 * region at all, there's no problem.
  				 */
  				if (pnp_end < pci_start || pnp_start > pci_end)
  					continue;
  
  				/*
  				 * If the PNP region completely encloses (or is
  				 * at least as large as) the PCI region, that's
  				 * also OK.  For example, this happens when the
  				 * PNP device describes a bridge with PCI
  				 * behind it.
  				 */
  				if (pnp_start <= pci_start &&
  				    pnp_end >= pci_end)
  					continue;
  
  				/*
  				 * Otherwise, the PNP region overlaps *part* of
  				 * the PCI region, and that might prevent a PCI
  				 * driver from requesting its resources.
  				 */
c7dabef8a   Bjorn Helgaas   vsprintf: use %pR...
288
289
290
291
  				dev_warn(&dev->dev,
  					 "disabling %pR because it overlaps "
  					 "%s BAR %d %pR
  ", res,
9a007b379   Bjorn Helgaas   PNP: print resour...
292
  					 pci_name(pdev), i, &pdev->resource[i]);
4b34fe156   Bjorn Helgaas   PNP: mark resourc...
293
  				res->flags |= IORESOURCE_DISABLED;
0509ad5e1   Bjorn Helgaas   PNP: disable PNP ...
294
295
296
297
  			}
  		}
  	}
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
298
299
300
301
302
303
304
  /*
   *  PnP Quirks
   *  Cards or devices that need some tweaking due to incomplete resource info
   */
  
  static struct pnp_fixup pnp_fixups[] = {
  	/* Soundblaster awe io port quirk */
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
305
306
307
  	{"CTL0021", quirk_awe32_resources},
  	{"CTL0022", quirk_awe32_resources},
  	{"CTL0023", quirk_awe32_resources},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  	/* CMI 8330 interrupt and dma fix */
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
309
  	{"@X@0001", quirk_cmi8330_resources},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
310
  	/* Soundblaster audio device io port range quirk */
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
311
312
313
314
315
316
317
  	{"CTL0001", quirk_sb16audio_resources},
  	{"CTL0031", quirk_sb16audio_resources},
  	{"CTL0041", quirk_sb16audio_resources},
  	{"CTL0042", quirk_sb16audio_resources},
  	{"CTL0043", quirk_sb16audio_resources},
  	{"CTL0044", quirk_sb16audio_resources},
  	{"CTL0045", quirk_sb16audio_resources},
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
318
  	/* Add IRQ-optional MPU options */
3b73a2236   Rene Herman   pnp: add ISAPnP M...
319
  	{"ADS7151", quirk_ad1815_mpu_resources},
1f32ca31e   Bjorn Helgaas   PNP: convert reso...
320
321
  	{"ADS7181", quirk_add_irq_optional_dependent_sets},
  	{"AZT0002", quirk_add_irq_optional_dependent_sets},
3b73a2236   Rene Herman   pnp: add ISAPnP M...
322
  	/* PnP resources that might overlap PCI BARs */
0509ad5e1   Bjorn Helgaas   PNP: disable PNP ...
323
324
  	{"PNP0c01", quirk_system_pci_resources},
  	{"PNP0c02", quirk_system_pci_resources},
9dd78466c   Bjorn Helgaas   PNP: Lindent all ...
325
  	{""}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
329
  };
  
  void pnp_fixup_device(struct pnp_dev *dev)
  {
726a7a3d1   Rene Herman   pnp: clean up pnp...
330
  	struct pnp_fixup *f;
a05d07816   Bjorn Helgaas   PNP: use dev_info...
331

726a7a3d1   Rene Herman   pnp: clean up pnp...
332
333
334
  	for (f = pnp_fixups; *f->id; f++) {
  		if (!compare_pnp_id(dev->id, f->id))
  			continue;
2f53432c2   Bjorn Helgaas   PNP: convert to u...
335
336
  		pnp_dbg(&dev->dev, "%s: calling %pF
  ", f->id,
668b21de1   Bjorn Helgaas   PNP: use new vspr...
337
  			f->quirk_function);
726a7a3d1   Rene Herman   pnp: clean up pnp...
338
  		f->quirk_function(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
339
340
  	}
  }