]>
Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
1da177e4 LT |
2 | * Copyright (C) 2001 Allan Trautman, IBM Corporation |
3 | * | |
4 | * iSeries specific routines for PCI. | |
d387899f | 5 | * |
1da177e4 LT |
6 | * Based on code from pci.c and iSeries_pci.c 32bit |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
d387899f | 12 | * |
1da177e4 LT |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
d387899f | 17 | * |
1da177e4 LT |
18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
21 | */ | |
22 | #include <linux/kernel.h> | |
d387899f | 23 | #include <linux/list.h> |
1da177e4 LT |
24 | #include <linux/string.h> |
25 | #include <linux/init.h> | |
26 | #include <linux/module.h> | |
1da177e4 LT |
27 | #include <linux/pci.h> |
28 | ||
29 | #include <asm/io.h> | |
30 | #include <asm/irq.h> | |
31 | #include <asm/prom.h> | |
32 | #include <asm/machdep.h> | |
33 | #include <asm/pci-bridge.h> | |
1da177e4 | 34 | #include <asm/iommu.h> |
426c1a11 | 35 | #include <asm/abs_addr.h> |
caf81329 | 36 | #include <asm/firmware.h> |
1da177e4 | 37 | |
8021b8a7 | 38 | #include <asm/iseries/hv_call_xm.h> |
bbc8b628 | 39 | #include <asm/iseries/mf.h> |
c7f0e8cb | 40 | #include <asm/iseries/iommu.h> |
1da177e4 | 41 | |
d387899f | 42 | #include <asm/ppc-pci.h> |
1da177e4 | 43 | |
b08567cb | 44 | #include "irq.h" |
426c1a11 | 45 | #include "pci.h" |
c6d2ea92 | 46 | #include "call_pci.h" |
b08567cb | 47 | |
b9b1812c SR |
48 | #define PCI_RETRY_MAX 3 |
49 | static int limit_pci_retries = 1; /* Set Retry Error on. */ | |
1da177e4 | 50 | |
1da177e4 LT |
51 | /* |
52 | * Table defines | |
53 | * Each Entry size is 4 MB * 1024 Entries = 4GB I/O address space. | |
54 | */ | |
55 | #define IOMM_TABLE_MAX_ENTRIES 1024 | |
56 | #define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL | |
57 | #define BASE_IO_MEMORY 0xE000000000000000UL | |
58 | ||
b58b7f98 | 59 | static unsigned long max_io_memory = BASE_IO_MEMORY; |
1da177e4 LT |
60 | static long current_iomm_table_entry; |
61 | ||
62 | /* | |
63 | * Lookup Tables. | |
64 | */ | |
b58b7f98 SR |
65 | static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES]; |
66 | static u8 iobar_table[IOMM_TABLE_MAX_ENTRIES]; | |
1da177e4 | 67 | |
b58b7f98 | 68 | static const char pci_io_text[] = "iSeries PCI I/O"; |
1da177e4 LT |
69 | static DEFINE_SPINLOCK(iomm_table_lock); |
70 | ||
1da177e4 LT |
71 | /* |
72 | * iomm_table_allocate_entry | |
73 | * | |
74 | * Adds pci_dev entry in address translation table | |
75 | * | |
76 | * - Allocates the number of entries required in table base on BAR | |
77 | * size. | |
78 | * - Allocates starting at BASE_IO_MEMORY and increases. | |
79 | * - The size is round up to be a multiple of entry size. | |
80 | * - CurrentIndex is incremented to keep track of the last entry. | |
81 | * - Builds the resource entry for allocated BARs. | |
82 | */ | |
1e105904 | 83 | static void __init iomm_table_allocate_entry(struct pci_dev *dev, int bar_num) |
1da177e4 LT |
84 | { |
85 | struct resource *bar_res = &dev->resource[bar_num]; | |
86 | long bar_size = pci_resource_len(dev, bar_num); | |
87 | ||
88 | /* | |
89 | * No space to allocate, quick exit, skip Allocation. | |
90 | */ | |
91 | if (bar_size == 0) | |
92 | return; | |
93 | /* | |
94 | * Set Resource values. | |
95 | */ | |
96 | spin_lock(&iomm_table_lock); | |
97 | bar_res->name = pci_io_text; | |
b58b7f98 | 98 | bar_res->start = BASE_IO_MEMORY + |
1da177e4 | 99 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; |
1da177e4 LT |
100 | bar_res->end = bar_res->start + bar_size - 1; |
101 | /* | |
102 | * Allocate the number of table entries needed for BAR. | |
103 | */ | |
104 | while (bar_size > 0 ) { | |
105 | iomm_table[current_iomm_table_entry] = dev->sysdata; | |
106 | iobar_table[current_iomm_table_entry] = bar_num; | |
107 | bar_size -= IOMM_TABLE_ENTRY_SIZE; | |
108 | ++current_iomm_table_entry; | |
109 | } | |
110 | max_io_memory = BASE_IO_MEMORY + | |
b58b7f98 | 111 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; |
1da177e4 LT |
112 | spin_unlock(&iomm_table_lock); |
113 | } | |
114 | ||
115 | /* | |
116 | * allocate_device_bars | |
117 | * | |
118 | * - Allocates ALL pci_dev BAR's and updates the resources with the | |
119 | * BAR value. BARS with zero length will have the resources | |
120 | * The HvCallPci_getBarParms is used to get the size of the BAR | |
121 | * space. It calls iomm_table_allocate_entry to allocate | |
122 | * each entry. | |
123 | * - Loops through The Bar resources(0 - 5) including the ROM | |
124 | * is resource(6). | |
125 | */ | |
1e105904 | 126 | static void __init allocate_device_bars(struct pci_dev *dev) |
1da177e4 | 127 | { |
1da177e4 LT |
128 | int bar_num; |
129 | ||
b58b7f98 | 130 | for (bar_num = 0; bar_num <= PCI_ROM_RESOURCE; ++bar_num) |
1da177e4 | 131 | iomm_table_allocate_entry(dev, bar_num); |
1da177e4 LT |
132 | } |
133 | ||
134 | /* | |
135 | * Log error information to system console. | |
136 | * Filter out the device not there errors. | |
137 | * PCI: EADs Connect Failed 0x18.58.10 Rc: 0x00xx | |
138 | * PCI: Read Vendor Failed 0x18.58.10 Rc: 0x00xx | |
139 | * PCI: Connect Bus Unit Failed 0x18.58.10 Rc: 0x00xx | |
140 | */ | |
7a73bd7f SR |
141 | static void pci_log_error(char *error, int bus, int subbus, |
142 | int agent, int hv_res) | |
1da177e4 | 143 | { |
7a73bd7f | 144 | if (hv_res == 0x0302) |
1da177e4 LT |
145 | return; |
146 | printk(KERN_ERR "PCI: %s Failed: 0x%02X.%02X.%02X Rc: 0x%04X", | |
7a73bd7f | 147 | error, bus, subbus, agent, hv_res); |
1da177e4 LT |
148 | } |
149 | ||
9103eb7d SR |
150 | /* |
151 | * Look down the chain to find the matching Device Device | |
152 | */ | |
7a73bd7f | 153 | static struct device_node *find_device_node(int bus, int devfn) |
9103eb7d SR |
154 | { |
155 | struct device_node *node; | |
156 | ||
157 | for (node = NULL; (node = of_find_all_nodes(node)); ) { | |
158 | struct pci_dn *pdn = PCI_DN(node); | |
159 | ||
160 | if (pdn && (bus == pdn->busno) && (devfn == pdn->devfn)) | |
161 | return node; | |
162 | } | |
163 | return NULL; | |
164 | } | |
165 | ||
1da177e4 | 166 | /* |
d387899f | 167 | * iSeries_pci_final_fixup(void) |
1da177e4 LT |
168 | */ |
169 | void __init iSeries_pci_final_fixup(void) | |
170 | { | |
171 | struct pci_dev *pdev = NULL; | |
252e75a5 | 172 | struct device_node *node; |
7a73bd7f | 173 | int num_dev = 0; |
1da177e4 | 174 | |
1da177e4 LT |
175 | /* Fix up at the device node and pci_dev relationship */ |
176 | mf_display_src(0xC9000100); | |
177 | ||
178 | printk("pcibios_final_fixup\n"); | |
179 | for_each_pci_dev(pdev) { | |
3f178632 SR |
180 | struct pci_dn *pdn; |
181 | const u32 *agent; | |
182 | ||
7a73bd7f | 183 | node = find_device_node(pdev->bus->number, pdev->devfn); |
1da177e4 LT |
184 | printk("pci dev %p (%x.%x), node %p\n", pdev, |
185 | pdev->bus->number, pdev->devfn, node); | |
3f178632 SR |
186 | if (!node) { |
187 | printk("PCI: Device Tree not found for 0x%016lX\n", | |
188 | (unsigned long)pdev); | |
189 | continue; | |
190 | } | |
191 | ||
192 | pdn = PCI_DN(node); | |
193 | agent = of_get_property(node, "linux,agent-id", NULL); | |
194 | if (pdn && agent) { | |
195 | u8 irq = iSeries_allocate_IRQ(pdn->busno, 0, | |
196 | pdn->bussubno); | |
197 | int err; | |
198 | ||
199 | err = HvCallXm_connectBusUnit(pdn->busno, pdn->bussubno, | |
200 | *agent, irq); | |
201 | if (err) | |
202 | pci_log_error("Connect Bus Unit", | |
203 | pdn->busno, pdn->bussubno, *agent, err); | |
204 | else { | |
205 | err = HvCallPci_configStore8(pdn->busno, | |
206 | pdn->bussubno, *agent, | |
207 | PCI_INTERRUPT_LINE, irq); | |
b0252793 | 208 | if (err) |
3f178632 SR |
209 | pci_log_error("PciCfgStore Irq Failed!", |
210 | pdn->busno, pdn->bussubno, | |
211 | *agent, err); | |
212 | else | |
b0252793 SR |
213 | pdev->irq = irq; |
214 | } | |
3f178632 SR |
215 | } |
216 | ||
217 | num_dev++; | |
218 | pdev->sysdata = node; | |
219 | PCI_DN(node)->pcidev = pdev; | |
220 | allocate_device_bars(pdev); | |
221 | iSeries_Device_Information(pdev, num_dev); | |
222 | iommu_devnode_init_iSeries(pdev, node); | |
1da177e4 LT |
223 | } |
224 | iSeries_activate_IRQs(); | |
225 | mf_display_src(0xC9000200); | |
226 | } | |
227 | ||
1da177e4 LT |
228 | /* |
229 | * Config space read and write functions. | |
230 | * For now at least, we look for the device node for the bus and devfn | |
231 | * that we are asked to access. It may be possible to translate the devfn | |
232 | * to a subbus and deviceid more directly. | |
233 | */ | |
234 | static u64 hv_cfg_read_func[4] = { | |
235 | HvCallPciConfigLoad8, HvCallPciConfigLoad16, | |
236 | HvCallPciConfigLoad32, HvCallPciConfigLoad32 | |
237 | }; | |
238 | ||
239 | static u64 hv_cfg_write_func[4] = { | |
240 | HvCallPciConfigStore8, HvCallPciConfigStore16, | |
241 | HvCallPciConfigStore32, HvCallPciConfigStore32 | |
242 | }; | |
243 | ||
244 | /* | |
245 | * Read PCI config space | |
246 | */ | |
247 | static int iSeries_pci_read_config(struct pci_bus *bus, unsigned int devfn, | |
248 | int offset, int size, u32 *val) | |
249 | { | |
7a73bd7f | 250 | struct device_node *node = find_device_node(bus->number, devfn); |
1da177e4 LT |
251 | u64 fn; |
252 | struct HvCallPci_LoadReturn ret; | |
253 | ||
254 | if (node == NULL) | |
255 | return PCIBIOS_DEVICE_NOT_FOUND; | |
256 | if (offset > 255) { | |
257 | *val = ~0; | |
258 | return PCIBIOS_BAD_REGISTER_NUMBER; | |
259 | } | |
260 | ||
261 | fn = hv_cfg_read_func[(size - 1) & 3]; | |
20f48ccf | 262 | HvCall3Ret16(fn, &ret, iseries_ds_addr(node), offset, 0); |
1da177e4 LT |
263 | |
264 | if (ret.rc != 0) { | |
265 | *val = ~0; | |
266 | return PCIBIOS_DEVICE_NOT_FOUND; /* or something */ | |
267 | } | |
268 | ||
269 | *val = ret.value; | |
270 | return 0; | |
271 | } | |
272 | ||
273 | /* | |
274 | * Write PCI config space | |
275 | */ | |
276 | ||
277 | static int iSeries_pci_write_config(struct pci_bus *bus, unsigned int devfn, | |
278 | int offset, int size, u32 val) | |
279 | { | |
7a73bd7f | 280 | struct device_node *node = find_device_node(bus->number, devfn); |
1da177e4 LT |
281 | u64 fn; |
282 | u64 ret; | |
283 | ||
284 | if (node == NULL) | |
285 | return PCIBIOS_DEVICE_NOT_FOUND; | |
286 | if (offset > 255) | |
287 | return PCIBIOS_BAD_REGISTER_NUMBER; | |
288 | ||
289 | fn = hv_cfg_write_func[(size - 1) & 3]; | |
20f48ccf | 290 | ret = HvCall4(fn, iseries_ds_addr(node), offset, val, 0); |
1da177e4 LT |
291 | |
292 | if (ret != 0) | |
293 | return PCIBIOS_DEVICE_NOT_FOUND; | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | static struct pci_ops iSeries_pci_ops = { | |
299 | .read = iSeries_pci_read_config, | |
300 | .write = iSeries_pci_write_config | |
301 | }; | |
302 | ||
303 | /* | |
304 | * Check Return Code | |
305 | * -> On Failure, print and log information. | |
306 | * Increment Retry Count, if exceeds max, panic partition. | |
1da177e4 LT |
307 | * |
308 | * PCI: Device 23.90 ReadL I/O Error( 0): 0x1234 | |
309 | * PCI: Device 23.90 ReadL Retry( 1) | |
310 | * PCI: Device 23.90 ReadL Retry Successful(1) | |
311 | */ | |
7a73bd7f | 312 | static int check_return_code(char *type, struct device_node *dn, |
a2ebaf25 | 313 | int *retry, u64 ret) |
1da177e4 LT |
314 | { |
315 | if (ret != 0) { | |
7a73bd7f | 316 | struct pci_dn *pdn = PCI_DN(dn); |
252e75a5 | 317 | |
a2ebaf25 | 318 | (*retry)++; |
1da177e4 | 319 | printk("PCI: %s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X\n", |
7a73bd7f | 320 | type, pdn->busno, pdn->devfn, |
a2ebaf25 | 321 | *retry, (int)ret); |
1da177e4 LT |
322 | /* |
323 | * Bump the retry and check for retry count exceeded. | |
324 | * If, Exceeded, panic the system. | |
325 | */ | |
b9b1812c SR |
326 | if (((*retry) > PCI_RETRY_MAX) && |
327 | (limit_pci_retries > 0)) { | |
1da177e4 | 328 | mf_display_src(0xB6000103); |
a2ebaf25 | 329 | panic_timeout = 0; |
1da177e4 LT |
330 | panic("PCI: Hardware I/O Error, SRC B6000103, " |
331 | "Automatic Reboot Disabled.\n"); | |
332 | } | |
333 | return -1; /* Retry Try */ | |
334 | } | |
a2ebaf25 | 335 | return 0; |
1da177e4 LT |
336 | } |
337 | ||
338 | /* | |
339 | * Translate the I/O Address into a device node, bar, and bar offset. | |
340 | * Note: Make sure the passed variable end up on the stack to avoid | |
341 | * the exposure of being device global. | |
342 | */ | |
252e75a5 | 343 | static inline struct device_node *xlate_iomm_address( |
7a73bd7f SR |
344 | const volatile void __iomem *addr, |
345 | u64 *dsaptr, u64 *bar_offset) | |
1da177e4 | 346 | { |
7a73bd7f SR |
347 | unsigned long orig_addr; |
348 | unsigned long base_addr; | |
349 | unsigned long ind; | |
350 | struct device_node *dn; | |
1da177e4 | 351 | |
7a73bd7f SR |
352 | orig_addr = (unsigned long __force)addr; |
353 | if ((orig_addr < BASE_IO_MEMORY) || (orig_addr >= max_io_memory)) | |
1da177e4 | 354 | return NULL; |
7a73bd7f SR |
355 | base_addr = orig_addr - BASE_IO_MEMORY; |
356 | ind = base_addr / IOMM_TABLE_ENTRY_SIZE; | |
357 | dn = iomm_table[ind]; | |
358 | ||
359 | if (dn != NULL) { | |
360 | int barnum = iobar_table[ind]; | |
361 | *dsaptr = iseries_ds_addr(dn) | (barnum << 24); | |
362 | *bar_offset = base_addr % IOMM_TABLE_ENTRY_SIZE; | |
1da177e4 | 363 | } else |
7a73bd7f SR |
364 | panic("PCI: Invalid PCI IO address detected!\n"); |
365 | return dn; | |
1da177e4 LT |
366 | } |
367 | ||
368 | /* | |
369 | * Read MM I/O Instructions for the iSeries | |
370 | * On MM I/O error, all ones are returned and iSeries_pci_IoError is cal | |
4cb3cee0 | 371 | * else, data is returned in Big Endian format. |
1da177e4 | 372 | */ |
7a73bd7f | 373 | static u8 iSeries_read_byte(const volatile void __iomem *addr) |
1da177e4 | 374 | { |
7a73bd7f | 375 | u64 bar_offset; |
1da177e4 | 376 | u64 dsa; |
a2ebaf25 | 377 | int retry = 0; |
1da177e4 | 378 | struct HvCallPci_LoadReturn ret; |
7a73bd7f SR |
379 | struct device_node *dn = |
380 | xlate_iomm_address(addr, &dsa, &bar_offset); | |
1da177e4 | 381 | |
7a73bd7f | 382 | if (dn == NULL) { |
1da177e4 LT |
383 | static unsigned long last_jiffies; |
384 | static int num_printed; | |
385 | ||
386 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
387 | last_jiffies = jiffies; | |
388 | num_printed = 0; | |
389 | } | |
390 | if (num_printed++ < 10) | |
7a73bd7f SR |
391 | printk(KERN_ERR "iSeries_read_byte: invalid access at IO address %p\n", |
392 | addr); | |
1da177e4 LT |
393 | return 0xff; |
394 | } | |
395 | do { | |
7a73bd7f SR |
396 | HvCall3Ret16(HvCallPciBarLoad8, &ret, dsa, bar_offset, 0); |
397 | } while (check_return_code("RDB", dn, &retry, ret.rc) != 0); | |
1da177e4 | 398 | |
4cb3cee0 | 399 | return ret.value; |
1da177e4 | 400 | } |
1da177e4 | 401 | |
7a73bd7f | 402 | static u16 iSeries_read_word(const volatile void __iomem *addr) |
1da177e4 | 403 | { |
7a73bd7f | 404 | u64 bar_offset; |
1da177e4 | 405 | u64 dsa; |
a2ebaf25 | 406 | int retry = 0; |
1da177e4 | 407 | struct HvCallPci_LoadReturn ret; |
7a73bd7f SR |
408 | struct device_node *dn = |
409 | xlate_iomm_address(addr, &dsa, &bar_offset); | |
1da177e4 | 410 | |
7a73bd7f | 411 | if (dn == NULL) { |
1da177e4 LT |
412 | static unsigned long last_jiffies; |
413 | static int num_printed; | |
414 | ||
415 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
416 | last_jiffies = jiffies; | |
417 | num_printed = 0; | |
418 | } | |
419 | if (num_printed++ < 10) | |
7a73bd7f SR |
420 | printk(KERN_ERR "iSeries_read_word: invalid access at IO address %p\n", |
421 | addr); | |
1da177e4 LT |
422 | return 0xffff; |
423 | } | |
424 | do { | |
1da177e4 | 425 | HvCall3Ret16(HvCallPciBarLoad16, &ret, dsa, |
7a73bd7f SR |
426 | bar_offset, 0); |
427 | } while (check_return_code("RDW", dn, &retry, ret.rc) != 0); | |
1da177e4 | 428 | |
4cb3cee0 | 429 | return ret.value; |
1da177e4 | 430 | } |
1da177e4 | 431 | |
7a73bd7f | 432 | static u32 iSeries_read_long(const volatile void __iomem *addr) |
1da177e4 | 433 | { |
7a73bd7f | 434 | u64 bar_offset; |
1da177e4 | 435 | u64 dsa; |
a2ebaf25 | 436 | int retry = 0; |
1da177e4 | 437 | struct HvCallPci_LoadReturn ret; |
7a73bd7f SR |
438 | struct device_node *dn = |
439 | xlate_iomm_address(addr, &dsa, &bar_offset); | |
1da177e4 | 440 | |
7a73bd7f | 441 | if (dn == NULL) { |
1da177e4 LT |
442 | static unsigned long last_jiffies; |
443 | static int num_printed; | |
444 | ||
445 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
446 | last_jiffies = jiffies; | |
447 | num_printed = 0; | |
448 | } | |
449 | if (num_printed++ < 10) | |
7a73bd7f SR |
450 | printk(KERN_ERR "iSeries_read_long: invalid access at IO address %p\n", |
451 | addr); | |
1da177e4 LT |
452 | return 0xffffffff; |
453 | } | |
454 | do { | |
1da177e4 | 455 | HvCall3Ret16(HvCallPciBarLoad32, &ret, dsa, |
7a73bd7f SR |
456 | bar_offset, 0); |
457 | } while (check_return_code("RDL", dn, &retry, ret.rc) != 0); | |
1da177e4 | 458 | |
4cb3cee0 | 459 | return ret.value; |
1da177e4 | 460 | } |
1da177e4 LT |
461 | |
462 | /* | |
463 | * Write MM I/O Instructions for the iSeries | |
464 | * | |
1da177e4 | 465 | */ |
7a73bd7f | 466 | static void iSeries_write_byte(u8 data, volatile void __iomem *addr) |
1da177e4 | 467 | { |
7a73bd7f | 468 | u64 bar_offset; |
1da177e4 | 469 | u64 dsa; |
a2ebaf25 | 470 | int retry = 0; |
1da177e4 | 471 | u64 rc; |
7a73bd7f SR |
472 | struct device_node *dn = |
473 | xlate_iomm_address(addr, &dsa, &bar_offset); | |
1da177e4 | 474 | |
7a73bd7f | 475 | if (dn == NULL) { |
1da177e4 LT |
476 | static unsigned long last_jiffies; |
477 | static int num_printed; | |
478 | ||
479 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
480 | last_jiffies = jiffies; | |
481 | num_printed = 0; | |
482 | } | |
483 | if (num_printed++ < 10) | |
7a73bd7f | 484 | printk(KERN_ERR "iSeries_write_byte: invalid access at IO address %p\n", addr); |
1da177e4 LT |
485 | return; |
486 | } | |
487 | do { | |
7a73bd7f SR |
488 | rc = HvCall4(HvCallPciBarStore8, dsa, bar_offset, data, 0); |
489 | } while (check_return_code("WWB", dn, &retry, rc) != 0); | |
1da177e4 | 490 | } |
1da177e4 | 491 | |
7a73bd7f | 492 | static void iSeries_write_word(u16 data, volatile void __iomem *addr) |
1da177e4 | 493 | { |
7a73bd7f | 494 | u64 bar_offset; |
1da177e4 | 495 | u64 dsa; |
a2ebaf25 | 496 | int retry = 0; |
1da177e4 | 497 | u64 rc; |
7a73bd7f SR |
498 | struct device_node *dn = |
499 | xlate_iomm_address(addr, &dsa, &bar_offset); | |
1da177e4 | 500 | |
7a73bd7f | 501 | if (dn == NULL) { |
1da177e4 LT |
502 | static unsigned long last_jiffies; |
503 | static int num_printed; | |
504 | ||
505 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
506 | last_jiffies = jiffies; | |
507 | num_printed = 0; | |
508 | } | |
509 | if (num_printed++ < 10) | |
7a73bd7f SR |
510 | printk(KERN_ERR "iSeries_write_word: invalid access at IO address %p\n", |
511 | addr); | |
1da177e4 LT |
512 | return; |
513 | } | |
514 | do { | |
7a73bd7f SR |
515 | rc = HvCall4(HvCallPciBarStore16, dsa, bar_offset, data, 0); |
516 | } while (check_return_code("WWW", dn, &retry, rc) != 0); | |
1da177e4 | 517 | } |
1da177e4 | 518 | |
7a73bd7f | 519 | static void iSeries_write_long(u32 data, volatile void __iomem *addr) |
1da177e4 | 520 | { |
7a73bd7f | 521 | u64 bar_offset; |
1da177e4 | 522 | u64 dsa; |
a2ebaf25 | 523 | int retry = 0; |
1da177e4 | 524 | u64 rc; |
7a73bd7f SR |
525 | struct device_node *dn = |
526 | xlate_iomm_address(addr, &dsa, &bar_offset); | |
1da177e4 | 527 | |
7a73bd7f | 528 | if (dn == NULL) { |
1da177e4 LT |
529 | static unsigned long last_jiffies; |
530 | static int num_printed; | |
531 | ||
532 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
533 | last_jiffies = jiffies; | |
534 | num_printed = 0; | |
535 | } | |
536 | if (num_printed++ < 10) | |
7a73bd7f SR |
537 | printk(KERN_ERR "iSeries_write_long: invalid access at IO address %p\n", |
538 | addr); | |
1da177e4 LT |
539 | return; |
540 | } | |
541 | do { | |
7a73bd7f SR |
542 | rc = HvCall4(HvCallPciBarStore32, dsa, bar_offset, data, 0); |
543 | } while (check_return_code("WWL", dn, &retry, rc) != 0); | |
1da177e4 | 544 | } |
caf81329 | 545 | |
4cb3cee0 | 546 | static u8 iseries_readb(const volatile void __iomem *addr) |
caf81329 | 547 | { |
7a73bd7f | 548 | return iSeries_read_byte(addr); |
caf81329 | 549 | } |
caf81329 | 550 | |
4cb3cee0 | 551 | static u16 iseries_readw(const volatile void __iomem *addr) |
caf81329 | 552 | { |
7a73bd7f | 553 | return le16_to_cpu(iSeries_read_word(addr)); |
caf81329 | 554 | } |
caf81329 | 555 | |
4cb3cee0 | 556 | static u32 iseries_readl(const volatile void __iomem *addr) |
caf81329 | 557 | { |
7a73bd7f | 558 | return le32_to_cpu(iSeries_read_long(addr)); |
caf81329 | 559 | } |
caf81329 | 560 | |
4cb3cee0 | 561 | static u16 iseries_readw_be(const volatile void __iomem *addr) |
caf81329 | 562 | { |
7a73bd7f | 563 | return iSeries_read_word(addr); |
caf81329 | 564 | } |
caf81329 | 565 | |
4cb3cee0 | 566 | static u32 iseries_readl_be(const volatile void __iomem *addr) |
caf81329 | 567 | { |
7a73bd7f | 568 | return iSeries_read_long(addr); |
caf81329 | 569 | } |
caf81329 | 570 | |
4cb3cee0 | 571 | static void iseries_writeb(u8 data, volatile void __iomem *addr) |
caf81329 | 572 | { |
7a73bd7f | 573 | iSeries_write_byte(data, addr); |
caf81329 | 574 | } |
caf81329 | 575 | |
4cb3cee0 | 576 | static void iseries_writew(u16 data, volatile void __iomem *addr) |
caf81329 | 577 | { |
7a73bd7f | 578 | iSeries_write_word(cpu_to_le16(data), addr); |
caf81329 | 579 | } |
caf81329 | 580 | |
4cb3cee0 | 581 | static void iseries_writel(u32 data, volatile void __iomem *addr) |
caf81329 | 582 | { |
7a73bd7f | 583 | iSeries_write_long(cpu_to_le32(data), addr); |
caf81329 | 584 | } |
caf81329 | 585 | |
4cb3cee0 | 586 | static void iseries_writew_be(u16 data, volatile void __iomem *addr) |
caf81329 | 587 | { |
7a73bd7f | 588 | iSeries_write_word(data, addr); |
caf81329 | 589 | } |
caf81329 | 590 | |
4cb3cee0 | 591 | static void iseries_writel_be(u32 data, volatile void __iomem *addr) |
caf81329 | 592 | { |
7a73bd7f | 593 | iSeries_write_long(data, addr); |
caf81329 | 594 | } |
caf81329 | 595 | |
4cb3cee0 BH |
596 | static void iseries_readsb(const volatile void __iomem *addr, void *buf, |
597 | unsigned long count) | |
caf81329 | 598 | { |
4cb3cee0 BH |
599 | u8 *dst = buf; |
600 | while(count-- > 0) | |
7a73bd7f | 601 | *(dst++) = iSeries_read_byte(addr); |
caf81329 | 602 | } |
caf81329 | 603 | |
4cb3cee0 BH |
604 | static void iseries_readsw(const volatile void __iomem *addr, void *buf, |
605 | unsigned long count) | |
caf81329 | 606 | { |
4cb3cee0 BH |
607 | u16 *dst = buf; |
608 | while(count-- > 0) | |
7a73bd7f | 609 | *(dst++) = iSeries_read_word(addr); |
caf81329 | 610 | } |
caf81329 | 611 | |
4cb3cee0 BH |
612 | static void iseries_readsl(const volatile void __iomem *addr, void *buf, |
613 | unsigned long count) | |
caf81329 | 614 | { |
4cb3cee0 BH |
615 | u32 *dst = buf; |
616 | while(count-- > 0) | |
7a73bd7f | 617 | *(dst++) = iSeries_read_long(addr); |
caf81329 | 618 | } |
caf81329 | 619 | |
4cb3cee0 BH |
620 | static void iseries_writesb(volatile void __iomem *addr, const void *buf, |
621 | unsigned long count) | |
caf81329 | 622 | { |
4cb3cee0 BH |
623 | const u8 *src = buf; |
624 | while(count-- > 0) | |
7a73bd7f | 625 | iSeries_write_byte(*(src++), addr); |
caf81329 | 626 | } |
caf81329 | 627 | |
4cb3cee0 BH |
628 | static void iseries_writesw(volatile void __iomem *addr, const void *buf, |
629 | unsigned long count) | |
caf81329 | 630 | { |
4cb3cee0 BH |
631 | const u16 *src = buf; |
632 | while(count-- > 0) | |
7a73bd7f | 633 | iSeries_write_word(*(src++), addr); |
caf81329 | 634 | } |
caf81329 | 635 | |
4cb3cee0 BH |
636 | static void iseries_writesl(volatile void __iomem *addr, const void *buf, |
637 | unsigned long count) | |
caf81329 | 638 | { |
4cb3cee0 BH |
639 | const u32 *src = buf; |
640 | while(count-- > 0) | |
7a73bd7f | 641 | iSeries_write_long(*(src++), addr); |
caf81329 | 642 | } |
caf81329 | 643 | |
4cb3cee0 BH |
644 | static void iseries_memset_io(volatile void __iomem *addr, int c, |
645 | unsigned long n) | |
caf81329 | 646 | { |
4cb3cee0 | 647 | volatile char __iomem *d = addr; |
caf81329 | 648 | |
4cb3cee0 | 649 | while (n-- > 0) |
7a73bd7f | 650 | iSeries_write_byte(c, d++); |
caf81329 | 651 | } |
caf81329 | 652 | |
4cb3cee0 BH |
653 | static void iseries_memcpy_fromio(void *dest, const volatile void __iomem *src, |
654 | unsigned long n) | |
caf81329 | 655 | { |
4cb3cee0 BH |
656 | char *d = dest; |
657 | const volatile char __iomem *s = src; | |
caf81329 | 658 | |
4cb3cee0 | 659 | while (n-- > 0) |
7a73bd7f | 660 | *d++ = iSeries_read_byte(s++); |
caf81329 | 661 | } |
caf81329 | 662 | |
4cb3cee0 BH |
663 | static void iseries_memcpy_toio(volatile void __iomem *dest, const void *src, |
664 | unsigned long n) | |
caf81329 | 665 | { |
4cb3cee0 BH |
666 | const char *s = src; |
667 | volatile char __iomem *d = dest; | |
caf81329 | 668 | |
4cb3cee0 | 669 | while (n-- > 0) |
7a73bd7f | 670 | iSeries_write_byte(*s++, d++); |
caf81329 | 671 | } |
caf81329 | 672 | |
4cb3cee0 BH |
673 | /* We only set MMIO ops. The default PIO ops will be default |
674 | * to the MMIO ops + pci_io_base which is 0 on iSeries as | |
675 | * expected so both should work. | |
676 | * | |
677 | * Note that we don't implement the readq/writeq versions as | |
678 | * I don't know of an HV call for doing so. Thus, the default | |
679 | * operation will be used instead, which will fault a the value | |
680 | * return by iSeries for MMIO addresses always hits a non mapped | |
681 | * area. This is as good as the BUG() we used to have there. | |
682 | */ | |
683 | static struct ppc_pci_io __initdata iseries_pci_io = { | |
684 | .readb = iseries_readb, | |
685 | .readw = iseries_readw, | |
686 | .readl = iseries_readl, | |
687 | .readw_be = iseries_readw_be, | |
688 | .readl_be = iseries_readl_be, | |
689 | .writeb = iseries_writeb, | |
690 | .writew = iseries_writew, | |
691 | .writel = iseries_writel, | |
692 | .writew_be = iseries_writew_be, | |
693 | .writel_be = iseries_writel_be, | |
694 | .readsb = iseries_readsb, | |
695 | .readsw = iseries_readsw, | |
696 | .readsl = iseries_readsl, | |
697 | .writesb = iseries_writesb, | |
698 | .writesw = iseries_writesw, | |
699 | .writesl = iseries_writesl, | |
700 | .memset_io = iseries_memset_io, | |
701 | .memcpy_fromio = iseries_memcpy_fromio, | |
702 | .memcpy_toio = iseries_memcpy_toio, | |
703 | }; | |
caf81329 | 704 | |
4cb3cee0 BH |
705 | /* |
706 | * iSeries_pcibios_init | |
707 | * | |
708 | * Description: | |
709 | * This function checks for all possible system PCI host bridges that connect | |
710 | * PCI buses. The system hypervisor is queried as to the guest partition | |
711 | * ownership status. A pci_controller is built for any bus which is partially | |
712 | * owned or fully owned by this guest partition. | |
713 | */ | |
714 | void __init iSeries_pcibios_init(void) | |
caf81329 | 715 | { |
4cb3cee0 BH |
716 | struct pci_controller *phb; |
717 | struct device_node *root = of_find_node_by_path("/"); | |
718 | struct device_node *node = NULL; | |
caf81329 | 719 | |
4cb3cee0 BH |
720 | /* Install IO hooks */ |
721 | ppc_pci_io = iseries_pci_io; | |
caf81329 | 722 | |
3d5134ee BH |
723 | /* iSeries has no IO space in the common sense, it needs to set |
724 | * the IO base to 0 | |
725 | */ | |
726 | pci_io_base = 0; | |
727 | ||
4cb3cee0 BH |
728 | if (root == NULL) { |
729 | printk(KERN_CRIT "iSeries_pcibios_init: can't find root " | |
730 | "of device tree\n"); | |
731 | return; | |
732 | } | |
733 | while ((node = of_get_next_child(root, node)) != NULL) { | |
734 | HvBusNumber bus; | |
735 | const u32 *busp; | |
caf81329 | 736 | |
4cb3cee0 BH |
737 | if ((node->type == NULL) || (strcmp(node->type, "pci") != 0)) |
738 | continue; | |
caf81329 | 739 | |
e2eb6392 | 740 | busp = of_get_property(node, "bus-range", NULL); |
4cb3cee0 BH |
741 | if (busp == NULL) |
742 | continue; | |
743 | bus = *busp; | |
744 | printk("bus %d appears to exist\n", bus); | |
745 | phb = pcibios_alloc_controller(node); | |
746 | if (phb == NULL) | |
747 | continue; | |
caf81329 | 748 | |
bf440b71 | 749 | phb->pci_mem_offset = bus; |
4cb3cee0 BH |
750 | phb->first_busno = bus; |
751 | phb->last_busno = bus; | |
752 | phb->ops = &iSeries_pci_ops; | |
753 | } | |
caf81329 | 754 | |
4cb3cee0 | 755 | of_node_put(root); |
caf81329 | 756 | |
4cb3cee0 | 757 | pci_devs_phb_init(); |
caf81329 | 758 | } |
4cb3cee0 | 759 |