1 2#undef DEBUG 3 4#include <linux/kernel.h> 5 6#include <asm/io.h> 7#include <asm/machdep.h> 8#include <asm/pgtable.h> 9#include <asm/ppc-pci.h> 10 11#include "io-workarounds.h" 12 13#define IOWA_MAX_BUS 8 14 15static struct iowa_bus iowa_busses[IOWA_MAX_BUS]; 16static unsigned int iowa_bus_count; 17 18static struct iowa_bus *iowa_pci_find(unsigned long vaddr, unsigned long paddr) 19{ 20 int i, j; 21 struct resource *res; 22 unsigned long vstart, vend; 23 24 for (i = 0; i < iowa_bus_count; i++) { 25 struct iowa_bus *bus = &iowa_busses[i]; 26 struct pci_controller *phb = bus->phb; 27 28 if (vaddr) { 29 vstart = (unsigned long)phb->io_base_virt; 30 vend = vstart + phb->pci_io_size - 1; 31 if ((vaddr >= vstart) && (vaddr <= vend)) 32 return bus; 33 } 34 35 if (paddr) 36 for (j = 0; j < 3; j++) { 37 res = &phb->mem_resources[j]; 38 if (paddr >= res->start && paddr <= res->end) 39 return bus; 40 } 41 } 42 43 return NULL; 44} 45 46struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr) 47{ 48 struct iowa_bus *bus; 49 int token; 50 51 token = PCI_GET_ADDR_TOKEN(addr); 52 53 if (token && token <= iowa_bus_count) 54 bus = &iowa_busses[token - 1]; 55 else { 56 unsigned long vaddr, paddr; 57 pte_t *ptep; 58 59 vaddr = (unsigned long)PCI_FIX_ADDR(addr); 60 if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END) 61 return NULL; 62 63 ptep = find_linux_pte(init_mm.pgd, vaddr); 64 if (ptep == NULL) 65 paddr = 0; 66 else 67 paddr = pte_pfn(*ptep) << PAGE_SHIFT; 68 bus = iowa_pci_find(vaddr, paddr); 69 70 if (bus == NULL) 71 return NULL; 72 } 73 74 return bus; 75} 76 77struct iowa_bus *iowa_pio_find_bus(unsigned long port) 78{ 79 unsigned long vaddr = (unsigned long)pci_io_base + port; 80 return iowa_pci_find(vaddr, 0); 81} 82 83 84#define DEF_PCI_AC_RET(name, ret, at, al, space, aa) \ 85static ret iowa_##name at \ 86{ \ 87 struct iowa_bus *bus; \ 88 bus = iowa_##space##_find_bus(aa); \ 89 if (bus && bus->ops && bus->ops->name) \ 90 return bus->ops->name al; \ 91 return __do_##name al; \ 92} 93 94#define DEF_PCI_AC_NORET(name, at, al, space, aa) \ 95static void iowa_##name at \ 96{ \ 97 struct iowa_bus *bus; \ 98 bus = iowa_##space##_find_bus(aa); \ 99 if (bus && bus->ops && bus->ops->name) { \ 100 bus->ops->name al; \ 101 return; \ 102 } \ 103 __do_##name al; \ 104} 105 106#include <asm/io-defs.h> 107 108#undef DEF_PCI_AC_RET 109#undef DEF_PCI_AC_NORET 110 111static const struct ppc_pci_io __devinitconst iowa_pci_io = { 112 113#define DEF_PCI_AC_RET(name, ret, at, al, space, aa) .name = iowa_##name, 114#define DEF_PCI_AC_NORET(name, at, al, space, aa) .name = iowa_##name, 115 116#include <asm/io-defs.h> 117 118#undef DEF_PCI_AC_RET 119#undef DEF_PCI_AC_NORET 120 121}; 122 123static void __iomem *iowa_ioremap(phys_addr_t addr, unsigned long size, 124 unsigned long flags, void *caller) 125{ 126 struct iowa_bus *bus; 127 void __iomem *res = __ioremap_caller(addr, size, flags, caller); 128 int busno; 129 130 bus = iowa_pci_find(0, (unsigned long)addr); 131 if (bus != NULL) { 132 busno = bus - iowa_busses; 133 PCI_SET_ADDR_TOKEN(res, busno + 1); 134 } 135 return res; 136} 137 138void __devinit iowa_register_bus(struct pci_controller *phb, 139 struct ppc_pci_io *ops, 140 int (*initfunc)(struct iowa_bus *, void *), void *data) 141{ 142 struct iowa_bus *bus; 143 struct device_node *np = phb->dn; 144 145 if (iowa_bus_count >= IOWA_MAX_BUS) { 146 pr_err("IOWA:Too many pci bridges, " 147 "workarounds disabled for %s\n", np->full_name); 148 return; 149 } 150 151 bus = &iowa_busses[iowa_bus_count]; 152 bus->phb = phb; 153 bus->ops = ops; 154 155 if (initfunc) 156 if ((*initfunc)(bus, data)) 157 return; 158 159 iowa_bus_count++; 160 161 pr_debug("IOWA:[%d]Add bus, %s.\n", iowa_bus_count-1, np->full_name); 162} 163 164void __devinit io_workaround_init(void) 165{ 166 static int io_workaround_inited; 167 168 if (io_workaround_inited) 169 return; 170 ppc_pci_io = iowa_pci_io; 171 ppc_md.ioremap = iowa_ioremap; 172 io_workaround_inited = 1; 173} 174