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.
25 #include <linux/of_device.h>
26 #include <linux/irq.h>
30 #include <asm/machdep.h>
31 #include <asm/pci-bridge.h>
32 #include <asm/ppc-pci.h>
36 #define MAX_CELLEB_PCI_BUS 4
38 void *celleb_dummy_page_va
;
40 static struct celleb_pci_bus
{
41 struct pci_controller
*phb
;
42 void (*dummy_read
)(struct pci_controller
*);
43 } celleb_pci_busses
[MAX_CELLEB_PCI_BUS
];
45 static int celleb_pci_count
= 0;
47 static struct celleb_pci_bus
*celleb_pci_find(unsigned long vaddr
,
53 for (i
= 0; i
< celleb_pci_count
; i
++) {
54 struct celleb_pci_bus
*bus
= &celleb_pci_busses
[i
];
55 struct pci_controller
*phb
= bus
->phb
;
57 for (j
= 0; j
< 3; j
++) {
58 res
= &phb
->mem_resources
[j
];
59 if (paddr
>= res
->start
&& paddr
<= res
->end
)
62 res
= &phb
->io_resource
;
63 if (vaddr
&& vaddr
>= res
->start
&& vaddr
<= res
->end
)
69 static void celleb_io_flush(const PCI_IO_ADDR addr
)
71 struct celleb_pci_bus
*bus
;
74 token
= PCI_GET_ADDR_TOKEN(addr
);
76 if (token
&& token
<= celleb_pci_count
)
77 bus
= &celleb_pci_busses
[token
- 1];
79 unsigned long vaddr
, paddr
;
82 vaddr
= (unsigned long)PCI_FIX_ADDR(addr
);
83 if (vaddr
< PHB_IO_BASE
|| vaddr
>= PHB_IO_END
)
86 ptep
= find_linux_pte(init_mm
.pgd
, vaddr
);
90 paddr
= pte_pfn(*ptep
) << PAGE_SHIFT
;
91 bus
= celleb_pci_find(vaddr
, paddr
);
98 bus
->dummy_read(bus
->phb
);
101 static u8
celleb_readb(const PCI_IO_ADDR addr
)
104 val
= __do_readb(addr
);
105 celleb_io_flush(addr
);
109 static u16
celleb_readw(const PCI_IO_ADDR addr
)
112 val
= __do_readw(addr
);
113 celleb_io_flush(addr
);
117 static u32
celleb_readl(const PCI_IO_ADDR addr
)
120 val
= __do_readl(addr
);
121 celleb_io_flush(addr
);
125 static u64
celleb_readq(const PCI_IO_ADDR addr
)
128 val
= __do_readq(addr
);
129 celleb_io_flush(addr
);
133 static u16
celleb_readw_be(const PCI_IO_ADDR addr
)
136 val
= __do_readw_be(addr
);
137 celleb_io_flush(addr
);
141 static u32
celleb_readl_be(const PCI_IO_ADDR addr
)
144 val
= __do_readl_be(addr
);
145 celleb_io_flush(addr
);
149 static u64
celleb_readq_be(const PCI_IO_ADDR addr
)
152 val
= __do_readq_be(addr
);
153 celleb_io_flush(addr
);
157 static void celleb_readsb(const PCI_IO_ADDR addr
,
158 void *buf
, unsigned long count
)
160 __do_readsb(addr
, buf
, count
);
161 celleb_io_flush(addr
);
164 static void celleb_readsw(const PCI_IO_ADDR addr
,
165 void *buf
, unsigned long count
)
167 __do_readsw(addr
, buf
, count
);
168 celleb_io_flush(addr
);
171 static void celleb_readsl(const PCI_IO_ADDR addr
,
172 void *buf
, unsigned long count
)
174 __do_readsl(addr
, buf
, count
);
175 celleb_io_flush(addr
);
178 static void celleb_memcpy_fromio(void *dest
,
179 const PCI_IO_ADDR src
,
182 __do_memcpy_fromio(dest
, src
, n
);
183 celleb_io_flush(src
);
186 static void __iomem
*celleb_ioremap(unsigned long addr
,
190 struct celleb_pci_bus
*bus
;
191 void __iomem
*res
= __ioremap(addr
, size
, flags
);
194 bus
= celleb_pci_find(0, addr
);
196 busno
= bus
- celleb_pci_busses
;
197 PCI_SET_ADDR_TOKEN(res
, busno
+ 1);
202 static void celleb_iounmap(volatile void __iomem
*addr
)
204 return __iounmap(PCI_FIX_ADDR(addr
));
207 static struct ppc_pci_io celleb_pci_io __initdata
= {
208 .readb
= celleb_readb
,
209 .readw
= celleb_readw
,
210 .readl
= celleb_readl
,
211 .readq
= celleb_readq
,
212 .readw_be
= celleb_readw_be
,
213 .readl_be
= celleb_readl_be
,
214 .readq_be
= celleb_readq_be
,
215 .readsb
= celleb_readsb
,
216 .readsw
= celleb_readsw
,
217 .readsl
= celleb_readsl
,
218 .memcpy_fromio
= celleb_memcpy_fromio
,
221 void __init
celleb_pci_add_one(struct pci_controller
*phb
,
222 void (*dummy_read
)(struct pci_controller
*))
224 struct celleb_pci_bus
*bus
= &celleb_pci_busses
[celleb_pci_count
];
225 struct device_node
*np
= phb
->arch_data
;
227 if (celleb_pci_count
>= MAX_CELLEB_PCI_BUS
) {
228 printk(KERN_ERR
"Too many pci bridges, workarounds"
229 " disabled for %s\n", np
->full_name
);
236 bus
->dummy_read
= dummy_read
;
239 static struct of_device_id celleb_pci_workaround_match
[] __initdata
= {
241 .name
= "pci-pseudo",
242 .data
= fake_pci_workaround_init
,
245 .data
= epci_workaround_init
,
250 int __init
celleb_pci_workaround_init(void)
252 struct pci_controller
*phb
;
253 struct device_node
*node
;
254 const struct of_device_id
*match
;
255 void (*init_func
)(struct pci_controller
*);
257 celleb_dummy_page_va
= kmalloc(PAGE_SIZE
, GFP_KERNEL
);
258 if (!celleb_dummy_page_va
) {
259 printk(KERN_ERR
"Celleb: dummy read disabled."
260 "Alloc celleb_dummy_page_va failed\n");
264 list_for_each_entry(phb
, &hose_list
, list_node
) {
265 node
= phb
->arch_data
;
266 match
= of_match_node(celleb_pci_workaround_match
, node
);
269 init_func
= match
->data
;
274 ppc_pci_io
= celleb_pci_io
;
275 ppc_md
.ioremap
= celleb_ioremap
;
276 ppc_md
.iounmap
= celleb_iounmap
;