]> git.ipfire.org Git - thirdparty/kernel/stable.git/blame - arch/powerpc/kernel/io-workarounds.c
powerpc/pci: Make IO workarounds init implicit when first bus is registered
[thirdparty/kernel/stable.git] / arch / powerpc / kernel / io-workarounds.c
CommitLineData
014da7ff 1/*
7cfb62a2
IK
2 * Support PCI IO workaround
3 *
014da7ff
BH
4 * Copyright (C) 2006 Benjamin Herrenschmidt <benh@kernel.crashing.org>
5 * IBM, Corp.
7cfb62a2 6 * (C) Copyright 2007-2008 TOSHIBA CORPORATION
014da7ff
BH
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>
7cfb62a2 15
014da7ff
BH
16#include <asm/io.h>
17#include <asm/machdep.h>
7cfb62a2 18#include <asm/pgtable.h>
014da7ff 19#include <asm/ppc-pci.h>
3cc30d07 20#include <asm/io-workarounds.h>
014da7ff 21
7cfb62a2 22#define IOWA_MAX_BUS 8
014da7ff 23
7cfb62a2
IK
24static struct iowa_bus iowa_busses[IOWA_MAX_BUS];
25static unsigned int iowa_bus_count;
014da7ff 26
7cfb62a2
IK
27static 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;
014da7ff 32
7cfb62a2
IK
33 for (i = 0; i < iowa_bus_count; i++) {
34 struct iowa_bus *bus = &iowa_busses[i];
35 struct pci_controller *phb = bus->phb;
014da7ff 36
7cfb62a2
IK
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 }
014da7ff 50 }
7cfb62a2 51
014da7ff
BH
52 return NULL;
53}
54
7cfb62a2 55struct iowa_bus *iowa_mem_find_bus(const PCI_IO_ADDR addr)
014da7ff 56{
7cfb62a2 57 struct iowa_bus *bus;
014da7ff
BH
58 int token;
59
014da7ff
BH
60 token = PCI_GET_ADDR_TOKEN(addr);
61
7cfb62a2
IK
62 if (token && token <= iowa_bus_count)
63 bus = &iowa_busses[token - 1];
014da7ff
BH
64 else {
65 unsigned long vaddr, paddr;
66 pte_t *ptep;
67
014da7ff 68 vaddr = (unsigned long)PCI_FIX_ADDR(addr);
7cfb62a2
IK
69 if (vaddr < PHB_IO_BASE || vaddr >= PHB_IO_END)
70 return NULL;
014da7ff 71
014da7ff
BH
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;
7cfb62a2 77 bus = iowa_pci_find(vaddr, paddr);
014da7ff 78
014da7ff 79 if (bus == NULL)
7cfb62a2 80 return NULL;
014da7ff
BH
81 }
82
7cfb62a2 83 return bus;
014da7ff
BH
84}
85
7cfb62a2 86struct iowa_bus *iowa_pio_find_bus(unsigned long port)
014da7ff 87{
7cfb62a2
IK
88 unsigned long vaddr = (unsigned long)pci_io_base + port;
89 return iowa_pci_find(vaddr, 0);
014da7ff
BH
90}
91
014da7ff 92
7cfb62a2
IK
93#define DEF_PCI_AC_RET(name, ret, at, al, space, aa) \
94static 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; \
014da7ff
BH
101}
102
7cfb62a2
IK
103#define DEF_PCI_AC_NORET(name, at, al, space, aa) \
104static 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; \
014da7ff
BH
113}
114
7cfb62a2 115#include <asm/io-defs.h>
014da7ff 116
7cfb62a2
IK
117#undef DEF_PCI_AC_RET
118#undef DEF_PCI_AC_NORET
014da7ff 119
7012255a 120static const struct ppc_pci_io __devinitconst iowa_pci_io = {
014da7ff 121
7cfb62a2
IK
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,
014da7ff 124
7cfb62a2 125#include <asm/io-defs.h>
014da7ff 126
7cfb62a2
IK
127#undef DEF_PCI_AC_RET
128#undef DEF_PCI_AC_NORET
014da7ff 129
7cfb62a2 130};
014da7ff 131
b36ac9e8 132static void __iomem *iowa_ioremap(phys_addr_t addr, unsigned long size,
1cdab55d 133 unsigned long flags, void *caller)
014da7ff 134{
7cfb62a2 135 struct iowa_bus *bus;
1cdab55d 136 void __iomem *res = __ioremap_caller(addr, size, flags, caller);
014da7ff
BH
137 int busno;
138
b36ac9e8 139 bus = iowa_pci_find(0, (unsigned long)addr);
014da7ff 140 if (bus != NULL) {
7cfb62a2 141 busno = bus - iowa_busses;
014da7ff
BH
142 PCI_SET_ADDR_TOKEN(res, busno + 1);
143 }
014da7ff
BH
144 return res;
145}
146
d1109b75
ME
147/* Enable IO workaround */
148static 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 */
7012255a 160void __devinit iowa_register_bus(struct pci_controller *phb,
7cfb62a2
IK
161 struct ppc_pci_io *ops,
162 int (*initfunc)(struct iowa_bus *, void *), void *data)
014da7ff 163{
7cfb62a2 164 struct iowa_bus *bus;
44ef3390 165 struct device_node *np = phb->dn;
014da7ff 166
d1109b75
ME
167 io_workaround_init();
168
7cfb62a2
IK
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);
014da7ff
BH
172 return;
173 }
174
7cfb62a2
IK
175 bus = &iowa_busses[iowa_bus_count];
176 bus->phb = phb;
177 bus->ops = ops;
014da7ff 178
7cfb62a2
IK
179 if (initfunc)
180 if ((*initfunc)(bus, data))
181 return;
014da7ff 182
7cfb62a2 183 iowa_bus_count++;
014da7ff 184
7cfb62a2 185 pr_debug("IOWA:[%d]Add bus, %s.\n", iowa_bus_count-1, np->full_name);
014da7ff
BH
186}
187