aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIshizaki Kou <kou.ishizaki@toshiba.co.jp>2008-04-24 06:27:39 -0400
committerPaul Mackerras <paulus@samba.org>2008-04-24 07:08:14 -0400
commit884d04cd8d7bba3dc885227ad400f8aea5623cdc (patch)
tree937d9edd0e85bed8f268c442878d56027a9bdc69
parentad2c6987978d17b58204926e9be776955935f8b1 (diff)
[POWERPC] celleb: Add support for PCI Express
This adds support for PCI Express port on Celleb. I/O space of this PCI Express port is not mapped in memory space. So we use the io-workaround mechanism to make accesses indirect. Signed-off-by: Kou Ishizaki <kou.ishizaki@toshiba.co.jp> Acked-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/platforms/cell/Makefile1
-rw-r--r--arch/powerpc/platforms/cell/celleb_pci.c3
-rw-r--r--arch/powerpc/platforms/cell/celleb_pci.h1
-rw-r--r--arch/powerpc/platforms/cell/celleb_scc.h87
-rw-r--r--arch/powerpc/platforms/cell/celleb_scc_pciex.c547
5 files changed, 639 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/cell/Makefile b/arch/powerpc/platforms/cell/Makefile
index 2701bde28473..c2a7e4e5ddf9 100644
--- a/arch/powerpc/platforms/cell/Makefile
+++ b/arch/powerpc/platforms/cell/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_PCI_MSI) += axon_msi.o
33ifeq ($(CONFIG_PPC_CELLEB),y) 33ifeq ($(CONFIG_PPC_CELLEB),y)
34obj-y += celleb_setup.o \ 34obj-y += celleb_setup.o \
35 celleb_pci.o celleb_scc_epci.o \ 35 celleb_pci.o celleb_scc_epci.o \
36 celleb_scc_pciex.o \
36 celleb_scc_uhc.o \ 37 celleb_scc_uhc.o \
37 io-workarounds.o spider-pci.o \ 38 io-workarounds.o spider-pci.o \
38 beat.o beat_htab.o beat_hvCall.o \ 39 beat.o beat_htab.o beat_hvCall.o \
diff --git a/arch/powerpc/platforms/cell/celleb_pci.c b/arch/powerpc/platforms/cell/celleb_pci.c
index ff25e6088480..f39a3b2a1667 100644
--- a/arch/powerpc/platforms/cell/celleb_pci.c
+++ b/arch/powerpc/platforms/cell/celleb_pci.c
@@ -468,6 +468,9 @@ static struct of_device_id celleb_phb_match[] __initdata = {
468 .name = "epci", 468 .name = "epci",
469 .data = &celleb_epci_spec, 469 .data = &celleb_epci_spec,
470 }, { 470 }, {
471 .name = "pcie",
472 .data = &celleb_pciex_spec,
473 }, {
471 }, 474 },
472}; 475};
473 476
diff --git a/arch/powerpc/platforms/cell/celleb_pci.h b/arch/powerpc/platforms/cell/celleb_pci.h
index 79e59848cb62..4cba1523ec50 100644
--- a/arch/powerpc/platforms/cell/celleb_pci.h
+++ b/arch/powerpc/platforms/cell/celleb_pci.h
@@ -40,5 +40,6 @@ extern int celleb_setup_phb(struct pci_controller *);
40extern int celleb_pci_probe_mode(struct pci_bus *); 40extern int celleb_pci_probe_mode(struct pci_bus *);
41 41
42extern struct celleb_phb_spec celleb_epci_spec; 42extern struct celleb_phb_spec celleb_epci_spec;
43extern struct celleb_phb_spec celleb_pciex_spec;
43 44
44#endif /* _CELLEB_PCI_H */ 45#endif /* _CELLEB_PCI_H */
diff --git a/arch/powerpc/platforms/cell/celleb_scc.h b/arch/powerpc/platforms/cell/celleb_scc.h
index 6be1542a6e66..b596a711c348 100644
--- a/arch/powerpc/platforms/cell/celleb_scc.h
+++ b/arch/powerpc/platforms/cell/celleb_scc.h
@@ -125,6 +125,93 @@
125/* bits for SCC_EPCI_CNTOPT */ 125/* bits for SCC_EPCI_CNTOPT */
126#define SCC_EPCI_CNTOPT_O2PMB 0x00000002 126#define SCC_EPCI_CNTOPT_O2PMB 0x00000002
127 127
128/* SCC PCIEXC SMMIO registers */
129#define PEXCADRS 0x000
130#define PEXCWDATA 0x004
131#define PEXCRDATA 0x008
132#define PEXDADRS 0x010
133#define PEXDCMND 0x014
134#define PEXDWDATA 0x018
135#define PEXDRDATA 0x01c
136#define PEXREQID 0x020
137#define PEXTIDMAP 0x024
138#define PEXINTMASK 0x028
139#define PEXINTSTS 0x02c
140#define PEXAERRMASK 0x030
141#define PEXAERRSTS 0x034
142#define PEXPRERRMASK 0x040
143#define PEXPRERRSTS 0x044
144#define PEXPRERRID01 0x048
145#define PEXPRERRID23 0x04c
146#define PEXVDMASK 0x050
147#define PEXVDSTS 0x054
148#define PEXRCVCPLIDA 0x060
149#define PEXLENERRIDA 0x068
150#define PEXPHYPLLST 0x070
151#define PEXDMRDEN0 0x100
152#define PEXDMRDADR0 0x104
153#define PEXDMRDENX 0x110
154#define PEXDMRDADRX 0x114
155#define PEXECMODE 0xf00
156#define PEXMAEA(n) (0xf50 + (8 * n))
157#define PEXMAEC(n) (0xf54 + (8 * n))
158#define PEXCCRCTRL 0xff0
159
160/* SCC PCIEXC bits and shifts for PEXCADRS */
161#define PEXCADRS_BYTE_EN_SHIFT 20
162#define PEXCADRS_CMD_SHIFT 16
163#define PEXCADRS_CMD_READ (0xa << PEXCADRS_CMD_SHIFT)
164#define PEXCADRS_CMD_WRITE (0xb << PEXCADRS_CMD_SHIFT)
165
166/* SCC PCIEXC shifts for PEXDADRS */
167#define PEXDADRS_BUSNO_SHIFT 20
168#define PEXDADRS_DEVNO_SHIFT 15
169#define PEXDADRS_FUNCNO_SHIFT 12
170
171/* SCC PCIEXC bits and shifts for PEXDCMND */
172#define PEXDCMND_BYTE_EN_SHIFT 4
173#define PEXDCMND_IO_READ 0x2
174#define PEXDCMND_IO_WRITE 0x3
175#define PEXDCMND_CONFIG_READ 0xa
176#define PEXDCMND_CONFIG_WRITE 0xb
177
178/* SCC PCIEXC bits for PEXPHYPLLST */
179#define PEXPHYPLLST_PEXPHYAPLLST 0x00000001
180
181/* SCC PCIEXC bits for PEXECMODE */
182#define PEXECMODE_ALL_THROUGH 0x00000000
183#define PEXECMODE_ALL_8BIT 0x00550155
184#define PEXECMODE_ALL_16BIT 0x00aa02aa
185
186/* SCC PCIEXC bits for PEXCCRCTRL */
187#define PEXCCRCTRL_PEXIPCOREEN 0x00040000
188#define PEXCCRCTRL_PEXIPCONTEN 0x00020000
189#define PEXCCRCTRL_PEXPHYPLLEN 0x00010000
190#define PEXCCRCTRL_PCIEXCAOCKEN 0x00000100
191
192/* SCC PCIEXC port configuration registers */
193#define PEXTCERRCHK 0x21c
194#define PEXTAMAPB0 0x220
195#define PEXTAMAPL0 0x224
196#define PEXTAMAPB(n) (PEXTAMAPB0 + 8 * (n))
197#define PEXTAMAPL(n) (PEXTAMAPL0 + 8 * (n))
198#define PEXCHVC0P 0x500
199#define PEXCHVC0NP 0x504
200#define PEXCHVC0C 0x508
201#define PEXCDVC0P 0x50c
202#define PEXCDVC0NP 0x510
203#define PEXCDVC0C 0x514
204#define PEXCHVCXP 0x518
205#define PEXCHVCXNP 0x51c
206#define PEXCHVCXC 0x520
207#define PEXCDVCXP 0x524
208#define PEXCDVCXNP 0x528
209#define PEXCDVCXC 0x52c
210#define PEXCTTRG 0x530
211#define PEXTSCTRL 0x700
212#define PEXTSSTS 0x704
213#define PEXSKPCTRL 0x708
214
128/* UHC registers */ 215/* UHC registers */
129#define SCC_UHC_CKRCTRL 0xff0 216#define SCC_UHC_CKRCTRL 0xff0
130#define SCC_UHC_ECMODE 0xf00 217#define SCC_UHC_ECMODE 0xf00
diff --git a/arch/powerpc/platforms/cell/celleb_scc_pciex.c b/arch/powerpc/platforms/cell/celleb_scc_pciex.c
new file mode 100644
index 000000000000..ab24d94baab6
--- /dev/null
+++ b/arch/powerpc/platforms/cell/celleb_scc_pciex.c
@@ -0,0 +1,547 @@
1/*
2 * Support for Celleb PCI-Express.
3 *
4 * (C) Copyright 2007-2008 TOSHIBA CORPORATION
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21#undef DEBUG
22
23#include <linux/kernel.h>
24#include <linux/pci.h>
25#include <linux/string.h>
26#include <linux/init.h>
27#include <linux/bootmem.h>
28#include <linux/delay.h>
29#include <linux/interrupt.h>
30
31#include <asm/io.h>
32#include <asm/irq.h>
33#include <asm/iommu.h>
34#include <asm/byteorder.h>
35
36#include "celleb_scc.h"
37#include "celleb_pci.h"
38
39#define PEX_IN(base, off) in_be32((void *)(base) + (off))
40#define PEX_OUT(base, off, data) out_be32((void *)(base) + (off), (data))
41
42static void scc_pciex_io_flush(struct iowa_bus *bus)
43{
44 (void)PEX_IN(bus->phb->cfg_addr, PEXDMRDEN0);
45}
46
47/*
48 * Memory space access to device on PCIEX
49 */
50#define PCIEX_MMIO_READ(name, ret) \
51static ret scc_pciex_##name(const PCI_IO_ADDR addr) \
52{ \
53 ret val = __do_##name(addr); \
54 scc_pciex_io_flush(iowa_mem_find_bus(addr)); \
55 return val; \
56}
57
58#define PCIEX_MMIO_READ_STR(name) \
59static void scc_pciex_##name(const PCI_IO_ADDR addr, void *buf, \
60 unsigned long count) \
61{ \
62 __do_##name(addr, buf, count); \
63 scc_pciex_io_flush(iowa_mem_find_bus(addr)); \
64}
65
66PCIEX_MMIO_READ(readb, u8)
67PCIEX_MMIO_READ(readw, u16)
68PCIEX_MMIO_READ(readl, u32)
69PCIEX_MMIO_READ(readq, u64)
70PCIEX_MMIO_READ(readw_be, u16)
71PCIEX_MMIO_READ(readl_be, u32)
72PCIEX_MMIO_READ(readq_be, u64)
73PCIEX_MMIO_READ_STR(readsb)
74PCIEX_MMIO_READ_STR(readsw)
75PCIEX_MMIO_READ_STR(readsl)
76
77static void scc_pciex_memcpy_fromio(void *dest, const PCI_IO_ADDR src,
78 unsigned long n)
79{
80 __do_memcpy_fromio(dest, src, n);
81 scc_pciex_io_flush(iowa_mem_find_bus(src));
82}
83
84/*
85 * I/O port access to devices on PCIEX.
86 */
87
88static inline unsigned long get_bus_address(struct pci_controller *phb,
89 unsigned long port)
90{
91 return port - ((unsigned long)(phb->io_base_virt) - _IO_BASE);
92}
93
94static u32 scc_pciex_read_port(struct pci_controller *phb,
95 unsigned long port, int size)
96{
97 unsigned int byte_enable;
98 unsigned int cmd, shift;
99 unsigned long addr;
100 u32 data, ret;
101
102 BUG_ON(((port & 0x3ul) + size) > 4);
103
104 addr = get_bus_address(phb, port);
105 shift = addr & 0x3ul;
106 byte_enable = ((1 << size) - 1) << shift;
107 cmd = PEXDCMND_IO_READ | (byte_enable << PEXDCMND_BYTE_EN_SHIFT);
108 PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul));
109 PEX_OUT(phb->cfg_addr, PEXDCMND, cmd);
110 data = PEX_IN(phb->cfg_addr, PEXDRDATA);
111 ret = (data >> (shift * 8)) & (0xFFFFFFFF >> ((4 - size) * 8));
112
113 pr_debug("PCIEX:PIO READ:port=0x%lx, addr=0x%lx, size=%d, be=%x,"
114 " cmd=%x, data=%x, ret=%x\n", port, addr, size, byte_enable,
115 cmd, data, ret);
116
117 return ret;
118}
119
120static void scc_pciex_write_port(struct pci_controller *phb,
121 unsigned long port, int size, u32 val)
122{
123 unsigned int byte_enable;
124 unsigned int cmd, shift;
125 unsigned long addr;
126 u32 data;
127
128 BUG_ON(((port & 0x3ul) + size) > 4);
129
130 addr = get_bus_address(phb, port);
131 shift = addr & 0x3ul;
132 byte_enable = ((1 << size) - 1) << shift;
133 cmd = PEXDCMND_IO_WRITE | (byte_enable << PEXDCMND_BYTE_EN_SHIFT);
134 data = (val & (0xFFFFFFFF >> (4 - size) * 8)) << (shift * 8);
135 PEX_OUT(phb->cfg_addr, PEXDADRS, (addr & ~0x3ul));
136 PEX_OUT(phb->cfg_addr, PEXDCMND, cmd);
137 PEX_OUT(phb->cfg_addr, PEXDWDATA, data);
138
139 pr_debug("PCIEX:PIO WRITE:port=0x%lx, addr=%lx, size=%d, val=%x,"
140 " be=%x, cmd=%x, data=%x\n", port, addr, size, val,
141 byte_enable, cmd, data);
142}
143
144static u8 __scc_pciex_inb(struct pci_controller *phb, unsigned long port)
145{
146 return (u8)scc_pciex_read_port(phb, port, 1);
147}
148
149static u16 __scc_pciex_inw(struct pci_controller *phb, unsigned long port)
150{
151 u32 data;
152 if ((port & 0x3ul) < 3)
153 data = scc_pciex_read_port(phb, port, 2);
154 else {
155 u32 d1 = scc_pciex_read_port(phb, port, 1);
156 u32 d2 = scc_pciex_read_port(phb, port + 1, 1);
157 data = d1 | (d2 << 8);
158 }
159 return (u16)data;
160}
161
162static u32 __scc_pciex_inl(struct pci_controller *phb, unsigned long port)
163{
164 unsigned int mod = port & 0x3ul;
165 u32 data;
166 if (mod == 0)
167 data = scc_pciex_read_port(phb, port, 4);
168 else {
169 u32 d1 = scc_pciex_read_port(phb, port, 4 - mod);
170 u32 d2 = scc_pciex_read_port(phb, port + 1, mod);
171 data = d1 | (d2 << (mod * 8));
172 }
173 return data;
174}
175
176static void __scc_pciex_outb(struct pci_controller *phb,
177 u8 val, unsigned long port)
178{
179 scc_pciex_write_port(phb, port, 1, (u32)val);
180}
181
182static void __scc_pciex_outw(struct pci_controller *phb,
183 u16 val, unsigned long port)
184{
185 if ((port & 0x3ul) < 3)
186 scc_pciex_write_port(phb, port, 2, (u32)val);
187 else {
188 u32 d1 = val & 0x000000FF;
189 u32 d2 = (val & 0x0000FF00) >> 8;
190 scc_pciex_write_port(phb, port, 1, d1);
191 scc_pciex_write_port(phb, port + 1, 1, d2);
192 }
193}
194
195static void __scc_pciex_outl(struct pci_controller *phb,
196 u32 val, unsigned long port)
197{
198 unsigned int mod = port & 0x3ul;
199 if (mod == 0)
200 scc_pciex_write_port(phb, port, 4, val);
201 else {
202 u32 d1 = val & (0xFFFFFFFFul >> (mod * 8));
203 u32 d2 = val >> ((4 - mod) * 8);
204 scc_pciex_write_port(phb, port, 4 - mod, d1);
205 scc_pciex_write_port(phb, port + 1, mod, d2);
206 }
207}
208
209#define PCIEX_PIO_FUNC(size, name) \
210static u##size scc_pciex_in##name(unsigned long port) \
211{ \
212 struct iowa_bus *bus = iowa_pio_find_bus(port); \
213 u##size data = __scc_pciex_in##name(bus->phb, port); \
214 scc_pciex_io_flush(bus); \
215 return data; \
216} \
217static void scc_pciex_ins##name(unsigned long p, void *b, unsigned long c) \
218{ \
219 struct iowa_bus *bus = iowa_pio_find_bus(p); \
220 u##size *dst = b; \
221 for (; c != 0; c--, dst++) \
222 *dst = cpu_to_le##size(__scc_pciex_in##name(bus->phb, p)); \
223 scc_pciex_io_flush(bus); \
224} \
225static void scc_pciex_out##name(u##size val, unsigned long port) \
226{ \
227 struct iowa_bus *bus = iowa_pio_find_bus(port); \
228 __scc_pciex_out##name(bus->phb, val, port); \
229} \
230static void scc_pciex_outs##name(unsigned long p, const void *b, \
231 unsigned long c) \
232{ \
233 struct iowa_bus *bus = iowa_pio_find_bus(p); \
234 const u##size *src = b; \
235 for (; c != 0; c--, src++) \
236 __scc_pciex_out##name(bus->phb, le##size##_to_cpu(*src), p); \
237}
238#define cpu_to_le8(x) (x)
239#define le8_to_cpu(x) (x)
240PCIEX_PIO_FUNC(8, b)
241PCIEX_PIO_FUNC(16, w)
242PCIEX_PIO_FUNC(32, l)
243
244static struct ppc_pci_io scc_pciex_ops = {
245 .readb = scc_pciex_readb,
246 .readw = scc_pciex_readw,
247 .readl = scc_pciex_readl,
248 .readq = scc_pciex_readq,
249 .readw_be = scc_pciex_readw_be,
250 .readl_be = scc_pciex_readl_be,
251 .readq_be = scc_pciex_readq_be,
252 .readsb = scc_pciex_readsb,
253 .readsw = scc_pciex_readsw,
254 .readsl = scc_pciex_readsl,
255 .memcpy_fromio = scc_pciex_memcpy_fromio,
256 .inb = scc_pciex_inb,
257 .inw = scc_pciex_inw,
258 .inl = scc_pciex_inl,
259 .outb = scc_pciex_outb,
260 .outw = scc_pciex_outw,
261 .outl = scc_pciex_outl,
262 .insb = scc_pciex_insb,
263 .insw = scc_pciex_insw,
264 .insl = scc_pciex_insl,
265 .outsb = scc_pciex_outsb,
266 .outsw = scc_pciex_outsw,
267 .outsl = scc_pciex_outsl,
268};
269
270static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data)
271{
272 dma_addr_t dummy_page_da;
273 void *dummy_page_va;
274
275 dummy_page_va = kmalloc(PAGE_SIZE, GFP_KERNEL);
276 if (!dummy_page_va) {
277 pr_err("PCIEX:Alloc dummy_page_va failed\n");
278 return -1;
279 }
280
281 dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va,
282 PAGE_SIZE, DMA_FROM_DEVICE);
283 if (dma_mapping_error(dummy_page_da)) {
284 pr_err("PCIEX:Map dummy page failed.\n");
285 kfree(dummy_page_va);
286 return -1;
287 }
288
289 PEX_OUT(bus->phb->cfg_addr, PEXDMRDADR0, dummy_page_da);
290
291 return 0;
292}
293
294/*
295 * config space access
296 */
297#define MK_PEXDADRS(bus_no, dev_no, func_no, addr) \
298 ((uint32_t)(((addr) & ~0x3UL) | \
299 ((bus_no) << PEXDADRS_BUSNO_SHIFT) | \
300 ((dev_no) << PEXDADRS_DEVNO_SHIFT) | \
301 ((func_no) << PEXDADRS_FUNCNO_SHIFT)))
302
303#define MK_PEXDCMND_BYTE_EN(addr, size) \
304 ((((0x1 << (size))-1) << ((addr) & 0x3)) << PEXDCMND_BYTE_EN_SHIFT)
305#define MK_PEXDCMND(cmd, addr, size) ((cmd) | MK_PEXDCMND_BYTE_EN(addr, size))
306
307static uint32_t config_read_pciex_dev(unsigned int *base,
308 uint64_t bus_no, uint64_t dev_no, uint64_t func_no,
309 uint64_t off, uint64_t size)
310{
311 uint32_t ret;
312 uint32_t addr, cmd;
313
314 addr = MK_PEXDADRS(bus_no, dev_no, func_no, off);
315 cmd = MK_PEXDCMND(PEXDCMND_CONFIG_READ, off, size);
316 PEX_OUT(base, PEXDADRS, addr);
317 PEX_OUT(base, PEXDCMND, cmd);
318 ret = (PEX_IN(base, PEXDRDATA)
319 >> ((off & (4-size)) * 8)) & ((0x1 << (size * 8)) - 1);
320 return ret;
321}
322
323static void config_write_pciex_dev(unsigned int *base, uint64_t bus_no,
324 uint64_t dev_no, uint64_t func_no, uint64_t off, uint64_t size,
325 uint32_t data)
326{
327 uint32_t addr, cmd;
328
329 addr = MK_PEXDADRS(bus_no, dev_no, func_no, off);
330 cmd = MK_PEXDCMND(PEXDCMND_CONFIG_WRITE, off, size);
331 PEX_OUT(base, PEXDADRS, addr);
332 PEX_OUT(base, PEXDCMND, cmd);
333 PEX_OUT(base, PEXDWDATA,
334 (data & ((0x1 << (size * 8)) - 1)) << ((off & (4-size)) * 8));
335}
336
337#define MK_PEXCADRS_BYTE_EN(off, len) \
338 ((((0x1 << (len)) - 1) << ((off) & 0x3)) << PEXCADRS_BYTE_EN_SHIFT)
339#define MK_PEXCADRS(cmd, addr, size) \
340 ((cmd) | MK_PEXCADRS_BYTE_EN(addr, size) | ((addr) & ~0x3))
341static uint32_t config_read_pciex_rc(unsigned int *base,
342 uint32_t where, uint32_t size)
343{
344 PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_READ, where, size));
345 return (PEX_IN(base, PEXCRDATA)
346 >> ((where & (4 - size)) * 8)) & ((0x1 << (size * 8)) - 1);
347}
348
349static void config_write_pciex_rc(unsigned int *base, uint32_t where,
350 uint32_t size, uint32_t val)
351{
352 uint32_t data;
353
354 data = (val & ((0x1 << (size * 8)) - 1)) << ((where & (4 - size)) * 8);
355 PEX_OUT(base, PEXCADRS, MK_PEXCADRS(PEXCADRS_CMD_WRITE, where, size));
356 PEX_OUT(base, PEXCWDATA, data);
357}
358
359/* Interfaces */
360/* Note: Work-around
361 * On SCC PCIEXC, one device is seen on all 32 dev_no.
362 * As SCC PCIEXC can have only one device on the bus, we look only one dev_no.
363 * (dev_no = 1)
364 */
365static int scc_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
366 int where, int size, unsigned int *val)
367{
368 struct device_node *dn;
369 struct pci_controller *phb;
370
371 dn = bus->sysdata;
372 phb = pci_find_hose_for_OF_device(dn);
373
374 if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1) {
375 *val = ~0;
376 return PCIBIOS_DEVICE_NOT_FOUND;
377 }
378
379 if (bus->number == 0 && PCI_SLOT(devfn) == 0)
380 *val = config_read_pciex_rc(phb->cfg_addr, where, size);
381 else
382 *val = config_read_pciex_dev(phb->cfg_addr, bus->number,
383 PCI_SLOT(devfn), PCI_FUNC(devfn), where, size);
384
385 return PCIBIOS_SUCCESSFUL;
386}
387
388static int scc_pciex_write_config(struct pci_bus *bus, unsigned int devfn,
389 int where, int size, unsigned int val)
390{
391 struct device_node *dn;
392 struct pci_controller *phb;
393
394 dn = bus->sysdata;
395 phb = pci_find_hose_for_OF_device(dn);
396
397 if (bus->number == phb->first_busno && PCI_SLOT(devfn) != 1)
398 return PCIBIOS_DEVICE_NOT_FOUND;
399
400 if (bus->number == 0 && PCI_SLOT(devfn) == 0)
401 config_write_pciex_rc(phb->cfg_addr, where, size, val);
402 else
403 config_write_pciex_dev(phb->cfg_addr, bus->number,
404 PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, val);
405 return PCIBIOS_SUCCESSFUL;
406}
407
408static struct pci_ops scc_pciex_pci_ops = {
409 scc_pciex_read_config,
410 scc_pciex_write_config,
411};
412
413static void pciex_clear_intr_all(unsigned int *base)
414{
415 PEX_OUT(base, PEXAERRSTS, 0xffffffff);
416 PEX_OUT(base, PEXPRERRSTS, 0xffffffff);
417 PEX_OUT(base, PEXINTSTS, 0xffffffff);
418}
419
420#if 0
421static void pciex_disable_intr_all(unsigned int *base)
422{
423 PEX_OUT(base, PEXINTMASK, 0x0);
424 PEX_OUT(base, PEXAERRMASK, 0x0);
425 PEX_OUT(base, PEXPRERRMASK, 0x0);
426 PEX_OUT(base, PEXVDMASK, 0x0);
427}
428#endif
429
430static void pciex_enable_intr_all(unsigned int *base)
431{
432 PEX_OUT(base, PEXINTMASK, 0x0000e7f1);
433 PEX_OUT(base, PEXAERRMASK, 0x03ff01ff);
434 PEX_OUT(base, PEXPRERRMASK, 0x0001010f);
435 PEX_OUT(base, PEXVDMASK, 0x00000001);
436}
437
438static void pciex_check_status(unsigned int *base)
439{
440 uint32_t err = 0;
441 uint32_t intsts, aerr, prerr, rcvcp, lenerr;
442 uint32_t maea, maec;
443
444 intsts = PEX_IN(base, PEXINTSTS);
445 aerr = PEX_IN(base, PEXAERRSTS);
446 prerr = PEX_IN(base, PEXPRERRSTS);
447 rcvcp = PEX_IN(base, PEXRCVCPLIDA);
448 lenerr = PEX_IN(base, PEXLENERRIDA);
449
450 if (intsts || aerr || prerr || rcvcp || lenerr)
451 err = 1;
452
453 pr_info("PCEXC interrupt!!\n");
454 pr_info("PEXINTSTS :0x%08x\n", intsts);
455 pr_info("PEXAERRSTS :0x%08x\n", aerr);
456 pr_info("PEXPRERRSTS :0x%08x\n", prerr);
457 pr_info("PEXRCVCPLIDA :0x%08x\n", rcvcp);
458 pr_info("PEXLENERRIDA :0x%08x\n", lenerr);
459
460 /* print detail of Protection Error */
461 if (intsts & 0x00004000) {
462 uint32_t i, n;
463 for (i = 0; i < 4; i++) {
464 n = 1 << i;
465 if (prerr & n) {
466 maea = PEX_IN(base, PEXMAEA(i));
467 maec = PEX_IN(base, PEXMAEC(i));
468 pr_info("PEXMAEC%d :0x%08x\n", i, maec);
469 pr_info("PEXMAEA%d :0x%08x\n", i, maea);
470 }
471 }
472 }
473
474 if (err)
475 pciex_clear_intr_all(base);
476}
477
478static irqreturn_t pciex_handle_internal_irq(int irq, void *dev_id)
479{
480 struct pci_controller *phb = dev_id;
481
482 pr_debug("PCIEX:pciex_handle_internal_irq(irq=%d)\n", irq);
483
484 BUG_ON(phb->cfg_addr == NULL);
485
486 pciex_check_status(phb->cfg_addr);
487
488 return IRQ_HANDLED;
489}
490
491static __init int celleb_setup_pciex(struct device_node *node,
492 struct pci_controller *phb)
493{
494 struct resource r;
495 struct of_irq oirq;
496 int virq;
497
498 /* SMMIO registers; used inside this file */
499 if (of_address_to_resource(node, 0, &r)) {
500 pr_err("PCIEXC:Failed to get config resource.\n");
501 return 1;
502 }
503 phb->cfg_addr = ioremap(r.start, r.end - r.start + 1);
504 if (!phb->cfg_addr) {
505 pr_err("PCIEXC:Failed to remap SMMIO region.\n");
506 return 1;
507 }
508
509 /* Not use cfg_data, cmd and data regs are near address reg */
510 phb->cfg_data = NULL;
511
512 /* set pci_ops */
513 phb->ops = &scc_pciex_pci_ops;
514
515 /* internal interrupt handler */
516 if (of_irq_map_one(node, 1, &oirq)) {
517 pr_err("PCIEXC:Failed to map irq\n");
518 goto error;
519 }
520 virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
521 oirq.size);
522 if (request_irq(virq, pciex_handle_internal_irq,
523 IRQF_DISABLED, "pciex", (void *)phb)) {
524 pr_err("PCIEXC:Failed to request irq\n");
525 goto error;
526 }
527
528 /* enable all interrupts */
529 pciex_clear_intr_all(phb->cfg_addr);
530 pciex_enable_intr_all(phb->cfg_addr);
531 /* MSI: TBD */
532
533 return 0;
534
535error:
536 phb->cfg_data = NULL;
537 if (phb->cfg_addr)
538 iounmap(phb->cfg_addr);
539 phb->cfg_addr = NULL;
540 return 1;
541}
542
543struct celleb_phb_spec celleb_pciex_spec __initdata = {
544 .setup = celleb_setup_pciex,
545 .ops = &scc_pciex_ops,
546 .iowa_init = &scc_pciex_iowa_init,
547};