Subversion Repositories shark

Rev

Rev 428 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
428 giacomo 1
/*
2
 *      drivers/pci/setup-res.c
3
 *
4
 * Extruded from code written by
5
 *      Dave Rusling (david.rusling@reo.mts.dec.com)
6
 *      David Mosberger (davidm@cs.arizona.edu)
7
 *      David Miller (davem@redhat.com)
8
 *
9
 * Support routines for initializing a PCI subsystem.
10
 */
11
 
12
/* fixed for multiple pci buses, 1999 Andrea Arcangeli <andrea@suse.de> */
13
 
14
/*
15
 * Nov 2000, Ivan Kokshaysky <ink@jurassic.park.msu.ru>
16
 *           Resource sorting
17
 */
18
 
430 giacomo 19
#include <linuxcomp.h>
20
 
428 giacomo 21
#include <linux/init.h>
22
#include <linux/kernel.h>
23
#include <linux/pci.h>
24
#include <linux/errno.h>
25
#include <linux/ioport.h>
26
#include <linux/cache.h>
27
#include <linux/slab.h>
28
#include "pci.h"
29
 
30
#define DEBUG_CONFIG 0
31
#if DEBUG_CONFIG
32
# define DBGC(args)     printk args
33
#else
34
# define DBGC(args)
35
#endif
36
 
37
 
38
static void
39
pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
40
{
41
        struct pci_bus_region region;
42
        u32 new, check, mask;
43
        int reg;
44
 
45
        pcibios_resource_to_bus(dev, &region, res);
46
 
47
        DBGC((KERN_ERR "  got res [%lx:%lx] bus [%lx:%lx] flags %lx for "
48
              "BAR %d of %s\n", res->start, res->end,
49
              region.start, region.end, res->flags,
50
              resno, pci_name(dev)));
51
 
52
        new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
53
        if (res->flags & IORESOURCE_IO)
54
                mask = (u32)PCI_BASE_ADDRESS_IO_MASK;
55
        else
56
                mask = (u32)PCI_BASE_ADDRESS_MEM_MASK;
57
 
58
        if (resno < 6) {
59
                reg = PCI_BASE_ADDRESS_0 + 4 * resno;
60
        } else if (resno == PCI_ROM_RESOURCE) {
61
                new |= res->flags & PCI_ROM_ADDRESS_ENABLE;
62
                reg = dev->rom_base_reg;
63
        } else {
64
                /* Hmm, non-standard resource. */
65
                BUG();
66
                return;         /* kill uninitialised var warning */
67
        }
68
 
69
        pci_write_config_dword(dev, reg, new);
70
        pci_read_config_dword(dev, reg, &check);
71
 
72
        if ((new ^ check) & mask) {
73
                printk(KERN_ERR "PCI: Error while updating region "
74
                       "%s/%d (%08x != %08x)\n", pci_name(dev), resno,
75
                       new, check);
76
        }
77
 
78
        if ((new & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) ==
79
            (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64)) {
80
                new = 0; /* currently everyone zeros the high address */
81
                pci_write_config_dword(dev, reg + 4, new);
82
                pci_read_config_dword(dev, reg + 4, &check);
83
                if (check != new) {
84
                        printk(KERN_ERR "PCI: Error updating region "
85
                               "%s/%d (high %08x != %08x)\n",
86
                               pci_name(dev), resno, new, check);
87
                }
88
        }
89
}
90
 
91
int __init
92
pci_claim_resource(struct pci_dev *dev, int resource)
93
{
94
        struct resource *res = &dev->resource[resource];
95
        struct resource *root = pci_find_parent_resource(dev, res);
96
        char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
97
        int err;
98
 
99
        err = -EINVAL;
100
        if (root != NULL)
101
                err = request_resource(root, res);
102
 
103
        if (err) {
104
                printk(KERN_ERR "PCI: %s region %d of %s %s [%lx:%lx]\n",
105
                       root ? "Address space collision on" :
106
                              "No parent found for",
107
                       resource, dtype, pci_name(dev), res->start, res->end);
108
        }
109
 
110
        return err;
111
}
112
 
113
int pci_assign_resource(struct pci_dev *dev, int resno)
114
{
115
        struct pci_bus *bus = dev->bus;
116
        struct resource *res = dev->resource + resno;
117
        unsigned long size, min, align;
118
        int ret;
119
 
120
        size = res->end - res->start + 1;
121
        min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM;
122
        /* The bridge resources are special, as their
123
           size != alignment. Sizing routines return
124
           required alignment in the "start" field. */
125
        align = (resno < PCI_BRIDGE_RESOURCES) ? size : res->start;
126
 
127
        /* First, try exact prefetching match.. */
128
        ret = pci_bus_alloc_resource(bus, res, size, align, min,
129
                                     IORESOURCE_PREFETCH,
130
                                     pcibios_align_resource, dev);
131
 
132
        if (ret < 0 && (res->flags & IORESOURCE_PREFETCH)) {
133
                /*
134
                 * That failed.
135
                 *
136
                 * But a prefetching area can handle a non-prefetching
137
                 * window (it will just not perform as well).
138
                 */
139
                ret = pci_bus_alloc_resource(bus, res, size, align, min, 0,
140
                                             pcibios_align_resource, dev);
141
        }
142
 
143
        if (ret) {
144
                printk(KERN_ERR "PCI: Failed to allocate resource %d(%lx-%lx) for %s\n",
145
                       resno, res->start, res->end, pci_name(dev));
146
        } else if (resno < PCI_BRIDGE_RESOURCES) {
147
                pci_update_resource(dev, res, resno);
148
        }
149
 
150
        return ret;
151
}
152
 
153
/* Sort resources by alignment */
154
void __devinit
155
pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
156
{
157
        int i;
158
 
159
        for (i = 0; i < PCI_NUM_RESOURCES; i++) {
160
                struct resource *r;
161
                struct resource_list *list, *tmp;
162
                unsigned long r_align;
163
 
164
                r = &dev->resource[i];
165
                r_align = r->end - r->start;
166
 
167
                if (!(r->flags) || r->parent)
168
                        continue;
169
                if (!r_align) {
170
                        printk(KERN_WARNING "PCI: Ignore bogus resource %d "
171
                                            "[%lx:%lx] of %s\n",
172
                                            i, r->start, r->end, pci_name(dev));
173
                        continue;
174
                }
175
                r_align = (i < PCI_BRIDGE_RESOURCES) ? r_align + 1 : r->start;
176
                for (list = head; ; list = list->next) {
177
                        unsigned long align = 0;
178
                        struct resource_list *ln = list->next;
179
                        int idx;
180
 
181
                        if (ln) {
182
                                idx = ln->res - &ln->dev->resource[0];
183
                                align = (idx < PCI_BRIDGE_RESOURCES) ?
184
                                        ln->res->end - ln->res->start + 1 :
185
                                        ln->res->start;
186
                        }
187
                        if (r_align > align) {
188
                                tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
189
                                if (!tmp)
190
                                        panic("pdev_sort_resources(): "
191
                                              "kmalloc() failed!\n");
192
                                tmp->next = ln;
193
                                tmp->res = r;
194
                                tmp->dev = dev;
195
                                list->next = tmp;
196
                                break;
197
                        }
198
                }
199
        }
200
}