diff options
Diffstat (limited to 'arch/powerpc/kernel/io-workarounds.c')
-rw-r--r-- | arch/powerpc/kernel/io-workarounds.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/arch/powerpc/kernel/io-workarounds.c b/arch/powerpc/kernel/io-workarounds.c new file mode 100644 index 000000000000..ffafaea3d261 --- /dev/null +++ b/arch/powerpc/kernel/io-workarounds.c | |||
@@ -0,0 +1,188 @@ | |||
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 | |||