2 * Support for Celleb io workarounds
4 * (C) Copyright 2006-2007 TOSHIBA CORPORATION
6 * This file is based to arch/powerpc/platform/cell/io-workarounds.c
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.
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.
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 #include <linux/of_device.h>
27 #include <linux/irq.h>
31 #include <asm/machdep.h>
32 #include <asm/pci-bridge.h>
33 #include <asm/ppc-pci.h>
37 #define MAX_CELLEB_PCI_BUS 4
39 void *celleb_dummy_page_va
;
41 static struct celleb_pci_bus
{
42 struct pci_controller
*phb
;
43 void (*dummy_read
)(struct pci_controller
*);
44 } celleb_pci_busses
[MAX_CELLEB_PCI_BUS
];
46 static int celleb_pci_count
= 0;
48 static struct celleb_pci_bus
*celleb_pci_find(unsigned long vaddr
,
54 for (i
= 0; i
< celleb_pci_count
; i
++) {
55 struct celleb_pci_bus
*bus
= &celleb_pci_busses
[i
];
56 struct pci_controller
*phb
= bus
->phb
;
58 for (j
= 0; j
< 3; j
++) {
59 res
= &phb
->mem_resources
[j
];
60 if (paddr
>= res
->start
&& paddr
<= res
->end
)
63 res
= &phb
->io_resource
;
64 if (vaddr
&& vaddr
>= res
->start
&& vaddr
<= res
->end
)
70 static void celleb_io_flush(const PCI_IO_ADDR addr
)
72 struct celleb_pci_bus
*bus
;
75 token
= PCI_GET_ADDR_TOKEN(addr
);
77 if (token
&& token
<= celleb_pci_count
)
78 bus
= &celleb_pci_busses
[token
- 1];
80 unsigned long vaddr
, paddr
;
83 vaddr
= (unsigned long)PCI_FIX_ADDR(addr
);
84 if (vaddr
< PHB_IO_BASE
|| vaddr
>= PHB_IO_END
)
87 ptep
= find_linux_pte(init_mm
.pgd
, vaddr
);
91 paddr
= pte_pfn(*ptep
) << PAGE_SHIFT
;
92 bus
= celleb_pci_find(vaddr
, paddr
);
99 bus
->dummy_read(bus
->phb
);
102 static u8
celleb_readb(const PCI_IO_ADDR addr
)
105 val
= __do_readb(addr
);
106 celleb_io_flush(addr
);
110 static u16
celleb_readw(const PCI_IO_ADDR addr
)
113 val
= __do_readw(addr
);
114 celleb_io_flush(addr
);
118 static u32
celleb_readl(const PCI_IO_ADDR addr
)
121 val
= __do_readl(addr
);
122 celleb_io_flush(addr
);
126 static u64
celleb_readq(const PCI_IO_ADDR addr
)
129 val
= __do_readq(addr
);
130 celleb_io_flush(addr
);
134 static u16
celleb_readw_be(const PCI_IO_ADDR addr
)
137 val
= __do_readw_be(addr
);
138 celleb_io_flush(addr
);
142 static u32
celleb_readl_be(const PCI_IO_ADDR addr
)
145 val
= __do_readl_be(addr
);
146 celleb_io_flush(addr
);
150 static u64
celleb_readq_be(const PCI_IO_ADDR addr
)
153 val
= __do_readq_be(addr
);
154 celleb_io_flush(addr
);
158 static void celleb_readsb(const PCI_IO_ADDR addr
,
159 void *buf
, unsigned long count
)
161 __do_readsb(addr
, buf
, count
);
162 celleb_io_flush(addr
);
165 static void celleb_readsw(const PCI_IO_ADDR addr
,
166 void *buf
, unsigned long count
)
168 __do_readsw(addr
, buf
, count
);
169 celleb_io_flush(addr
);
172 static void celleb_readsl(const PCI_IO_ADDR addr
,
173 void *buf
, unsigned long count
)
175 __do_readsl(addr
, buf
, count
);
176 celleb_io_flush(addr
);
179 static void celleb_memcpy_fromio(void *dest
,
180 const PCI_IO_ADDR src
,
183 __do_memcpy_fromio(dest
, src
, n
);
184 celleb_io_flush(src
);
187 static void __iomem
*celleb_ioremap(unsigned long addr
,
191 struct celleb_pci_bus
*bus
;
192 void __iomem
*res
= __ioremap(addr
, size
, flags
);
195 bus
= celleb_pci_find(0, addr
);
197 busno
= bus
- celleb_pci_busses
;
198 PCI_SET_ADDR_TOKEN(res
, busno
+ 1);
203 static void celleb_iounmap(volatile void __iomem
*addr
)
205 return __iounmap(PCI_FIX_ADDR(addr
));
208 static struct ppc_pci_io celleb_pci_io __initdata
= {
209 .readb
= celleb_readb
,
210 .readw
= celleb_readw
,
211 .readl
= celleb_readl
,
212 .readq
= celleb_readq
,
213 .readw_be
= celleb_readw_be
,
214 .readl_be
= celleb_readl_be
,
215 .readq_be
= celleb_readq_be
,
216 .readsb
= celleb_readsb
,
217 .readsw
= celleb_readsw
,
218 .readsl
= celleb_readsl
,
219 .memcpy_fromio
= celleb_memcpy_fromio
,
222 void __init
celleb_pci_add_one(struct pci_controller
*phb
,
223 void (*dummy_read
)(struct pci_controller
*))
225 struct celleb_pci_bus
*bus
= &celleb_pci_busses
[celleb_pci_count
];
226 struct device_node
*np
= phb
->dn
;
228 if (celleb_pci_count
>= MAX_CELLEB_PCI_BUS
) {
229 printk(KERN_ERR
"Too many pci bridges, workarounds"
230 " disabled for %s\n", np
->full_name
);
237 bus
->dummy_read
= dummy_read
;
240 static struct of_device_id celleb_pci_workaround_match
[] __initdata
= {
242 .name
= "pci-pseudo",
243 .data
= fake_pci_workaround_init
,
246 .data
= epci_workaround_init
,
251 int __init
celleb_pci_workaround_init(void)
253 struct pci_controller
*phb
;
254 struct device_node
*node
;
255 const struct of_device_id
*match
;
256 void (*init_func
)(struct pci_controller
*);
258 celleb_dummy_page_va
= kmalloc(PAGE_SIZE
, GFP_KERNEL
);
259 if (!celleb_dummy_page_va
) {
260 printk(KERN_ERR
"Celleb: dummy read disabled. "
261 "Alloc celleb_dummy_page_va failed\n");
265 list_for_each_entry(phb
, &hose_list
, list_node
) {
267 match
= of_match_node(celleb_pci_workaround_match
, node
);
270 init_func
= match
->data
;
275 ppc_pci_io
= celleb_pci_io
;
276 ppc_md
.ioremap
= celleb_ioremap
;
277 ppc_md
.iounmap
= celleb_iounmap
;