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> | |
27 | #include <linux/ide.h> | |
28 | #include <linux/pci.h> | |
29 | ||
30 | #include <asm/io.h> | |
31 | #include <asm/irq.h> | |
32 | #include <asm/prom.h> | |
33 | #include <asm/machdep.h> | |
34 | #include <asm/pci-bridge.h> | |
1da177e4 | 35 | #include <asm/iommu.h> |
426c1a11 | 36 | #include <asm/abs_addr.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 | |
1da177e4 | 48 | /* |
d387899f | 49 | * Forward declares of prototypes. |
1da177e4 | 50 | */ |
252e75a5 | 51 | static struct device_node *find_Device_Node(int bus, int devfn); |
1da177e4 | 52 | |
d387899f | 53 | static int Pci_Retry_Max = 3; /* Only retry 3 times */ |
1da177e4 LT |
54 | static int Pci_Error_Flag = 1; /* Set Retry Error on. */ |
55 | ||
56 | static struct pci_ops iSeries_pci_ops; | |
57 | ||
58 | /* | |
59 | * Table defines | |
60 | * Each Entry size is 4 MB * 1024 Entries = 4GB I/O address space. | |
61 | */ | |
62 | #define IOMM_TABLE_MAX_ENTRIES 1024 | |
63 | #define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL | |
64 | #define BASE_IO_MEMORY 0xE000000000000000UL | |
65 | ||
b58b7f98 | 66 | static unsigned long max_io_memory = BASE_IO_MEMORY; |
1da177e4 LT |
67 | static long current_iomm_table_entry; |
68 | ||
69 | /* | |
70 | * Lookup Tables. | |
71 | */ | |
b58b7f98 SR |
72 | static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES]; |
73 | static u8 iobar_table[IOMM_TABLE_MAX_ENTRIES]; | |
1da177e4 | 74 | |
b58b7f98 | 75 | static const char pci_io_text[] = "iSeries PCI I/O"; |
1da177e4 LT |
76 | static DEFINE_SPINLOCK(iomm_table_lock); |
77 | ||
1da177e4 LT |
78 | /* |
79 | * iomm_table_allocate_entry | |
80 | * | |
81 | * Adds pci_dev entry in address translation table | |
82 | * | |
83 | * - Allocates the number of entries required in table base on BAR | |
84 | * size. | |
85 | * - Allocates starting at BASE_IO_MEMORY and increases. | |
86 | * - The size is round up to be a multiple of entry size. | |
87 | * - CurrentIndex is incremented to keep track of the last entry. | |
88 | * - Builds the resource entry for allocated BARs. | |
89 | */ | |
90 | static void iomm_table_allocate_entry(struct pci_dev *dev, int bar_num) | |
91 | { | |
92 | struct resource *bar_res = &dev->resource[bar_num]; | |
93 | long bar_size = pci_resource_len(dev, bar_num); | |
94 | ||
95 | /* | |
96 | * No space to allocate, quick exit, skip Allocation. | |
97 | */ | |
98 | if (bar_size == 0) | |
99 | return; | |
100 | /* | |
101 | * Set Resource values. | |
102 | */ | |
103 | spin_lock(&iomm_table_lock); | |
104 | bar_res->name = pci_io_text; | |
b58b7f98 | 105 | bar_res->start = BASE_IO_MEMORY + |
1da177e4 | 106 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; |
1da177e4 LT |
107 | bar_res->end = bar_res->start + bar_size - 1; |
108 | /* | |
109 | * Allocate the number of table entries needed for BAR. | |
110 | */ | |
111 | while (bar_size > 0 ) { | |
112 | iomm_table[current_iomm_table_entry] = dev->sysdata; | |
113 | iobar_table[current_iomm_table_entry] = bar_num; | |
114 | bar_size -= IOMM_TABLE_ENTRY_SIZE; | |
115 | ++current_iomm_table_entry; | |
116 | } | |
117 | max_io_memory = BASE_IO_MEMORY + | |
b58b7f98 | 118 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; |
1da177e4 LT |
119 | spin_unlock(&iomm_table_lock); |
120 | } | |
121 | ||
122 | /* | |
123 | * allocate_device_bars | |
124 | * | |
125 | * - Allocates ALL pci_dev BAR's and updates the resources with the | |
126 | * BAR value. BARS with zero length will have the resources | |
127 | * The HvCallPci_getBarParms is used to get the size of the BAR | |
128 | * space. It calls iomm_table_allocate_entry to allocate | |
129 | * each entry. | |
130 | * - Loops through The Bar resources(0 - 5) including the ROM | |
131 | * is resource(6). | |
132 | */ | |
133 | static void allocate_device_bars(struct pci_dev *dev) | |
134 | { | |
1da177e4 LT |
135 | int bar_num; |
136 | ||
b58b7f98 | 137 | for (bar_num = 0; bar_num <= PCI_ROM_RESOURCE; ++bar_num) |
1da177e4 | 138 | iomm_table_allocate_entry(dev, bar_num); |
1da177e4 LT |
139 | } |
140 | ||
141 | /* | |
142 | * Log error information to system console. | |
143 | * Filter out the device not there errors. | |
144 | * PCI: EADs Connect Failed 0x18.58.10 Rc: 0x00xx | |
145 | * PCI: Read Vendor Failed 0x18.58.10 Rc: 0x00xx | |
146 | * PCI: Connect Bus Unit Failed 0x18.58.10 Rc: 0x00xx | |
147 | */ | |
148 | static void pci_Log_Error(char *Error_Text, int Bus, int SubBus, | |
149 | int AgentId, int HvRc) | |
150 | { | |
151 | if (HvRc == 0x0302) | |
152 | return; | |
153 | printk(KERN_ERR "PCI: %s Failed: 0x%02X.%02X.%02X Rc: 0x%04X", | |
154 | Error_Text, Bus, SubBus, AgentId, HvRc); | |
155 | } | |
156 | ||
1da177e4 | 157 | /* |
b58b7f98 | 158 | * iSeries_pcibios_init |
1da177e4 LT |
159 | * |
160 | * Description: | |
161 | * This function checks for all possible system PCI host bridges that connect | |
162 | * PCI buses. The system hypervisor is queried as to the guest partition | |
163 | * ownership status. A pci_controller is built for any bus which is partially | |
164 | * owned or fully owned by this guest partition. | |
165 | */ | |
b58b7f98 | 166 | void iSeries_pcibios_init(void) |
1da177e4 LT |
167 | { |
168 | struct pci_controller *phb; | |
095eed4f SR |
169 | struct device_node *root = of_find_node_by_path("/"); |
170 | struct device_node *node = NULL; | |
0d177df1 | 171 | |
095eed4f SR |
172 | if (root == NULL) { |
173 | printk(KERN_CRIT "iSeries_pcibios_init: can't find root " | |
174 | "of device tree\n"); | |
175 | return; | |
176 | } | |
177 | while ((node = of_get_next_child(root, node)) != NULL) { | |
0d177df1 | 178 | HvBusNumber bus; |
c4c7cba9 | 179 | const u32 *busp; |
0d177df1 | 180 | |
095eed4f SR |
181 | if ((node->type == NULL) || (strcmp(node->type, "pci") != 0)) |
182 | continue; | |
183 | ||
c4c7cba9 | 184 | busp = get_property(node, "bus-range", NULL); |
0d177df1 SR |
185 | if (busp == NULL) |
186 | continue; | |
187 | bus = *busp; | |
188 | printk("bus %d appears to exist\n", bus); | |
189 | phb = pcibios_alloc_controller(node); | |
190 | if (phb == NULL) | |
191 | continue; | |
192 | ||
193 | phb->pci_mem_offset = phb->local_number = bus; | |
194 | phb->first_busno = bus; | |
195 | phb->last_busno = bus; | |
196 | phb->ops = &iSeries_pci_ops; | |
1da177e4 | 197 | } |
095eed4f SR |
198 | |
199 | of_node_put(root); | |
200 | ||
201 | pci_devs_phb_init(); | |
1da177e4 LT |
202 | } |
203 | ||
204 | /* | |
d387899f | 205 | * iSeries_pci_final_fixup(void) |
1da177e4 LT |
206 | */ |
207 | void __init iSeries_pci_final_fixup(void) | |
208 | { | |
209 | struct pci_dev *pdev = NULL; | |
252e75a5 | 210 | struct device_node *node; |
d387899f | 211 | int DeviceCount = 0; |
1da177e4 | 212 | |
1da177e4 LT |
213 | /* Fix up at the device node and pci_dev relationship */ |
214 | mf_display_src(0xC9000100); | |
215 | ||
216 | printk("pcibios_final_fixup\n"); | |
217 | for_each_pci_dev(pdev) { | |
218 | node = find_Device_Node(pdev->bus->number, pdev->devfn); | |
219 | printk("pci dev %p (%x.%x), node %p\n", pdev, | |
220 | pdev->bus->number, pdev->devfn, node); | |
221 | ||
222 | if (node != NULL) { | |
b0252793 | 223 | struct pci_dn *pdn = PCI_DN(node); |
c4c7cba9 | 224 | const u32 *agent; |
b0252793 | 225 | |
c4c7cba9 | 226 | agent = get_property(node, "linux,agent-id", NULL); |
b0252793 SR |
227 | if ((pdn != NULL) && (agent != NULL)) { |
228 | u8 irq = iSeries_allocate_IRQ(pdn->busno, 0, | |
229 | pdn->bussubno); | |
230 | int err; | |
231 | ||
232 | err = HvCallXm_connectBusUnit(pdn->busno, pdn->bussubno, | |
233 | *agent, irq); | |
234 | if (err) | |
235 | pci_Log_Error("Connect Bus Unit", | |
236 | pdn->busno, pdn->bussubno, *agent, err); | |
237 | else { | |
238 | err = HvCallPci_configStore8(pdn->busno, pdn->bussubno, | |
239 | *agent, | |
240 | PCI_INTERRUPT_LINE, | |
241 | irq); | |
242 | if (err) | |
243 | pci_Log_Error("PciCfgStore Irq Failed!", | |
244 | pdn->busno, pdn->bussubno, *agent, err); | |
245 | } | |
246 | if (!err) | |
247 | pdev->irq = irq; | |
248 | } | |
249 | ||
1da177e4 LT |
250 | ++DeviceCount; |
251 | pdev->sysdata = (void *)node; | |
252e75a5 | 252 | PCI_DN(node)->pcidev = pdev; |
1da177e4 | 253 | allocate_device_bars(pdev); |
061c063e | 254 | iSeries_Device_Information(pdev, DeviceCount); |
1da177e4 LT |
255 | iommu_devnode_init_iSeries(node); |
256 | } else | |
257 | printk("PCI: Device Tree not found for 0x%016lX\n", | |
258 | (unsigned long)pdev); | |
1da177e4 LT |
259 | } |
260 | iSeries_activate_IRQs(); | |
261 | mf_display_src(0xC9000200); | |
262 | } | |
263 | ||
264 | void pcibios_fixup_bus(struct pci_bus *PciBus) | |
265 | { | |
1da177e4 LT |
266 | } |
267 | ||
268 | void pcibios_fixup_resources(struct pci_dev *pdev) | |
269 | { | |
d387899f | 270 | } |
1da177e4 | 271 | |
1da177e4 LT |
272 | /* |
273 | * I/0 Memory copy MUST use mmio commands on iSeries | |
274 | * To do; For performance, include the hv call directly | |
275 | */ | |
276 | void iSeries_memset_io(volatile void __iomem *dest, char c, size_t Count) | |
277 | { | |
278 | u8 ByteValue = c; | |
279 | long NumberOfBytes = Count; | |
280 | ||
281 | while (NumberOfBytes > 0) { | |
282 | iSeries_Write_Byte(ByteValue, dest++); | |
283 | -- NumberOfBytes; | |
284 | } | |
285 | } | |
286 | EXPORT_SYMBOL(iSeries_memset_io); | |
287 | ||
288 | void iSeries_memcpy_toio(volatile void __iomem *dest, void *source, size_t count) | |
289 | { | |
290 | char *src = source; | |
291 | long NumberOfBytes = count; | |
292 | ||
293 | while (NumberOfBytes > 0) { | |
294 | iSeries_Write_Byte(*src++, dest++); | |
295 | -- NumberOfBytes; | |
296 | } | |
297 | } | |
298 | EXPORT_SYMBOL(iSeries_memcpy_toio); | |
299 | ||
300 | void iSeries_memcpy_fromio(void *dest, const volatile void __iomem *src, size_t count) | |
301 | { | |
302 | char *dst = dest; | |
303 | long NumberOfBytes = count; | |
304 | ||
305 | while (NumberOfBytes > 0) { | |
306 | *dst++ = iSeries_Read_Byte(src++); | |
307 | -- NumberOfBytes; | |
308 | } | |
309 | } | |
310 | EXPORT_SYMBOL(iSeries_memcpy_fromio); | |
311 | ||
312 | /* | |
313 | * Look down the chain to find the matching Device Device | |
314 | */ | |
252e75a5 | 315 | static struct device_node *find_Device_Node(int bus, int devfn) |
1da177e4 | 316 | { |
96ff6afa SR |
317 | struct device_node *node; |
318 | ||
319 | for (node = NULL; (node = of_find_all_nodes(node)); ) { | |
320 | struct pci_dn *pdn = PCI_DN(node); | |
1da177e4 | 321 | |
96ff6afa SR |
322 | if (pdn && (bus == pdn->busno) && (devfn == pdn->devfn)) |
323 | return node; | |
1da177e4 LT |
324 | } |
325 | return NULL; | |
326 | } | |
327 | ||
328 | #if 0 | |
329 | /* | |
330 | * Returns the device node for the passed pci_dev | |
331 | * Sanity Check Node PciDev to passed pci_dev | |
332 | * If none is found, returns a NULL which the client must handle. | |
333 | */ | |
252e75a5 | 334 | static struct device_node *get_Device_Node(struct pci_dev *pdev) |
1da177e4 | 335 | { |
252e75a5 | 336 | struct device_node *node; |
1da177e4 LT |
337 | |
338 | node = pdev->sysdata; | |
252e75a5 | 339 | if (node == NULL || PCI_DN(node)->pcidev != pdev) |
1da177e4 LT |
340 | node = find_Device_Node(pdev->bus->number, pdev->devfn); |
341 | return node; | |
342 | } | |
343 | #endif | |
344 | ||
345 | /* | |
346 | * Config space read and write functions. | |
347 | * For now at least, we look for the device node for the bus and devfn | |
348 | * that we are asked to access. It may be possible to translate the devfn | |
349 | * to a subbus and deviceid more directly. | |
350 | */ | |
351 | static u64 hv_cfg_read_func[4] = { | |
352 | HvCallPciConfigLoad8, HvCallPciConfigLoad16, | |
353 | HvCallPciConfigLoad32, HvCallPciConfigLoad32 | |
354 | }; | |
355 | ||
356 | static u64 hv_cfg_write_func[4] = { | |
357 | HvCallPciConfigStore8, HvCallPciConfigStore16, | |
358 | HvCallPciConfigStore32, HvCallPciConfigStore32 | |
359 | }; | |
360 | ||
361 | /* | |
362 | * Read PCI config space | |
363 | */ | |
364 | static int iSeries_pci_read_config(struct pci_bus *bus, unsigned int devfn, | |
365 | int offset, int size, u32 *val) | |
366 | { | |
252e75a5 | 367 | struct device_node *node = find_Device_Node(bus->number, devfn); |
1da177e4 LT |
368 | u64 fn; |
369 | struct HvCallPci_LoadReturn ret; | |
370 | ||
371 | if (node == NULL) | |
372 | return PCIBIOS_DEVICE_NOT_FOUND; | |
373 | if (offset > 255) { | |
374 | *val = ~0; | |
375 | return PCIBIOS_BAD_REGISTER_NUMBER; | |
376 | } | |
377 | ||
378 | fn = hv_cfg_read_func[(size - 1) & 3]; | |
20f48ccf | 379 | HvCall3Ret16(fn, &ret, iseries_ds_addr(node), offset, 0); |
1da177e4 LT |
380 | |
381 | if (ret.rc != 0) { | |
382 | *val = ~0; | |
383 | return PCIBIOS_DEVICE_NOT_FOUND; /* or something */ | |
384 | } | |
385 | ||
386 | *val = ret.value; | |
387 | return 0; | |
388 | } | |
389 | ||
390 | /* | |
391 | * Write PCI config space | |
392 | */ | |
393 | ||
394 | static int iSeries_pci_write_config(struct pci_bus *bus, unsigned int devfn, | |
395 | int offset, int size, u32 val) | |
396 | { | |
252e75a5 | 397 | struct device_node *node = find_Device_Node(bus->number, devfn); |
1da177e4 LT |
398 | u64 fn; |
399 | u64 ret; | |
400 | ||
401 | if (node == NULL) | |
402 | return PCIBIOS_DEVICE_NOT_FOUND; | |
403 | if (offset > 255) | |
404 | return PCIBIOS_BAD_REGISTER_NUMBER; | |
405 | ||
406 | fn = hv_cfg_write_func[(size - 1) & 3]; | |
20f48ccf | 407 | ret = HvCall4(fn, iseries_ds_addr(node), offset, val, 0); |
1da177e4 LT |
408 | |
409 | if (ret != 0) | |
410 | return PCIBIOS_DEVICE_NOT_FOUND; | |
411 | ||
412 | return 0; | |
413 | } | |
414 | ||
415 | static struct pci_ops iSeries_pci_ops = { | |
416 | .read = iSeries_pci_read_config, | |
417 | .write = iSeries_pci_write_config | |
418 | }; | |
419 | ||
420 | /* | |
421 | * Check Return Code | |
422 | * -> On Failure, print and log information. | |
423 | * Increment Retry Count, if exceeds max, panic partition. | |
1da177e4 LT |
424 | * |
425 | * PCI: Device 23.90 ReadL I/O Error( 0): 0x1234 | |
426 | * PCI: Device 23.90 ReadL Retry( 1) | |
427 | * PCI: Device 23.90 ReadL Retry Successful(1) | |
428 | */ | |
252e75a5 | 429 | static int CheckReturnCode(char *TextHdr, struct device_node *DevNode, |
a2ebaf25 | 430 | int *retry, u64 ret) |
1da177e4 LT |
431 | { |
432 | if (ret != 0) { | |
252e75a5 SR |
433 | struct pci_dn *pdn = PCI_DN(DevNode); |
434 | ||
a2ebaf25 | 435 | (*retry)++; |
1da177e4 | 436 | printk("PCI: %s: Device 0x%04X:%02X I/O Error(%2d): 0x%04X\n", |
20f48ccf | 437 | TextHdr, pdn->busno, pdn->devfn, |
a2ebaf25 | 438 | *retry, (int)ret); |
1da177e4 LT |
439 | /* |
440 | * Bump the retry and check for retry count exceeded. | |
441 | * If, Exceeded, panic the system. | |
442 | */ | |
a2ebaf25 | 443 | if (((*retry) > Pci_Retry_Max) && |
1da177e4 LT |
444 | (Pci_Error_Flag > 0)) { |
445 | mf_display_src(0xB6000103); | |
a2ebaf25 | 446 | panic_timeout = 0; |
1da177e4 LT |
447 | panic("PCI: Hardware I/O Error, SRC B6000103, " |
448 | "Automatic Reboot Disabled.\n"); | |
449 | } | |
450 | return -1; /* Retry Try */ | |
451 | } | |
a2ebaf25 | 452 | return 0; |
1da177e4 LT |
453 | } |
454 | ||
455 | /* | |
456 | * Translate the I/O Address into a device node, bar, and bar offset. | |
457 | * Note: Make sure the passed variable end up on the stack to avoid | |
458 | * the exposure of being device global. | |
459 | */ | |
252e75a5 | 460 | static inline struct device_node *xlate_iomm_address( |
1da177e4 LT |
461 | const volatile void __iomem *IoAddress, |
462 | u64 *dsaptr, u64 *BarOffsetPtr) | |
463 | { | |
464 | unsigned long OrigIoAddr; | |
465 | unsigned long BaseIoAddr; | |
466 | unsigned long TableIndex; | |
252e75a5 | 467 | struct device_node *DevNode; |
1da177e4 LT |
468 | |
469 | OrigIoAddr = (unsigned long __force)IoAddress; | |
470 | if ((OrigIoAddr < BASE_IO_MEMORY) || (OrigIoAddr >= max_io_memory)) | |
471 | return NULL; | |
472 | BaseIoAddr = OrigIoAddr - BASE_IO_MEMORY; | |
473 | TableIndex = BaseIoAddr / IOMM_TABLE_ENTRY_SIZE; | |
474 | DevNode = iomm_table[TableIndex]; | |
475 | ||
476 | if (DevNode != NULL) { | |
477 | int barnum = iobar_table[TableIndex]; | |
20f48ccf | 478 | *dsaptr = iseries_ds_addr(DevNode) | (barnum << 24); |
1da177e4 LT |
479 | *BarOffsetPtr = BaseIoAddr % IOMM_TABLE_ENTRY_SIZE; |
480 | } else | |
481 | panic("PCI: Invalid PCI IoAddress detected!\n"); | |
482 | return DevNode; | |
483 | } | |
484 | ||
485 | /* | |
486 | * Read MM I/O Instructions for the iSeries | |
487 | * On MM I/O error, all ones are returned and iSeries_pci_IoError is cal | |
488 | * else, data is returned in big Endian format. | |
489 | * | |
490 | * iSeries_Read_Byte = Read Byte ( 8 bit) | |
491 | * iSeries_Read_Word = Read Word (16 bit) | |
492 | * iSeries_Read_Long = Read Long (32 bit) | |
493 | */ | |
494 | u8 iSeries_Read_Byte(const volatile void __iomem *IoAddress) | |
495 | { | |
496 | u64 BarOffset; | |
497 | u64 dsa; | |
a2ebaf25 | 498 | int retry = 0; |
1da177e4 | 499 | struct HvCallPci_LoadReturn ret; |
252e75a5 | 500 | struct device_node *DevNode = |
1da177e4 LT |
501 | xlate_iomm_address(IoAddress, &dsa, &BarOffset); |
502 | ||
503 | if (DevNode == NULL) { | |
504 | static unsigned long last_jiffies; | |
505 | static int num_printed; | |
506 | ||
507 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
508 | last_jiffies = jiffies; | |
509 | num_printed = 0; | |
510 | } | |
511 | if (num_printed++ < 10) | |
512 | printk(KERN_ERR "iSeries_Read_Byte: invalid access at IO address %p\n", IoAddress); | |
513 | return 0xff; | |
514 | } | |
515 | do { | |
1da177e4 | 516 | HvCall3Ret16(HvCallPciBarLoad8, &ret, dsa, BarOffset, 0); |
a2ebaf25 | 517 | } while (CheckReturnCode("RDB", DevNode, &retry, ret.rc) != 0); |
1da177e4 LT |
518 | |
519 | return (u8)ret.value; | |
520 | } | |
521 | EXPORT_SYMBOL(iSeries_Read_Byte); | |
522 | ||
523 | u16 iSeries_Read_Word(const volatile void __iomem *IoAddress) | |
524 | { | |
525 | u64 BarOffset; | |
526 | u64 dsa; | |
a2ebaf25 | 527 | int retry = 0; |
1da177e4 | 528 | struct HvCallPci_LoadReturn ret; |
252e75a5 | 529 | struct device_node *DevNode = |
1da177e4 LT |
530 | xlate_iomm_address(IoAddress, &dsa, &BarOffset); |
531 | ||
532 | if (DevNode == NULL) { | |
533 | static unsigned long last_jiffies; | |
534 | static int num_printed; | |
535 | ||
536 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
537 | last_jiffies = jiffies; | |
538 | num_printed = 0; | |
539 | } | |
540 | if (num_printed++ < 10) | |
541 | printk(KERN_ERR "iSeries_Read_Word: invalid access at IO address %p\n", IoAddress); | |
542 | return 0xffff; | |
543 | } | |
544 | do { | |
1da177e4 LT |
545 | HvCall3Ret16(HvCallPciBarLoad16, &ret, dsa, |
546 | BarOffset, 0); | |
a2ebaf25 | 547 | } while (CheckReturnCode("RDW", DevNode, &retry, ret.rc) != 0); |
1da177e4 LT |
548 | |
549 | return swab16((u16)ret.value); | |
550 | } | |
551 | EXPORT_SYMBOL(iSeries_Read_Word); | |
552 | ||
553 | u32 iSeries_Read_Long(const volatile void __iomem *IoAddress) | |
554 | { | |
555 | u64 BarOffset; | |
556 | u64 dsa; | |
a2ebaf25 | 557 | int retry = 0; |
1da177e4 | 558 | struct HvCallPci_LoadReturn ret; |
252e75a5 | 559 | struct device_node *DevNode = |
1da177e4 LT |
560 | xlate_iomm_address(IoAddress, &dsa, &BarOffset); |
561 | ||
562 | if (DevNode == NULL) { | |
563 | static unsigned long last_jiffies; | |
564 | static int num_printed; | |
565 | ||
566 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
567 | last_jiffies = jiffies; | |
568 | num_printed = 0; | |
569 | } | |
570 | if (num_printed++ < 10) | |
571 | printk(KERN_ERR "iSeries_Read_Long: invalid access at IO address %p\n", IoAddress); | |
572 | return 0xffffffff; | |
573 | } | |
574 | do { | |
1da177e4 LT |
575 | HvCall3Ret16(HvCallPciBarLoad32, &ret, dsa, |
576 | BarOffset, 0); | |
a2ebaf25 | 577 | } while (CheckReturnCode("RDL", DevNode, &retry, ret.rc) != 0); |
1da177e4 LT |
578 | |
579 | return swab32((u32)ret.value); | |
580 | } | |
581 | EXPORT_SYMBOL(iSeries_Read_Long); | |
582 | ||
583 | /* | |
584 | * Write MM I/O Instructions for the iSeries | |
585 | * | |
586 | * iSeries_Write_Byte = Write Byte (8 bit) | |
587 | * iSeries_Write_Word = Write Word(16 bit) | |
588 | * iSeries_Write_Long = Write Long(32 bit) | |
589 | */ | |
590 | void iSeries_Write_Byte(u8 data, volatile void __iomem *IoAddress) | |
591 | { | |
592 | u64 BarOffset; | |
593 | u64 dsa; | |
a2ebaf25 | 594 | int retry = 0; |
1da177e4 | 595 | u64 rc; |
252e75a5 | 596 | struct device_node *DevNode = |
1da177e4 LT |
597 | xlate_iomm_address(IoAddress, &dsa, &BarOffset); |
598 | ||
599 | if (DevNode == NULL) { | |
600 | static unsigned long last_jiffies; | |
601 | static int num_printed; | |
602 | ||
603 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
604 | last_jiffies = jiffies; | |
605 | num_printed = 0; | |
606 | } | |
607 | if (num_printed++ < 10) | |
608 | printk(KERN_ERR "iSeries_Write_Byte: invalid access at IO address %p\n", IoAddress); | |
609 | return; | |
610 | } | |
611 | do { | |
1da177e4 | 612 | rc = HvCall4(HvCallPciBarStore8, dsa, BarOffset, data, 0); |
a2ebaf25 | 613 | } while (CheckReturnCode("WWB", DevNode, &retry, rc) != 0); |
1da177e4 LT |
614 | } |
615 | EXPORT_SYMBOL(iSeries_Write_Byte); | |
616 | ||
617 | void iSeries_Write_Word(u16 data, volatile void __iomem *IoAddress) | |
618 | { | |
619 | u64 BarOffset; | |
620 | u64 dsa; | |
a2ebaf25 | 621 | int retry = 0; |
1da177e4 | 622 | u64 rc; |
252e75a5 | 623 | struct device_node *DevNode = |
1da177e4 LT |
624 | xlate_iomm_address(IoAddress, &dsa, &BarOffset); |
625 | ||
626 | if (DevNode == NULL) { | |
627 | static unsigned long last_jiffies; | |
628 | static int num_printed; | |
629 | ||
630 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
631 | last_jiffies = jiffies; | |
632 | num_printed = 0; | |
633 | } | |
634 | if (num_printed++ < 10) | |
635 | printk(KERN_ERR "iSeries_Write_Word: invalid access at IO address %p\n", IoAddress); | |
636 | return; | |
637 | } | |
638 | do { | |
1da177e4 | 639 | rc = HvCall4(HvCallPciBarStore16, dsa, BarOffset, swab16(data), 0); |
a2ebaf25 | 640 | } while (CheckReturnCode("WWW", DevNode, &retry, rc) != 0); |
1da177e4 LT |
641 | } |
642 | EXPORT_SYMBOL(iSeries_Write_Word); | |
643 | ||
644 | void iSeries_Write_Long(u32 data, volatile void __iomem *IoAddress) | |
645 | { | |
646 | u64 BarOffset; | |
647 | u64 dsa; | |
a2ebaf25 | 648 | int retry = 0; |
1da177e4 | 649 | u64 rc; |
252e75a5 | 650 | struct device_node *DevNode = |
1da177e4 LT |
651 | xlate_iomm_address(IoAddress, &dsa, &BarOffset); |
652 | ||
653 | if (DevNode == NULL) { | |
654 | static unsigned long last_jiffies; | |
655 | static int num_printed; | |
656 | ||
657 | if ((jiffies - last_jiffies) > 60 * HZ) { | |
658 | last_jiffies = jiffies; | |
659 | num_printed = 0; | |
660 | } | |
661 | if (num_printed++ < 10) | |
662 | printk(KERN_ERR "iSeries_Write_Long: invalid access at IO address %p\n", IoAddress); | |
663 | return; | |
664 | } | |
665 | do { | |
1da177e4 | 666 | rc = HvCall4(HvCallPciBarStore32, dsa, BarOffset, swab32(data), 0); |
a2ebaf25 | 667 | } while (CheckReturnCode("WWL", DevNode, &retry, rc) != 0); |
1da177e4 LT |
668 | } |
669 | EXPORT_SYMBOL(iSeries_Write_Long); |