diff options
Diffstat (limited to 'arch/sh/drivers/pci/pci.c')
-rw-r--r-- | arch/sh/drivers/pci/pci.c | 223 |
1 files changed, 150 insertions, 73 deletions
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index 9a1c423ad167..a09c77dd09db 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/dma-debug.h> | 19 | #include <linux/dma-debug.h> |
20 | #include <linux/io.h> | 20 | #include <linux/io.h> |
21 | #include <linux/mutex.h> | 21 | #include <linux/mutex.h> |
22 | #include <linux/spinlock.h> | ||
22 | 23 | ||
23 | unsigned long PCIBIOS_MIN_IO = 0x0000; | 24 | unsigned long PCIBIOS_MIN_IO = 0x0000; |
24 | unsigned long PCIBIOS_MIN_MEM = 0; | 25 | unsigned long PCIBIOS_MIN_MEM = 0; |
@@ -33,15 +34,22 @@ static int pci_initialized; | |||
33 | static void __devinit pcibios_scanbus(struct pci_channel *hose) | 34 | static void __devinit pcibios_scanbus(struct pci_channel *hose) |
34 | { | 35 | { |
35 | static int next_busno; | 36 | static int next_busno; |
37 | static int need_domain_info; | ||
36 | struct pci_bus *bus; | 38 | struct pci_bus *bus; |
37 | 39 | ||
38 | bus = pci_scan_bus(next_busno, hose->pci_ops, hose); | 40 | bus = pci_scan_bus(next_busno, hose->pci_ops, hose); |
41 | hose->bus = bus; | ||
42 | |||
43 | need_domain_info = need_domain_info || hose->index; | ||
44 | hose->need_domain_info = need_domain_info; | ||
39 | if (bus) { | 45 | if (bus) { |
40 | next_busno = bus->subordinate + 1; | 46 | next_busno = bus->subordinate + 1; |
41 | /* Don't allow 8-bit bus number overflow inside the hose - | 47 | /* Don't allow 8-bit bus number overflow inside the hose - |
42 | reserve some space for bridges. */ | 48 | reserve some space for bridges. */ |
43 | if (next_busno > 224) | 49 | if (next_busno > 224) { |
44 | next_busno = 0; | 50 | next_busno = 0; |
51 | need_domain_info = 1; | ||
52 | } | ||
45 | 53 | ||
46 | pci_bus_size_bridges(bus); | 54 | pci_bus_size_bridges(bus); |
47 | pci_bus_assign_resources(bus); | 55 | pci_bus_assign_resources(bus); |
@@ -49,12 +57,28 @@ static void __devinit pcibios_scanbus(struct pci_channel *hose) | |||
49 | } | 57 | } |
50 | } | 58 | } |
51 | 59 | ||
60 | /* | ||
61 | * This interrupt-safe spinlock protects all accesses to PCI | ||
62 | * configuration space. | ||
63 | */ | ||
64 | DEFINE_RAW_SPINLOCK(pci_config_lock); | ||
52 | static DEFINE_MUTEX(pci_scan_mutex); | 65 | static DEFINE_MUTEX(pci_scan_mutex); |
53 | 66 | ||
54 | void __devinit register_pci_controller(struct pci_channel *hose) | 67 | int __devinit register_pci_controller(struct pci_channel *hose) |
55 | { | 68 | { |
56 | request_resource(&iomem_resource, hose->mem_resource); | 69 | int i; |
57 | request_resource(&ioport_resource, hose->io_resource); | 70 | |
71 | for (i = 0; i < hose->nr_resources; i++) { | ||
72 | struct resource *res = hose->resources + i; | ||
73 | |||
74 | if (res->flags & IORESOURCE_IO) { | ||
75 | if (request_resource(&ioport_resource, res) < 0) | ||
76 | goto out; | ||
77 | } else { | ||
78 | if (request_resource(&iomem_resource, res) < 0) | ||
79 | goto out; | ||
80 | } | ||
81 | } | ||
58 | 82 | ||
59 | *hose_tail = hose; | 83 | *hose_tail = hose; |
60 | hose_tail = &hose->next; | 84 | hose_tail = &hose->next; |
@@ -68,6 +92,11 @@ void __devinit register_pci_controller(struct pci_channel *hose) | |||
68 | } | 92 | } |
69 | 93 | ||
70 | /* | 94 | /* |
95 | * Setup the ERR/PERR and SERR timers, if available. | ||
96 | */ | ||
97 | pcibios_enable_timers(hose); | ||
98 | |||
99 | /* | ||
71 | * Scan the bus if it is register after the PCI subsystem | 100 | * Scan the bus if it is register after the PCI subsystem |
72 | * initialization. | 101 | * initialization. |
73 | */ | 102 | */ |
@@ -76,6 +105,15 @@ void __devinit register_pci_controller(struct pci_channel *hose) | |||
76 | pcibios_scanbus(hose); | 105 | pcibios_scanbus(hose); |
77 | mutex_unlock(&pci_scan_mutex); | 106 | mutex_unlock(&pci_scan_mutex); |
78 | } | 107 | } |
108 | |||
109 | return 0; | ||
110 | |||
111 | out: | ||
112 | for (--i; i >= 0; i--) | ||
113 | release_resource(&hose->resources[i]); | ||
114 | |||
115 | printk(KERN_WARNING "Skipping PCI bus scan due to resource conflict\n"); | ||
116 | return -1; | ||
79 | } | 117 | } |
80 | 118 | ||
81 | static int __init pcibios_init(void) | 119 | static int __init pcibios_init(void) |
@@ -107,8 +145,6 @@ static void pcibios_fixup_device_resources(struct pci_dev *dev, | |||
107 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { | 145 | for (i = 0; i < PCI_NUM_RESOURCES; i++) { |
108 | if (!dev->resource[i].start) | 146 | if (!dev->resource[i].start) |
109 | continue; | 147 | continue; |
110 | if (dev->resource[i].flags & IORESOURCE_PCI_FIXED) | ||
111 | continue; | ||
112 | if (dev->resource[i].flags & IORESOURCE_IO) | 148 | if (dev->resource[i].flags & IORESOURCE_IO) |
113 | offset = hose->io_offset; | 149 | offset = hose->io_offset; |
114 | else if (dev->resource[i].flags & IORESOURCE_MEM) | 150 | else if (dev->resource[i].flags & IORESOURCE_MEM) |
@@ -127,11 +163,13 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) | |||
127 | { | 163 | { |
128 | struct pci_dev *dev = bus->self; | 164 | struct pci_dev *dev = bus->self; |
129 | struct list_head *ln; | 165 | struct list_head *ln; |
130 | struct pci_channel *chan = bus->sysdata; | 166 | struct pci_channel *hose = bus->sysdata; |
131 | 167 | ||
132 | if (!dev) { | 168 | if (!dev) { |
133 | bus->resource[0] = chan->io_resource; | 169 | int i; |
134 | bus->resource[1] = chan->mem_resource; | 170 | |
171 | for (i = 0; i < hose->nr_resources; i++) | ||
172 | bus->resource[i] = hose->resources + i; | ||
135 | } | 173 | } |
136 | 174 | ||
137 | for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { | 175 | for (ln = bus->devices.next; ln != &bus->devices; ln = ln->next) { |
@@ -148,34 +186,29 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus) | |||
148 | * addresses to be allocated in the 0x000-0x0ff region | 186 | * addresses to be allocated in the 0x000-0x0ff region |
149 | * modulo 0x400. | 187 | * modulo 0x400. |
150 | */ | 188 | */ |
151 | void pcibios_align_resource(void *data, struct resource *res, | 189 | resource_size_t pcibios_align_resource(void *data, const struct resource *res, |
152 | resource_size_t size, resource_size_t align) | 190 | resource_size_t size, resource_size_t align) |
153 | { | 191 | { |
154 | struct pci_dev *dev = data; | 192 | struct pci_dev *dev = data; |
155 | struct pci_channel *chan = dev->sysdata; | 193 | struct pci_channel *hose = dev->sysdata; |
156 | resource_size_t start = res->start; | 194 | resource_size_t start = res->start; |
157 | 195 | ||
158 | if (res->flags & IORESOURCE_IO) { | 196 | if (res->flags & IORESOURCE_IO) { |
159 | if (start < PCIBIOS_MIN_IO + chan->io_resource->start) | 197 | if (start < PCIBIOS_MIN_IO + hose->resources[0].start) |
160 | start = PCIBIOS_MIN_IO + chan->io_resource->start; | 198 | start = PCIBIOS_MIN_IO + hose->resources[0].start; |
161 | 199 | ||
162 | /* | 200 | /* |
163 | * Put everything into 0x00-0xff region modulo 0x400. | 201 | * Put everything into 0x00-0xff region modulo 0x400. |
164 | */ | 202 | */ |
165 | if (start & 0x300) { | 203 | if (start & 0x300) |
166 | start = (start + 0x3ff) & ~0x3ff; | 204 | start = (start + 0x3ff) & ~0x3ff; |
167 | res->start = start; | ||
168 | } | ||
169 | } else if (res->flags & IORESOURCE_MEM) { | ||
170 | if (start < PCIBIOS_MIN_MEM + chan->mem_resource->start) | ||
171 | start = PCIBIOS_MIN_MEM + chan->mem_resource->start; | ||
172 | } | 205 | } |
173 | 206 | ||
174 | res->start = start; | 207 | return start; |
175 | } | 208 | } |
176 | 209 | ||
177 | void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, | 210 | void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, |
178 | struct resource *res) | 211 | struct resource *res) |
179 | { | 212 | { |
180 | struct pci_channel *hose = dev->sysdata; | 213 | struct pci_channel *hose = dev->sysdata; |
181 | unsigned long offset = 0; | 214 | unsigned long offset = 0; |
@@ -189,9 +222,8 @@ void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, | |||
189 | region->end = res->end - offset; | 222 | region->end = res->end - offset; |
190 | } | 223 | } |
191 | 224 | ||
192 | void __devinit | 225 | void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, |
193 | pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, | 226 | struct pci_bus_region *region) |
194 | struct pci_bus_region *region) | ||
195 | { | 227 | { |
196 | struct pci_channel *hose = dev->sysdata; | 228 | struct pci_channel *hose = dev->sysdata; |
197 | unsigned long offset = 0; | 229 | unsigned long offset = 0; |
@@ -207,40 +239,7 @@ pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, | |||
207 | 239 | ||
208 | int pcibios_enable_device(struct pci_dev *dev, int mask) | 240 | int pcibios_enable_device(struct pci_dev *dev, int mask) |
209 | { | 241 | { |
210 | u16 cmd, old_cmd; | 242 | return pci_enable_resources(dev, mask); |
211 | int idx; | ||
212 | struct resource *r; | ||
213 | |||
214 | pci_read_config_word(dev, PCI_COMMAND, &cmd); | ||
215 | old_cmd = cmd; | ||
216 | for (idx=0; idx < PCI_NUM_RESOURCES; idx++) { | ||
217 | /* Only set up the requested stuff */ | ||
218 | if (!(mask & (1<<idx))) | ||
219 | continue; | ||
220 | |||
221 | r = &dev->resource[idx]; | ||
222 | if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) | ||
223 | continue; | ||
224 | if ((idx == PCI_ROM_RESOURCE) && | ||
225 | (!(r->flags & IORESOURCE_ROM_ENABLE))) | ||
226 | continue; | ||
227 | if (!r->start && r->end) { | ||
228 | printk(KERN_ERR "PCI: Device %s not available " | ||
229 | "because of resource collisions\n", | ||
230 | pci_name(dev)); | ||
231 | return -EINVAL; | ||
232 | } | ||
233 | if (r->flags & IORESOURCE_IO) | ||
234 | cmd |= PCI_COMMAND_IO; | ||
235 | if (r->flags & IORESOURCE_MEM) | ||
236 | cmd |= PCI_COMMAND_MEMORY; | ||
237 | } | ||
238 | if (cmd != old_cmd) { | ||
239 | printk("PCI: Enabling device %s (%04x -> %04x)\n", | ||
240 | pci_name(dev), old_cmd, cmd); | ||
241 | pci_write_config_word(dev, PCI_COMMAND, cmd); | ||
242 | } | ||
243 | return 0; | ||
244 | } | 243 | } |
245 | 244 | ||
246 | /* | 245 | /* |
@@ -269,11 +268,91 @@ void __init pcibios_update_irq(struct pci_dev *dev, int irq) | |||
269 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); | 268 | pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); |
270 | } | 269 | } |
271 | 270 | ||
272 | char * __devinit pcibios_setup(char *str) | 271 | char * __devinit __weak pcibios_setup(char *str) |
273 | { | 272 | { |
274 | return str; | 273 | return str; |
275 | } | 274 | } |
276 | 275 | ||
276 | static void __init | ||
277 | pcibios_bus_report_status_early(struct pci_channel *hose, | ||
278 | int top_bus, int current_bus, | ||
279 | unsigned int status_mask, int warn) | ||
280 | { | ||
281 | unsigned int pci_devfn; | ||
282 | u16 status; | ||
283 | int ret; | ||
284 | |||
285 | for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { | ||
286 | if (PCI_FUNC(pci_devfn)) | ||
287 | continue; | ||
288 | ret = early_read_config_word(hose, top_bus, current_bus, | ||
289 | pci_devfn, PCI_STATUS, &status); | ||
290 | if (ret != PCIBIOS_SUCCESSFUL) | ||
291 | continue; | ||
292 | if (status == 0xffff) | ||
293 | continue; | ||
294 | |||
295 | early_write_config_word(hose, top_bus, current_bus, | ||
296 | pci_devfn, PCI_STATUS, | ||
297 | status & status_mask); | ||
298 | if (warn) | ||
299 | printk("(%02x:%02x: %04X) ", current_bus, | ||
300 | pci_devfn, status); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * We can't use pci_find_device() here since we are | ||
306 | * called from interrupt context. | ||
307 | */ | ||
308 | static void __init_refok | ||
309 | pcibios_bus_report_status(struct pci_bus *bus, unsigned int status_mask, | ||
310 | int warn) | ||
311 | { | ||
312 | struct pci_dev *dev; | ||
313 | |||
314 | list_for_each_entry(dev, &bus->devices, bus_list) { | ||
315 | u16 status; | ||
316 | |||
317 | /* | ||
318 | * ignore host bridge - we handle | ||
319 | * that separately | ||
320 | */ | ||
321 | if (dev->bus->number == 0 && dev->devfn == 0) | ||
322 | continue; | ||
323 | |||
324 | pci_read_config_word(dev, PCI_STATUS, &status); | ||
325 | if (status == 0xffff) | ||
326 | continue; | ||
327 | |||
328 | if ((status & status_mask) == 0) | ||
329 | continue; | ||
330 | |||
331 | /* clear the status errors */ | ||
332 | pci_write_config_word(dev, PCI_STATUS, status & status_mask); | ||
333 | |||
334 | if (warn) | ||
335 | printk("(%s: %04X) ", pci_name(dev), status); | ||
336 | } | ||
337 | |||
338 | list_for_each_entry(dev, &bus->devices, bus_list) | ||
339 | if (dev->subordinate) | ||
340 | pcibios_bus_report_status(dev->subordinate, status_mask, warn); | ||
341 | } | ||
342 | |||
343 | void __init_refok pcibios_report_status(unsigned int status_mask, int warn) | ||
344 | { | ||
345 | struct pci_channel *hose; | ||
346 | |||
347 | for (hose = hose_head; hose; hose = hose->next) { | ||
348 | if (unlikely(!hose->bus)) | ||
349 | pcibios_bus_report_status_early(hose, hose_head->index, | ||
350 | hose->index, status_mask, warn); | ||
351 | else | ||
352 | pcibios_bus_report_status(hose->bus, status_mask, warn); | ||
353 | } | ||
354 | } | ||
355 | |||
277 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | 356 | int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, |
278 | enum pci_mmap_state mmap_state, int write_combine) | 357 | enum pci_mmap_state mmap_state, int write_combine) |
279 | { | 358 | { |
@@ -295,13 +374,20 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, | |||
295 | vma->vm_page_prot); | 374 | vma->vm_page_prot); |
296 | } | 375 | } |
297 | 376 | ||
377 | #ifndef CONFIG_GENERIC_IOMAP | ||
378 | |||
298 | static void __iomem *ioport_map_pci(struct pci_dev *dev, | 379 | static void __iomem *ioport_map_pci(struct pci_dev *dev, |
299 | unsigned long port, unsigned int nr) | 380 | unsigned long port, unsigned int nr) |
300 | { | 381 | { |
301 | struct pci_channel *chan = dev->sysdata; | 382 | struct pci_channel *chan = dev->sysdata; |
302 | 383 | ||
303 | if (!chan->io_map_base) | 384 | if (unlikely(!chan->io_map_base)) { |
304 | chan->io_map_base = generic_io_base; | 385 | chan->io_map_base = sh_io_port_base; |
386 | |||
387 | if (pci_domains_supported) | ||
388 | panic("To avoid data corruption io_map_base MUST be " | ||
389 | "set with multiple PCI domains."); | ||
390 | } | ||
305 | 391 | ||
306 | return (void __iomem *)(chan->io_map_base + port); | 392 | return (void __iomem *)(chan->io_map_base + port); |
307 | } | 393 | } |
@@ -319,20 +405,9 @@ void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen) | |||
319 | 405 | ||
320 | if (flags & IORESOURCE_IO) | 406 | if (flags & IORESOURCE_IO) |
321 | return ioport_map_pci(dev, start, len); | 407 | return ioport_map_pci(dev, start, len); |
322 | |||
323 | /* | ||
324 | * Presently the IORESOURCE_MEM case is a bit special, most | ||
325 | * SH7751 style PCI controllers have PCI memory at a fixed | ||
326 | * location in the address space where no remapping is desired. | ||
327 | * With the IORESOURCE_MEM case more care has to be taken | ||
328 | * to inhibit page table mapping for legacy cores, but this is | ||
329 | * punted off to __ioremap(). | ||
330 | * -- PFM. | ||
331 | */ | ||
332 | if (flags & IORESOURCE_MEM) { | 408 | if (flags & IORESOURCE_MEM) { |
333 | if (flags & IORESOURCE_CACHEABLE) | 409 | if (flags & IORESOURCE_CACHEABLE) |
334 | return ioremap(start, len); | 410 | return ioremap(start, len); |
335 | |||
336 | return ioremap_nocache(start, len); | 411 | return ioremap_nocache(start, len); |
337 | } | 412 | } |
338 | 413 | ||
@@ -346,6 +421,8 @@ void pci_iounmap(struct pci_dev *dev, void __iomem *addr) | |||
346 | } | 421 | } |
347 | EXPORT_SYMBOL(pci_iounmap); | 422 | EXPORT_SYMBOL(pci_iounmap); |
348 | 423 | ||
424 | #endif /* CONFIG_GENERIC_IOMAP */ | ||
425 | |||
349 | #ifdef CONFIG_HOTPLUG | 426 | #ifdef CONFIG_HOTPLUG |
350 | EXPORT_SYMBOL(pcibios_resource_to_bus); | 427 | EXPORT_SYMBOL(pcibios_resource_to_bus); |
351 | EXPORT_SYMBOL(pcibios_bus_to_resource); | 428 | EXPORT_SYMBOL(pcibios_bus_to_resource); |