Commit | Line | Data |
---|---|---|
9fd8b647 | 1 | /* pci_common.c: PCI controller common support. |
1da177e4 | 2 | * |
9fd8b647 | 3 | * Copyright (C) 1999, 2007 David S. Miller (davem@davemloft.net) |
1da177e4 LT |
4 | */ |
5 | ||
6 | #include <linux/string.h> | |
7 | #include <linux/slab.h> | |
8 | #include <linux/init.h> | |
cf69eab2 FMDN |
9 | #include <linux/pci.h> |
10 | #include <linux/device.h> | |
1da177e4 | 11 | |
de8d28b1 | 12 | #include <asm/prom.h> |
2b1e5978 | 13 | #include <asm/of_device.h> |
c57c2ffb | 14 | #include <asm/oplib.h> |
de8d28b1 DM |
15 | |
16 | #include "pci_impl.h" | |
1da177e4 | 17 | |
cfa0652c DM |
18 | void pci_get_pbm_props(struct pci_pbm_info *pbm) |
19 | { | |
20 | const u32 *val = of_get_property(pbm->prom_node, "bus-range", NULL); | |
21 | ||
22 | pbm->pci_first_busno = val[0]; | |
23 | pbm->pci_last_busno = val[1]; | |
24 | ||
25 | val = of_get_property(pbm->prom_node, "ino-bitmap", NULL); | |
26 | if (val) { | |
27 | pbm->ino_bitmap = (((u64)val[1] << 32UL) | | |
28 | ((u64)val[0] << 0UL)); | |
29 | } | |
30 | } | |
31 | ||
9fd8b647 DM |
32 | static void pci_register_legacy_regions(struct resource *io_res, |
33 | struct resource *mem_res) | |
1da177e4 LT |
34 | { |
35 | struct resource *p; | |
36 | ||
37 | /* VGA Video RAM. */ | |
9132983a | 38 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
1da177e4 LT |
39 | if (!p) |
40 | return; | |
41 | ||
1da177e4 LT |
42 | p->name = "Video RAM area"; |
43 | p->start = mem_res->start + 0xa0000UL; | |
44 | p->end = p->start + 0x1ffffUL; | |
45 | p->flags = IORESOURCE_BUSY; | |
46 | request_resource(mem_res, p); | |
47 | ||
9132983a | 48 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
1da177e4 LT |
49 | if (!p) |
50 | return; | |
51 | ||
1da177e4 LT |
52 | p->name = "System ROM"; |
53 | p->start = mem_res->start + 0xf0000UL; | |
54 | p->end = p->start + 0xffffUL; | |
55 | p->flags = IORESOURCE_BUSY; | |
56 | request_resource(mem_res, p); | |
57 | ||
9132983a | 58 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
1da177e4 LT |
59 | if (!p) |
60 | return; | |
61 | ||
1da177e4 LT |
62 | p->name = "Video ROM"; |
63 | p->start = mem_res->start + 0xc0000UL; | |
64 | p->end = p->start + 0x7fffUL; | |
65 | p->flags = IORESOURCE_BUSY; | |
66 | request_resource(mem_res, p); | |
67 | } | |
68 | ||
9fd8b647 DM |
69 | static void pci_register_iommu_region(struct pci_pbm_info *pbm) |
70 | { | |
a165b420 | 71 | const u32 *vdma = of_get_property(pbm->prom_node, "virtual-dma", NULL); |
9fd8b647 DM |
72 | |
73 | if (vdma) { | |
74 | struct resource *rp = kmalloc(sizeof(*rp), GFP_KERNEL); | |
75 | ||
76 | if (!rp) { | |
77 | prom_printf("Cannot allocate IOMMU resource.\n"); | |
78 | prom_halt(); | |
79 | } | |
80 | rp->name = "IOMMU"; | |
81 | rp->start = pbm->mem_space.start + (unsigned long) vdma[0]; | |
82 | rp->end = rp->start + (unsigned long) vdma[1] - 1UL; | |
83 | rp->flags = IORESOURCE_BUSY; | |
84 | request_resource(&pbm->mem_space, rp); | |
85 | } | |
86 | } | |
87 | ||
88 | void pci_determine_mem_io_space(struct pci_pbm_info *pbm) | |
89 | { | |
a165b420 | 90 | const struct linux_prom_pci_ranges *pbm_ranges; |
9fd8b647 | 91 | int i, saw_mem, saw_io; |
3487a1f9 | 92 | int num_pbm_ranges; |
9fd8b647 DM |
93 | |
94 | saw_mem = saw_io = 0; | |
3487a1f9 DM |
95 | pbm_ranges = of_get_property(pbm->prom_node, "ranges", &i); |
96 | num_pbm_ranges = i / sizeof(*pbm_ranges); | |
97 | ||
98 | for (i = 0; i < num_pbm_ranges; i++) { | |
a165b420 | 99 | const struct linux_prom_pci_ranges *pr = &pbm_ranges[i]; |
9fd8b647 | 100 | unsigned long a; |
3487a1f9 | 101 | u32 parent_phys_hi, parent_phys_lo; |
9fd8b647 DM |
102 | int type; |
103 | ||
3487a1f9 DM |
104 | parent_phys_hi = pr->parent_phys_hi; |
105 | parent_phys_lo = pr->parent_phys_lo; | |
106 | if (tlb_type == hypervisor) | |
107 | parent_phys_hi &= 0x0fffffff; | |
108 | ||
9fd8b647 | 109 | type = (pr->child_phys_hi >> 24) & 0x3; |
3487a1f9 DM |
110 | a = (((unsigned long)parent_phys_hi << 32UL) | |
111 | ((unsigned long)parent_phys_lo << 0UL)); | |
9fd8b647 DM |
112 | |
113 | switch (type) { | |
114 | case 0: | |
115 | /* PCI config space, 16MB */ | |
116 | pbm->config_space = a; | |
117 | break; | |
118 | ||
119 | case 1: | |
120 | /* 16-bit IO space, 16MB */ | |
121 | pbm->io_space.start = a; | |
122 | pbm->io_space.end = a + ((16UL*1024UL*1024UL) - 1UL); | |
123 | pbm->io_space.flags = IORESOURCE_IO; | |
124 | saw_io = 1; | |
125 | break; | |
126 | ||
127 | case 2: | |
128 | /* 32-bit MEM space, 2GB */ | |
129 | pbm->mem_space.start = a; | |
130 | pbm->mem_space.end = a + (0x80000000UL - 1UL); | |
131 | pbm->mem_space.flags = IORESOURCE_MEM; | |
132 | saw_mem = 1; | |
133 | break; | |
134 | ||
135 | case 3: | |
136 | /* XXX 64-bit MEM handling XXX */ | |
137 | ||
138 | default: | |
139 | break; | |
140 | }; | |
141 | } | |
142 | ||
143 | if (!saw_io || !saw_mem) { | |
144 | prom_printf("%s: Fatal error, missing %s PBM range.\n", | |
145 | pbm->name, | |
146 | (!saw_io ? "IO" : "MEM")); | |
147 | prom_halt(); | |
148 | } | |
149 | ||
150 | printk("%s: PCI IO[%lx] MEM[%lx]\n", | |
151 | pbm->name, | |
152 | pbm->io_space.start, | |
153 | pbm->mem_space.start); | |
154 | ||
155 | pbm->io_space.name = pbm->mem_space.name = pbm->name; | |
156 | ||
157 | request_resource(&ioport_resource, &pbm->io_space); | |
158 | request_resource(&iomem_resource, &pbm->mem_space); | |
159 | ||
160 | pci_register_legacy_regions(&pbm->io_space, | |
161 | &pbm->mem_space); | |
162 | pci_register_iommu_region(pbm); | |
163 | } | |
164 | ||
1da177e4 | 165 | /* Generic helper routines for PCI error reporting. */ |
6c108f12 | 166 | void pci_scan_for_target_abort(struct pci_pbm_info *pbm, |
1da177e4 LT |
167 | struct pci_bus *pbus) |
168 | { | |
169 | struct pci_dev *pdev; | |
170 | struct pci_bus *bus; | |
171 | ||
172 | list_for_each_entry(pdev, &pbus->devices, bus_list) { | |
173 | u16 status, error_bits; | |
174 | ||
175 | pci_read_config_word(pdev, PCI_STATUS, &status); | |
176 | error_bits = | |
177 | (status & (PCI_STATUS_SIG_TARGET_ABORT | | |
178 | PCI_STATUS_REC_TARGET_ABORT)); | |
179 | if (error_bits) { | |
180 | pci_write_config_word(pdev, PCI_STATUS, error_bits); | |
6c108f12 DM |
181 | printk("%s: Device %s saw Target Abort [%016x]\n", |
182 | pbm->name, pci_name(pdev), status); | |
1da177e4 LT |
183 | } |
184 | } | |
185 | ||
186 | list_for_each_entry(bus, &pbus->children, node) | |
6c108f12 | 187 | pci_scan_for_target_abort(pbm, bus); |
1da177e4 LT |
188 | } |
189 | ||
6c108f12 | 190 | void pci_scan_for_master_abort(struct pci_pbm_info *pbm, |
1da177e4 LT |
191 | struct pci_bus *pbus) |
192 | { | |
193 | struct pci_dev *pdev; | |
194 | struct pci_bus *bus; | |
195 | ||
196 | list_for_each_entry(pdev, &pbus->devices, bus_list) { | |
197 | u16 status, error_bits; | |
198 | ||
199 | pci_read_config_word(pdev, PCI_STATUS, &status); | |
200 | error_bits = | |
201 | (status & (PCI_STATUS_REC_MASTER_ABORT)); | |
202 | if (error_bits) { | |
203 | pci_write_config_word(pdev, PCI_STATUS, error_bits); | |
6c108f12 DM |
204 | printk("%s: Device %s received Master Abort [%016x]\n", |
205 | pbm->name, pci_name(pdev), status); | |
1da177e4 LT |
206 | } |
207 | } | |
208 | ||
209 | list_for_each_entry(bus, &pbus->children, node) | |
6c108f12 | 210 | pci_scan_for_master_abort(pbm, bus); |
1da177e4 LT |
211 | } |
212 | ||
6c108f12 | 213 | void pci_scan_for_parity_error(struct pci_pbm_info *pbm, |
1da177e4 LT |
214 | struct pci_bus *pbus) |
215 | { | |
216 | struct pci_dev *pdev; | |
217 | struct pci_bus *bus; | |
218 | ||
219 | list_for_each_entry(pdev, &pbus->devices, bus_list) { | |
220 | u16 status, error_bits; | |
221 | ||
222 | pci_read_config_word(pdev, PCI_STATUS, &status); | |
223 | error_bits = | |
224 | (status & (PCI_STATUS_PARITY | | |
225 | PCI_STATUS_DETECTED_PARITY)); | |
226 | if (error_bits) { | |
227 | pci_write_config_word(pdev, PCI_STATUS, error_bits); | |
6c108f12 DM |
228 | printk("%s: Device %s saw Parity Error [%016x]\n", |
229 | pbm->name, pci_name(pdev), status); | |
1da177e4 LT |
230 | } |
231 | } | |
232 | ||
233 | list_for_each_entry(bus, &pbus->children, node) | |
6c108f12 | 234 | pci_scan_for_parity_error(pbm, bus); |
1da177e4 | 235 | } |