Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * Copyright (C) 2001 David J. Mckay (david.mckay@st.com) | |
3 | * Copyright (C) 2003, 2004 Paul Mundt | |
4 | * Copyright (C) 2004 Richard Curnow | |
5 | * | |
6 | * May be copied or modified under the terms of the GNU General Public | |
7 | * License. See linux/COPYING for more information. | |
8 | * | |
9 | * Support functions for the SH5 PCI hardware. | |
10 | */ | |
11 | ||
1da177e4 LT |
12 | #include <linux/kernel.h> |
13 | #include <linux/rwsem.h> | |
14 | #include <linux/smp.h> | |
1da177e4 LT |
15 | #include <linux/interrupt.h> |
16 | #include <linux/init.h> | |
17 | #include <linux/errno.h> | |
18 | #include <linux/pci.h> | |
19 | #include <linux/delay.h> | |
20 | #include <linux/types.h> | |
1da177e4 | 21 | #include <linux/irq.h> |
f15cbe6f | 22 | #include <cpu/irq.h> |
1da177e4 | 23 | #include <asm/io.h> |
b6d7b666 | 24 | #include "pci-sh5.h" |
1da177e4 | 25 | |
b6d7b666 PM |
26 | unsigned long pcicr_virt; |
27 | unsigned long PCI_IO_AREA; | |
1da177e4 LT |
28 | |
29 | /* Rounds a number UP to the nearest power of two. Used for | |
30 | * sizing the PCI window. | |
31 | */ | |
32 | static u32 __init r2p2(u32 num) | |
33 | { | |
34 | int i = 31; | |
35 | u32 tmp = num; | |
36 | ||
37 | if (num == 0) | |
38 | return 0; | |
39 | ||
40 | do { | |
41 | if (tmp & (1 << 31)) | |
42 | break; | |
43 | i--; | |
44 | tmp <<= 1; | |
45 | } while (i >= 0); | |
46 | ||
47 | tmp = 1 << i; | |
48 | /* If the original number isn't a power of 2, round it up */ | |
49 | if (tmp != num) | |
50 | tmp <<= 1; | |
51 | ||
52 | return tmp; | |
53 | } | |
54 | ||
b6d7b666 PM |
55 | static irqreturn_t pcish5_err_irq(int irq, void *dev_id) |
56 | { | |
57 | struct pt_regs *regs = get_irq_regs(); | |
58 | unsigned pci_int, pci_air, pci_cir, pci_aint; | |
59 | ||
60 | pci_int = SH5PCI_READ(INT); | |
61 | pci_cir = SH5PCI_READ(CIR); | |
62 | pci_air = SH5PCI_READ(AIR); | |
1da177e4 | 63 | |
b6d7b666 PM |
64 | if (pci_int) { |
65 | printk("PCI INTERRUPT (at %08llx)!\n", regs->pc); | |
66 | printk("PCI INT -> 0x%x\n", pci_int & 0xffff); | |
67 | printk("PCI AIR -> 0x%x\n", pci_air); | |
68 | printk("PCI CIR -> 0x%x\n", pci_cir); | |
69 | SH5PCI_WRITE(INT, ~0); | |
70 | } | |
71 | ||
72 | pci_aint = SH5PCI_READ(AINT); | |
73 | if (pci_aint) { | |
74 | printk("PCI ARB INTERRUPT!\n"); | |
75 | printk("PCI AINT -> 0x%x\n", pci_aint); | |
76 | printk("PCI AIR -> 0x%x\n", pci_air); | |
77 | printk("PCI CIR -> 0x%x\n", pci_cir); | |
78 | SH5PCI_WRITE(AINT, ~0); | |
79 | } | |
80 | ||
81 | return IRQ_HANDLED; | |
82 | } | |
83 | ||
84 | static irqreturn_t pcish5_serr_irq(int irq, void *dev_id) | |
85 | { | |
86 | printk("SERR IRQ\n"); | |
87 | ||
88 | return IRQ_NONE; | |
89 | } | |
90 | ||
b6c58b1d | 91 | static struct resource sh5_pci_resources[2]; |
48e4237d PM |
92 | |
93 | static struct pci_channel sh5pci_controller = { | |
94 | .pci_ops = &sh5_pci_ops, | |
b6c58b1d PM |
95 | .resources = sh5_pci_resources, |
96 | .nr_resources = ARRAY_SIZE(sh5_pci_resources), | |
48e4237d | 97 | .mem_offset = 0x00000000, |
48e4237d PM |
98 | .io_offset = 0x00000000, |
99 | }; | |
100 | ||
101 | static int __init sh5pci_init(void) | |
1da177e4 | 102 | { |
48e4237d PM |
103 | unsigned long memStart = __pa(memory_start); |
104 | unsigned long memSize = __pa(memory_end) - memStart; | |
1da177e4 LT |
105 | u32 lsr0; |
106 | u32 uval; | |
107 | ||
b6d7b666 | 108 | if (request_irq(IRQ_ERR, pcish5_err_irq, |
d11584a0 | 109 | 0, "PCI Error",NULL) < 0) { |
b6d7b666 PM |
110 | printk(KERN_ERR "PCISH5: Cannot hook PCI_PERR interrupt\n"); |
111 | return -EINVAL; | |
112 | } | |
113 | ||
114 | if (request_irq(IRQ_SERR, pcish5_serr_irq, | |
d11584a0 | 115 | 0, "PCI SERR interrupt", NULL) < 0) { |
b6d7b666 PM |
116 | printk(KERN_ERR "PCISH5: Cannot hook PCI_SERR interrupt\n"); |
117 | return -EINVAL; | |
118 | } | |
119 | ||
0fb849b9 | 120 | pcicr_virt = (unsigned long)ioremap_nocache(SH5PCI_ICR_BASE, 1024); |
1da177e4 LT |
121 | if (!pcicr_virt) { |
122 | panic("Unable to remap PCICR\n"); | |
123 | } | |
124 | ||
0fb849b9 | 125 | PCI_IO_AREA = (unsigned long)ioremap_nocache(SH5PCI_IO_BASE, 0x10000); |
b6d7b666 | 126 | if (!PCI_IO_AREA) { |
1da177e4 LT |
127 | panic("Unable to remap PCIIO\n"); |
128 | } | |
129 | ||
1da177e4 LT |
130 | /* Clear snoop registers */ |
131 | SH5PCI_WRITE(CSCR0, 0); | |
132 | SH5PCI_WRITE(CSCR1, 0); | |
133 | ||
1da177e4 LT |
134 | /* Switch off interrupts */ |
135 | SH5PCI_WRITE(INTM, 0); | |
136 | SH5PCI_WRITE(AINTM, 0); | |
137 | SH5PCI_WRITE(PINTM, 0); | |
138 | ||
139 | /* Set bus active, take it out of reset */ | |
140 | uval = SH5PCI_READ(CR); | |
141 | ||
142 | /* Set command Register */ | |
b6d7b666 PM |
143 | SH5PCI_WRITE(CR, uval | CR_LOCK_MASK | CR_CFINT| CR_FTO | CR_PFE | |
144 | CR_PFCS | CR_BMAM); | |
1da177e4 LT |
145 | |
146 | uval=SH5PCI_READ(CR); | |
1da177e4 LT |
147 | |
148 | /* Allow it to be a master */ | |
149 | /* NB - WE DISABLE I/O ACCESS to stop overlap */ | |
150 | /* set WAIT bit to enable stepping, an attempt to improve stability */ | |
151 | SH5PCI_WRITE_SHORT(CSR_CMD, | |
b6d7b666 PM |
152 | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER | |
153 | PCI_COMMAND_WAIT); | |
1da177e4 LT |
154 | |
155 | /* | |
156 | ** Set translation mapping memory in order to convert the address | |
157 | ** used for the main bus, to the PCI internal address. | |
158 | */ | |
159 | SH5PCI_WRITE(MBR,0x40000000); | |
160 | ||
161 | /* Always set the max size 512M */ | |
162 | SH5PCI_WRITE(MBMR, PCISH5_MEM_SIZCONV(512*1024*1024)); | |
163 | ||
164 | /* | |
165 | ** I/O addresses are mapped at internal PCI specific address | |
166 | ** as is described into the configuration bridge table. | |
167 | ** These are changed to 0, to allow cards that have legacy | |
168 | ** io such as vga to function correctly. We set the SH5 IOBAR to | |
169 | ** 256K, which is a bit big as we can only have 64K of address space | |
170 | */ | |
171 | ||
172 | SH5PCI_WRITE(IOBR,0x0); | |
173 | ||
1da177e4 LT |
174 | /* Set up a 256K window. Totally pointless waste of address space */ |
175 | SH5PCI_WRITE(IOBMR,0); | |
1da177e4 | 176 | |
b6d7b666 PM |
177 | /* The SH5 has a HUGE 256K I/O region, which breaks the PCI spec. |
178 | * Ideally, we would want to map the I/O region somewhere, but it | |
179 | * is so big this is not that easy! | |
1da177e4 LT |
180 | */ |
181 | SH5PCI_WRITE(CSR_IBAR0,~0); | |
182 | /* Set memory size value */ | |
183 | memSize = memory_end - memory_start; | |
184 | ||
b6d7b666 PM |
185 | /* Now we set up the mbars so the PCI bus can see the memory of |
186 | * the machine */ | |
187 | if (memSize < (1024 * 1024)) { | |
188 | printk(KERN_ERR "PCISH5: Ridiculous memory size of 0x%lx?\n", | |
189 | memSize); | |
1da177e4 LT |
190 | return -EINVAL; |
191 | } | |
192 | ||
193 | /* Set LSR 0 */ | |
b6d7b666 PM |
194 | lsr0 = (memSize > (512 * 1024 * 1024)) ? 0x1ff00001 : |
195 | ((r2p2(memSize) - 0x100000) | 0x1); | |
1da177e4 LT |
196 | SH5PCI_WRITE(LSR0, lsr0); |
197 | ||
1da177e4 LT |
198 | /* Set MBAR 0 */ |
199 | SH5PCI_WRITE(CSR_MBAR0, memory_start); | |
200 | SH5PCI_WRITE(LAR0, memory_start); | |
201 | ||
202 | SH5PCI_WRITE(CSR_MBAR1,0); | |
203 | SH5PCI_WRITE(LAR1,0); | |
204 | SH5PCI_WRITE(LSR1,0); | |
205 | ||
1da177e4 LT |
206 | /* Enable the PCI interrupts on the device */ |
207 | SH5PCI_WRITE(INTM, ~0); | |
208 | SH5PCI_WRITE(AINTM, ~0); | |
209 | SH5PCI_WRITE(PINTM, ~0); | |
210 | ||
b6c58b1d PM |
211 | sh5_pci_resources[0].start = PCI_IO_AREA; |
212 | sh5_pci_resources[0].end = PCI_IO_AREA + 0x10000; | |
1da177e4 | 213 | |
b6c58b1d PM |
214 | sh5_pci_resources[1].start = memStart; |
215 | sh5_pci_resources[1].end = memStart + memSize; | |
710fa3c8 | 216 | |
bcf39352 | 217 | return register_pci_controller(&sh5pci_controller); |
1da177e4 | 218 | } |
48e4237d | 219 | arch_initcall(sh5pci_init); |