File: setup-bus.c

package info (click to toggle)
kernel-image-2.4.17-hppa 32.4
  • links: PTS
  • area: main
  • in suites: woody
  • size: 156,356 kB
  • ctags: 442,585
  • sloc: ansic: 2,542,442; asm: 144,771; makefile: 8,468; sh: 3,097; perl: 2,578; yacc: 1,177; tcl: 577; lex: 352; awk: 251; lisp: 218; sed: 72
file content (255 lines) | stat: -rw-r--r-- 7,754 bytes parent folder | download
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
/*
 *	drivers/pci/setup-bus.c
 *
 * Extruded from code written by
 *      Dave Rusling (david.rusling@reo.mts.dec.com)
 *      David Mosberger (davidm@cs.arizona.edu)
 *	David Miller (davem@redhat.com)
 *
 * Support routines for initializing a PCI subsystem.
 */

/*
 * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
 *	     PCI-PCI bridges cleanup, sorted resource allocation
 * Jul 2001, Grant Grundler <grundler@parisc-linux.org>,
 *	     Ivan Kokshaysky <ink@jurassic.park.msu.ru>
 *	     Further cleanup, generic Fast-Back-to-Back/PERR/SERR support
 */

/* NOTE: during reallocation we may have temporarily overlapping
 * IO or MEM ranges, so the arch code (pcibios_fixup_bus()) is
 * responsible for disabling all devices, probably except console
 * (VGA, serial etc.) and bridges the console device might be
 * behind -- typically AGP or PCI-(E)ISA bridges.
 */

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>


#define DEBUG_CONFIG 0
#if DEBUG_CONFIG
# define DBGC(args)     printk args
#else
# define DBGC(args)
#endif

#define ROUND_UP(x, a)		(((x) + (a) - 1) & ~((a) - 1))

static int __init
pbus_assign_resources_sorted(struct pci_bus *bus,
			     struct pbus_set_ranges_data *ranges)
{
	struct list_head *ln;
	struct resource *res;
	struct resource_list head_io, head_mem, *list, *tmp;
	unsigned long io_reserved = 0, mem_reserved = 0;
	int idx, found_vga = 0;

	head_io.next = head_mem.next = NULL;
	for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
		struct pci_dev *dev = pci_dev_b(ln);
		u16 class = dev->class >> 8;

		found_vga |= (class == PCI_CLASS_DISPLAY_VGA ||
			      class == PCI_CLASS_NOT_DEFINED_VGA);

		/* Reserve some resources for CardBus.
		   Are these values reasonable? */
		if (class == PCI_CLASS_BRIDGE_CARDBUS) {
			io_reserved += 8*1024;
			mem_reserved += 32*1024*1024;
			continue;
		}

		pdev_sort_resources(dev, &head_io, IORESOURCE_IO);
		pdev_sort_resources(dev, &head_mem, IORESOURCE_MEM);
	}

	for (list = head_io.next; list;) {
		res = list->res;
		idx = res - &list->dev->resource[0];
		if (pci_assign_resource(list->dev, idx) == 0
		    && ranges->io_end < res->end)
			ranges->io_end = res->end;
		tmp = list;
		list = list->next;
		kfree(tmp);
	}
	for (list = head_mem.next; list;) {
		res = list->res;
		idx = res - &list->dev->resource[0];
		if (pci_assign_resource(list->dev, idx) == 0
		    && ranges->mem_end < res->end)
			ranges->mem_end = res->end;
		tmp = list;
		list = list->next;
		kfree(tmp);
	}

	ranges->io_end += io_reserved;
	ranges->mem_end += mem_reserved;

	ranges->io_end = ROUND_UP(ranges->io_end, 4*1024);
	ranges->mem_end = ROUND_UP(ranges->mem_end, 1024*1024);

	return found_vga;
}

/* Initialize bridges with base/limit values we have collected */
static void __init
pci_setup_bridge(struct pci_bus *bus)
{
	struct pbus_set_ranges_data ranges;
	struct pci_dev *bridge = bus->self;
	u32 l;

	if (!bridge || (bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI)
		return;

	ranges.io_start = bus->resource[0]->start;
	ranges.io_end = bus->resource[0]->end;
	ranges.mem_start = bus->resource[1]->start;
	ranges.mem_end = bus->resource[1]->end;
	pcibios_fixup_pbus_ranges(bus, &ranges);

	DBGC((KERN_ERR "PCI: Bus %d, bridge: %s\n", bus->number, bridge->name));
	DBGC((KERN_ERR "  IO window: %04lx-%04lx\n", ranges.io_start, ranges.io_end));
	DBGC((KERN_ERR "  MEM window: %08lx-%08lx\n", ranges.mem_start, ranges.mem_end));

	/* Set up the top and bottom of the PCI I/O segment for this bus. */
	pci_read_config_dword(bridge, PCI_IO_BASE, &l);
	l &= 0xffff0000;
	l |= (ranges.io_start >> 8) & 0x00f0;
	l |= ranges.io_end & 0xf000;
	pci_write_config_dword(bridge, PCI_IO_BASE, l);

	/* Set up upper 16 bits of I/O base/limit. */
	pci_write_config_word(bridge, PCI_IO_BASE_UPPER16,
			ranges.io_start >> 16);
	pci_write_config_word(bridge, PCI_IO_LIMIT_UPPER16,
			ranges.io_end >> 16);

	/* Clear out the upper 32 bits of PREF base/limit. */
	pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, 0);
	pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0);

	/* Set up the top and bottom of the PCI Memory segment
	   for this bus. */
	l = (ranges.mem_start >> 16) & 0xfff0;
	l |= ranges.mem_end & 0xfff00000;
	pci_write_config_dword(bridge, PCI_MEMORY_BASE, l);

	/* Disable PREF memory range. */
	pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0000fff0);

	pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl);

	pci_enable_device(bridge);
	pci_set_master(bridge);
}

/* Check whether the bridge supports I/O forwarding.
   If not, its I/O base/limit register must be
   read-only and read as 0. */
static unsigned long __init
pci_bridge_check_io(struct pci_dev *bridge)
{
	u16 io;

	pci_read_config_word(bridge, PCI_IO_BASE, &io);
	if (!io) {
		pci_write_config_word(bridge, PCI_IO_BASE, 0xf0f0);
		pci_read_config_word(bridge, PCI_IO_BASE, &io);
		pci_write_config_word(bridge, PCI_IO_BASE, 0x0);
	}
	if (io)
		return IORESOURCE_IO;
	printk(KERN_WARNING "PCI: bridge %s does not support I/O forwarding!\n",
				bridge->name);
	return 0;
}

void __init
pbus_assign_resources(struct pci_bus *bus, struct pbus_set_ranges_data *ranges)
{
	struct list_head *ln;
	int found_vga = pbus_assign_resources_sorted(bus, ranges);

	if (!ranges->found_vga && found_vga) {
		struct pci_bus *b;

		ranges->found_vga = 1;
		/* Propagate presence of the VGA to upstream bridges */
		for (b = bus; b->parent; b = b->parent)
			b->bridge_ctl |= PCI_BRIDGE_CTL_VGA;
	}
	for (ln=bus->children.next; ln != &bus->children; ln=ln->next) {
		struct pci_bus *b = pci_bus_b(ln);
		struct pci_dev *bridge = b->self;
		int i;

		/* Link bus resources to the bridge ones.  */
		for (i = 0; i < 3; i++) {
			b->resource[i] =
				&bridge->resource[PCI_BRIDGE_RESOURCES + i];
			b->resource[i]->name = bus->name;
		}
		b->resource[0]->flags |= pci_bridge_check_io(bridge);
		b->resource[1]->flags |= IORESOURCE_MEM;
		b->resource[0]->start = ranges->io_start = ranges->io_end;
		b->resource[1]->start = ranges->mem_start = ranges->mem_end;

		/* For now, set IO and MEM limits of this bus
		   same as limits of its parent bus. */
		b->resource[0]->end = bus->resource[0]->end;
		b->resource[1]->end = bus->resource[1]->end;

		pbus_assign_resources(b, ranges);

		/* PCI-to-PCI Bridge Architecture Specification rev. 1.1 (1998)
		   requires that if there is no I/O ports or memory behind the
		   bridge, corresponding range must be turned off by writing
		   base value greater than limit to the bridge's base/limit
		   registers.
		   This is done automatically for empty (start==end) ranges. */
		b->resource[0]->end = ranges->io_end - 1;
		b->resource[1]->end = ranges->mem_end - 1;

		/* Add bridge resources to the resource tree. */
		if (b->resource[0]->end > b->resource[0]->start &&
		    request_resource(bus->resource[0], b->resource[0]) < 0)
			printk(KERN_ERR "PCI: failed to request IO "
					"for bus %d\n",	b->number);
		if (b->resource[1]->end > b->resource[1]->start &&
		    request_resource(bus->resource[1], b->resource[1]) < 0)
			printk(KERN_ERR "PCI: failed to request MEM "
					"for bus %d\n", b->number);

		pci_setup_bridge(b);
	}
}

void __init
pci_assign_unassigned_resources(void)
{
	struct pbus_set_ranges_data ranges;
	struct list_head *ln;

	for(ln=pci_root_buses.next; ln != &pci_root_buses; ln=ln->next) {
		struct pci_bus *b = pci_bus_b(ln);

		ranges.io_start = b->resource[0]->start + PCIBIOS_MIN_IO;
		ranges.mem_start = b->resource[1]->start + PCIBIOS_MIN_MEM;
		ranges.io_end = ranges.io_start;
		ranges.mem_end = ranges.mem_start;
		ranges.found_vga = 0;
		pbus_assign_resources(b, &ranges);
	}
}