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