Blame view

arch/x86/pci/i386.c 9.39 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  /*
   *	Low-Level PCI Access for i386 machines
   *
   * Copyright 1993, 1994 Drew Eckhardt
   *      Visionary Computing
   *      (Unix and Linux consulting and custom programming)
   *      Drew@Colorado.EDU
   *      +1 (303) 786-7975
   *
   * Drew's work was sponsored by:
   *	iX Multiuser Multitasking Magazine
   *	Hannover, Germany
   *	hm@ix.de
   *
   * Copyright 1997--2000 Martin Mares <mj@ucw.cz>
   *
   * For more information, please consult the following manuals (look at
   * http://www.pcisig.com/ for how to get them):
   *
   * PCI BIOS Specification
   * PCI Local Bus Specification
   * PCI to PCI Bridge Specification
   * PCI System Design Guide
   *
   */
  
  #include <linux/types.h>
  #include <linux/kernel.h>
  #include <linux/pci.h>
  #include <linux/init.h>
  #include <linux/ioport.h>
  #include <linux/errno.h>
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
33
34
35
  #include <linux/bootmem.h>
  
  #include <asm/pat.h>
58f7c9885   Yinghai Lu   x86: split e820 r...
36
  #include <asm/e820.h>
824877111   Jaswinder Singh Rajput   x86, pci: move ar...
37
  #include <asm/pci_x86.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
38

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39

036fff4cf   Gary Hade   PCI: skip ISA ior...
40
41
42
43
  static int
  skip_isa_ioresource_align(struct pci_dev *dev) {
  
  	if ((pci_probe & PCI_CAN_SKIP_ISA_ALIGN) &&
11949255d   Gary Hade   PCI: modify PCI b...
44
  	    !(dev->bus->bridge_ctl & PCI_BRIDGE_CTL_ISA))
036fff4cf   Gary Hade   PCI: skip ISA ior...
45
46
47
  		return 1;
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  /*
   * We need to avoid collisions with `mirrored' VGA ports
   * and other strange ISA hardware, so we always want the
   * addresses to be allocated in the 0x000-0x0ff region
   * modulo 0x400.
   *
   * Why? Because some silly external IO cards only decode
   * the low 10 bits of the IO address. The 0x00-0xff region
   * is reserved for motherboard devices that decode all 16
   * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
   * but we want to try to avoid allocating at 0x2900-0x2bff
   * which might have be mirrored at 0x0100-0x03ff..
   */
  void
  pcibios_align_resource(void *data, struct resource *res,
e31dd6e45   Greg Kroah-Hartman   [PATCH] 64bit res...
63
  			resource_size_t size, resource_size_t align)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
  {
036fff4cf   Gary Hade   PCI: skip ISA ior...
65
  	struct pci_dev *dev = data;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
66
  	if (res->flags & IORESOURCE_IO) {
e31dd6e45   Greg Kroah-Hartman   [PATCH] 64bit res...
67
  		resource_size_t start = res->start;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
68

036fff4cf   Gary Hade   PCI: skip ISA ior...
69
70
  		if (skip_isa_ioresource_align(dev))
  			return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
71
72
73
74
75
76
  		if (start & 0x300) {
  			start = (start + 0x3ff) & ~0x3ff;
  			res->start = start;
  		}
  	}
  }
6c00a61e1   Dave Airlie   intel-agp: add ch...
77
  EXPORT_SYMBOL(pcibios_align_resource);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
  
  /*
   *  Handle resources of PCI devices.  If the world were perfect, we could
   *  just allocate all the resource regions and do nothing more.  It isn't.
   *  On the other hand, we cannot just re-allocate all devices, as it would
   *  require us to know lots of host bridge internals.  So we attempt to
   *  keep as much of the original configuration as possible, but tweak it
   *  when it's found to be wrong.
   *
   *  Known BIOS problems we have to work around:
   *	- I/O or memory regions not configured
   *	- regions configured, but not enabled in the command register
   *	- bogus I/O addresses above 64K used
   *	- expansion ROMs left enabled (this may sound harmless, but given
   *	  the fact the PCI specs explicitly allow address decoders to be
   *	  shared between expansion ROMs and other resource regions, it's
   *	  at least dangerous)
   *
   *  Our solution:
   *	(1) Allocate resources for all buses behind PCI-to-PCI bridges.
   *	    This gives us fixed barriers on where we can allocate.
   *	(2) Allocate resources for all enabled devices.  If there is
   *	    a collision, just mark the resource as unallocated. Also
   *	    disable expansion ROMs during this step.
   *	(3) Try to allocate resources for disabled devices.  If the
   *	    resources were assigned correctly, everything goes well,
   *	    if they weren't, they won't disturb allocation of other
   *	    resources.
   *	(4) Assign new addresses to resources which were either
   *	    not configured at all or misconfigured.  If explicitly
   *	    requested by the user, configure expansion ROM address
   *	    as well.
   */
  
  static void __init pcibios_allocate_bus_resources(struct list_head *bus_list)
  {
  	struct pci_bus *bus;
  	struct pci_dev *dev;
  	int idx;
  	struct resource *r, *pr;
  
  	/* Depth-First Search on bus tree */
  	list_for_each_entry(bus, bus_list, node) {
  		if ((dev = bus->self)) {
7edab2f08   Randy Dunlap   pci/i386: style c...
122
123
  			for (idx = PCI_BRIDGE_RESOURCES;
  			    idx < PCI_NUM_RESOURCES; idx++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
  				r = &dev->resource[idx];
299de0343   Ivan Kokshaysky   [PATCH] PCI: pci_...
125
  				if (!r->flags)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
126
127
  					continue;
  				pr = pci_find_parent_resource(dev, r);
7edab2f08   Randy Dunlap   pci/i386: style c...
128
129
  				if (!r->start || !pr ||
  				    request_resource(pr, r) < 0) {
104bafcfa   Ingo Molnar   PCI: Don't carp a...
130
131
  					dev_info(&dev->dev, "BAR %d: can't allocate resource
  ", idx);
7edab2f08   Randy Dunlap   pci/i386: style c...
132
133
134
135
136
137
  					/*
  					 * Something is wrong with the region.
  					 * Invalidate the resource to prevent
  					 * child resource allocations in this
  					 * range.
  					 */
299de0343   Ivan Kokshaysky   [PATCH] PCI: pci_...
138
139
  					r->flags = 0;
  				}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  			}
  		}
  		pcibios_allocate_bus_resources(&bus->children);
  	}
  }
  
  static void __init pcibios_allocate_resources(int pass)
  {
  	struct pci_dev *dev = NULL;
  	int idx, disabled;
  	u16 command;
  	struct resource *r, *pr;
  
  	for_each_pci_dev(dev) {
  		pci_read_config_word(dev, PCI_COMMAND, &command);
7edab2f08   Randy Dunlap   pci/i386: style c...
155
  		for (idx = 0; idx < PCI_ROM_RESOURCE; idx++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
162
163
164
165
  			r = &dev->resource[idx];
  			if (r->parent)		/* Already allocated */
  				continue;
  			if (!r->start)		/* Address not assigned at all */
  				continue;
  			if (r->flags & IORESOURCE_IO)
  				disabled = !(command & PCI_COMMAND_IO);
  			else
  				disabled = !(command & PCI_COMMAND_MEMORY);
  			if (pass == disabled) {
011fec748   Linus Torvalds   Un-break printk s...
166
167
  				dev_dbg(&dev->dev, "resource %#08llx-%#08llx (f=%lx, d=%d, p=%d)
  ",
12c0b20fa   Bjorn Helgaas   x86/PCI: use dev_...
168
169
170
  					(unsigned long long) r->start,
  					(unsigned long long) r->end,
  					r->flags, disabled, pass);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
171
172
  				pr = pci_find_parent_resource(dev, r);
  				if (!pr || request_resource(pr, r) < 0) {
104bafcfa   Ingo Molnar   PCI: Don't carp a...
173
174
  					dev_info(&dev->dev, "BAR %d: can't allocate resource
  ", idx);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
176
177
178
179
180
181
182
183
  					/* We'll assign a new address later */
  					r->end -= r->start;
  					r->start = 0;
  				}
  			}
  		}
  		if (!pass) {
  			r = &dev->resource[PCI_ROM_RESOURCE];
  			if (r->flags & IORESOURCE_ROM_ENABLE) {
7edab2f08   Randy Dunlap   pci/i386: style c...
184
185
  				/* Turn the ROM off, leave the resource region,
  				 * but keep it unregistered. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
186
  				u32 reg;
12c0b20fa   Bjorn Helgaas   x86/PCI: use dev_...
187
188
  				dev_dbg(&dev->dev, "disabling ROM
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
189
  				r->flags &= ~IORESOURCE_ROM_ENABLE;
7edab2f08   Randy Dunlap   pci/i386: style c...
190
191
192
193
  				pci_read_config_dword(dev,
  						dev->rom_base_reg, &reg);
  				pci_write_config_dword(dev, dev->rom_base_reg,
  						reg & ~PCI_ROM_ADDRESS_ENABLE);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
194
195
196
197
198
199
200
201
  			}
  		}
  	}
  }
  
  static int __init pcibios_assign_resources(void)
  {
  	struct pci_dev *dev = NULL;
81d4af134   Ivan Kokshaysky   [PATCH] x86: pci_...
202
  	struct resource *r, *pr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
203

81d4af134   Ivan Kokshaysky   [PATCH] x86: pci_...
204
  	if (!(pci_probe & PCI_ASSIGN_ROMS)) {
7edab2f08   Randy Dunlap   pci/i386: style c...
205
206
207
208
209
  		/*
  		 * Try to use BIOS settings for ROMs, otherwise let
  		 * pci_assign_unassigned_resources() allocate the new
  		 * addresses.
  		 */
81d4af134   Ivan Kokshaysky   [PATCH] x86: pci_...
210
  		for_each_pci_dev(dev) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211
  			r = &dev->resource[PCI_ROM_RESOURCE];
81d4af134   Ivan Kokshaysky   [PATCH] x86: pci_...
212
213
214
215
216
217
218
  			if (!r->flags || !r->start)
  				continue;
  			pr = pci_find_parent_resource(dev, r);
  			if (!pr || request_resource(pr, r) < 0) {
  				r->end -= r->start;
  				r->start = 0;
  			}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219
220
  		}
  	}
81d4af134   Ivan Kokshaysky   [PATCH] x86: pci_...
221
222
  
  	pci_assign_unassigned_resources();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
226
227
228
229
230
231
232
  	return 0;
  }
  
  void __init pcibios_resource_survey(void)
  {
  	DBG("PCI: Allocating resources
  ");
  	pcibios_allocate_bus_resources(&pci_root_buses);
  	pcibios_allocate_resources(0);
  	pcibios_allocate_resources(1);
a5444d15b   Ingo Molnar   x86: split e820 r...
233
234
  
  	e820_reserve_resources_late();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
235
236
237
238
239
240
241
  }
  
  /**
   * called in fs_initcall (one below subsys_initcall),
   * give a chance for motherboard reserve resources
   */
  fs_initcall(pcibios_assign_resources);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  /*
   *  If we set up a device for bus mastering, we need to check the latency
   *  timer as certain crappy BIOSes forget to set it properly.
   */
  unsigned int pcibios_max_latency = 255;
  
  void pcibios_set_master(struct pci_dev *dev)
  {
  	u8 lat;
  	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
  	if (lat < 16)
  		lat = (64 <= pcibios_max_latency) ? 64 : pcibios_max_latency;
  	else if (lat > pcibios_max_latency)
  		lat = pcibios_max_latency;
  	else
  		return;
12c0b20fa   Bjorn Helgaas   x86/PCI: use dev_...
258
259
  	dev_printk(KERN_DEBUG, &dev->dev, "setting latency timer to %d
  ", lat);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
260
261
  	pci_write_config_byte(dev, PCI_LATENCY_TIMER, lat);
  }
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  static void pci_unmap_page_range(struct vm_area_struct *vma)
  {
  	u64 addr = (u64)vma->vm_pgoff << PAGE_SHIFT;
  	free_memtype(addr, addr + vma->vm_end - vma->vm_start);
  }
  
  static void pci_track_mmap_page_range(struct vm_area_struct *vma)
  {
  	u64 addr = (u64)vma->vm_pgoff << PAGE_SHIFT;
  	unsigned long flags = pgprot_val(vma->vm_page_prot)
  						& _PAGE_CACHE_MASK;
  
  	reserve_memtype(addr, addr + vma->vm_end - vma->vm_start, flags, NULL);
  }
  
  static struct vm_operations_struct pci_mmap_ops = {
  	.open  = pci_track_mmap_page_range,
  	.close = pci_unmap_page_range,
7ae8ed505   Rik van Riel   use generic_acces...
280
  	.access = generic_access_phys,
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
281
  };
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
282
283
284
285
  int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
  			enum pci_mmap_state mmap_state, int write_combine)
  {
  	unsigned long prot;
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
286
287
288
289
  	u64 addr = vma->vm_pgoff << PAGE_SHIFT;
  	unsigned long len = vma->vm_end - vma->vm_start;
  	unsigned long flags;
  	unsigned long new_flags;
dee7cbb21   Venki Pallipadi   x86: PAT bug fix ...
290
  	int retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
291
292
293
294
295
296
  
  	/* I/O space cannot be accessed via normal processor loads and
  	 * stores on this platform.
  	 */
  	if (mmap_state == pci_mmap_io)
  		return -EINVAL;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
297
  	prot = pgprot_val(vma->vm_page_prot);
499f8f84b   Andreas Herrmann   x86: rename pat_w...
298
  	if (pat_enabled && write_combine)
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
299
  		prot |= _PAGE_CACHE_WC;
499f8f84b   Andreas Herrmann   x86: rename pat_w...
300
  	else if (pat_enabled || boot_cpu_data.x86 > 3)
de33c442e   Suresh Siddha   x86 PAT: fix perf...
301
302
303
304
305
306
  		/*
  		 * ioremap() and ioremap_nocache() defaults to UC MINUS for now.
  		 * To avoid attribute conflicts, request UC MINUS here
  		 * aswell.
  		 */
  		prot |= _PAGE_CACHE_UC_MINUS;
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
307

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
308
  	vma->vm_page_prot = __pgprot(prot);
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
309
  	flags = pgprot_val(vma->vm_page_prot) & _PAGE_CACHE_MASK;
dee7cbb21   Venki Pallipadi   x86: PAT bug fix ...
310
311
312
313
314
  	retval = reserve_memtype(addr, addr + len, flags, &new_flags);
  	if (retval)
  		return retval;
  
  	if (flags != new_flags) {
afc7d20c8   venkatesh.pallipadi@intel.com   x86 PAT: consolid...
315
  		if (!is_new_memtype_allowed(flags, new_flags)) {
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
316
317
318
319
320
  			free_memtype(addr, addr+len);
  			return -EINVAL;
  		}
  		flags = new_flags;
  	}
965194c15   Yinghai Lu   x86: max_low_pfn_...
321
  	if (((vma->vm_pgoff < max_low_pfn_mapped) ||
f361a450b   Yinghai Lu   x86: introduce ma...
322
  	     (vma->vm_pgoff >= (1UL<<(32 - PAGE_SHIFT)) &&
965194c15   Yinghai Lu   x86: max_low_pfn_...
323
  	      vma->vm_pgoff < max_pfn_mapped)) &&
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
324
325
326
327
  	    ioremap_change_attr((unsigned long)__va(addr), len, flags)) {
  		free_memtype(addr, addr + len);
  		return -EINVAL;
  	}
346d38823   Michael S. Tsirkin   [PATCH] arch/386/...
328
329
330
  	if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
  			       vma->vm_end - vma->vm_start,
  			       vma->vm_page_prot))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
331
  		return -EAGAIN;
03d72aa18   venkatesh.pallipadi@intel.com   x86: PAT use rese...
332
  	vma->vm_ops = &pci_mmap_ops;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
333
334
  	return 0;
  }