diff options
author | Paul Mundt <lethal@linux-sh.org> | 2006-09-27 02:59:17 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2006-09-27 02:59:17 -0400 |
commit | 5283ecb5ccbdb90d49fce6488d3944bba63a591c (patch) | |
tree | a58e20bd532fa5f933d099bb7b5dd0637b581d33 /arch/sh/drivers/pci/pci-sh7780.c | |
parent | d7c30c682a278abe1a52db83f69efec1a9d8f8c2 (diff) |
sh: Add support for R7780RP and R7780MP boards.
This adds support for the Renesas SH7780 development boards,
R7780RP and R7780MP.
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/drivers/pci/pci-sh7780.c')
-rw-r--r-- | arch/sh/drivers/pci/pci-sh7780.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/arch/sh/drivers/pci/pci-sh7780.c b/arch/sh/drivers/pci/pci-sh7780.c new file mode 100644 index 000000000000..e09721330ac2 --- /dev/null +++ b/arch/sh/drivers/pci/pci-sh7780.c | |||
@@ -0,0 +1,341 @@ | |||
1 | /* | ||
2 | * Low-Level PCI Support for the SH7780 | ||
3 | * | ||
4 | * Dustin McIntire (dustin@sensoria.com) | ||
5 | * Derived from arch/i386/kernel/pci-*.c which bore the message: | ||
6 | * (c) 1999--2000 Martin Mares <mj@ucw.cz> | ||
7 | * | ||
8 | * Ported to the new API by Paul Mundt <lethal@linux-sh.org> | ||
9 | * With cleanup by Paul van Gool <pvangool@mimotech.com> | ||
10 | * | ||
11 | * May be copied or modified under the terms of the GNU General Public | ||
12 | * License. See linux/COPYING for more information. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #undef DEBUG | ||
17 | |||
18 | #include <linux/config.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/ioport.h> | ||
25 | #include <linux/errno.h> | ||
26 | #include <linux/irq.h> | ||
27 | #include <linux/delay.h> | ||
28 | |||
29 | #include <asm/machvec.h> | ||
30 | #include <asm/io.h> | ||
31 | #include "pci-sh7780.h" | ||
32 | |||
33 | static unsigned int pci_probe = PCI_PROBE_CONF1; | ||
34 | extern int pci_fixup_pcic(void); | ||
35 | |||
36 | /* | ||
37 | * Direct access to PCI hardware... | ||
38 | */ | ||
39 | |||
40 | #define CONFIG_CMD(bus, devfn, where) (0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3)) | ||
41 | |||
42 | /* | ||
43 | * Functions for accessing PCI configuration space with type 1 accesses | ||
44 | */ | ||
45 | static int sh7780_pci_read(struct pci_bus *bus, unsigned int devfn, | ||
46 | int where, int size, u32 *val) | ||
47 | { | ||
48 | unsigned long flags; | ||
49 | u32 data; | ||
50 | |||
51 | /* | ||
52 | * PCIPDR may only be accessed as 32 bit words, | ||
53 | * so we must do byte alignment by hand | ||
54 | */ | ||
55 | local_irq_save(flags); | ||
56 | outl(CONFIG_CMD(bus, devfn, where), PCI_REG(SH7780_PCIPAR)); | ||
57 | data = inl(PCI_REG(SH7780_PCIPDR)); | ||
58 | local_irq_restore(flags); | ||
59 | |||
60 | switch (size) { | ||
61 | case 1: | ||
62 | *val = (data >> ((where & 3) << 3)) & 0xff; | ||
63 | break; | ||
64 | case 2: | ||
65 | *val = (data >> ((where & 2) << 3)) & 0xffff; | ||
66 | break; | ||
67 | case 4: | ||
68 | *val = data; | ||
69 | break; | ||
70 | default: | ||
71 | return PCIBIOS_FUNC_NOT_SUPPORTED; | ||
72 | } | ||
73 | |||
74 | return PCIBIOS_SUCCESSFUL; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Since SH7780 only does 32bit access we'll have to do a read, | ||
79 | * mask,write operation. | ||
80 | * We'll allow an odd byte offset, though it should be illegal. | ||
81 | */ | ||
82 | static int sh7780_pci_write(struct pci_bus *bus, unsigned int devfn, | ||
83 | int where, int size, u32 val) | ||
84 | { | ||
85 | unsigned long flags; | ||
86 | int shift; | ||
87 | u32 data; | ||
88 | |||
89 | local_irq_save(flags); | ||
90 | outl(CONFIG_CMD(bus, devfn, where), PCI_REG(SH7780_PCIPAR)); | ||
91 | data = inl(PCI_REG(SH7780_PCIPDR)); | ||
92 | local_irq_restore(flags); | ||
93 | |||
94 | switch (size) { | ||
95 | case 1: | ||
96 | shift = (where & 3) << 3; | ||
97 | data &= ~(0xff << shift); | ||
98 | data |= ((val & 0xff) << shift); | ||
99 | break; | ||
100 | case 2: | ||
101 | shift = (where & 2) << 3; | ||
102 | data &= ~(0xffff << shift); | ||
103 | data |= ((val & 0xffff) << shift); | ||
104 | break; | ||
105 | case 4: | ||
106 | data = val; | ||
107 | break; | ||
108 | default: | ||
109 | return PCIBIOS_FUNC_NOT_SUPPORTED; | ||
110 | } | ||
111 | |||
112 | outl(data, PCI_REG(SH7780_PCIPDR)); | ||
113 | |||
114 | return PCIBIOS_SUCCESSFUL; | ||
115 | } | ||
116 | |||
117 | #undef CONFIG_CMD | ||
118 | |||
119 | struct pci_ops sh7780_pci_ops = { | ||
120 | .read = sh7780_pci_read, | ||
121 | .write = sh7780_pci_write, | ||
122 | }; | ||
123 | |||
124 | static int __init pci_check_direct(void) | ||
125 | { | ||
126 | unsigned int tmp, id; | ||
127 | |||
128 | outl(0x00000001, SH7780_PCI_VCR2); /* Enable PCIC */ | ||
129 | |||
130 | /* check for SH7780/SH7780R hardware */ | ||
131 | id = inl(PCI_REG(SH7780_PCIVID)); | ||
132 | if ((id != ((SH7780_DEVICE_ID << 16) | SH7780_VENDOR_ID)) && | ||
133 | (id != ((SH7781_DEVICE_ID << 16) | SH7780_VENDOR_ID))) { | ||
134 | printk(KERN_ERR "PCI: This is not an SH7780 (%x)\n", id); | ||
135 | return -ENODEV; | ||
136 | } | ||
137 | |||
138 | /* | ||
139 | * Check if configuration works. | ||
140 | */ | ||
141 | if (pci_probe & PCI_PROBE_CONF1) { | ||
142 | tmp = inl(PCI_REG(SH7780_PCIPAR)); | ||
143 | outl(0x80000000, PCI_REG(SH7780_PCIPAR)); | ||
144 | if (inl(PCI_REG(SH7780_PCIPAR)) == 0x80000000) { | ||
145 | outl(tmp, PCI_REG(SH7780_PCIPAR)); | ||
146 | printk(KERN_INFO "PCI: Using configuration type 1\n"); | ||
147 | request_region(PCI_REG(SH7780_PCIPAR), 8, "PCI conf1"); | ||
148 | return 0; | ||
149 | } | ||
150 | outl(tmp, PCI_REG(SH7780_PCIPAR)); | ||
151 | } | ||
152 | |||
153 | pr_debug("PCI: pci_check_direct failed\n"); | ||
154 | return -EINVAL; | ||
155 | } | ||
156 | |||
157 | /***************************************************************************************/ | ||
158 | |||
159 | /* | ||
160 | * Handle bus scanning and fixups .... | ||
161 | */ | ||
162 | |||
163 | static void __init pci_fixup_ide_bases(struct pci_dev *d) | ||
164 | { | ||
165 | int i; | ||
166 | |||
167 | /* | ||
168 | * PCI IDE controllers use non-standard I/O port decoding, respect it. | ||
169 | */ | ||
170 | if ((d->class >> 8) != PCI_CLASS_STORAGE_IDE) | ||
171 | return; | ||
172 | pr_debug("PCI: IDE base address fixup for %s\n", pci_name(d)); | ||
173 | for(i=0; i<4; i++) { | ||
174 | struct resource *r = &d->resource[i]; | ||
175 | if ((r->start & ~0x80) == 0x374) { | ||
176 | r->start |= 2; | ||
177 | r->end = r->start; | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | |||
182 | DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases); | ||
183 | |||
184 | /* | ||
185 | * Called after each bus is probed, but before its children | ||
186 | * are examined. | ||
187 | */ | ||
188 | |||
189 | void __init pcibios_fixup_bus(struct pci_bus *b) | ||
190 | { | ||
191 | pci_read_bridge_bases(b); | ||
192 | } | ||
193 | |||
194 | /* | ||
195 | * Initialization. Try all known PCI access methods. Note that we support | ||
196 | * using both PCI BIOS and direct access: in such cases, we use I/O ports | ||
197 | * to access config space. | ||
198 | * | ||
199 | * Note that the platform specific initialization (BSC registers, and memory | ||
200 | * space mapping) will be called via the machine vectors (sh_mv.mv_pci_init()) if it | ||
201 | * exists and via the platform defined function pcibios_init_platform(). | ||
202 | * See pci_bigsur.c for implementation; | ||
203 | * | ||
204 | * The BIOS version of the pci functions is not yet implemented but it is left | ||
205 | * in for completeness. Currently an error will be genereated at compile time. | ||
206 | */ | ||
207 | |||
208 | static int __init sh7780_pci_init(void) | ||
209 | { | ||
210 | int ret; | ||
211 | |||
212 | pr_debug("PCI: Starting intialization.\n"); | ||
213 | |||
214 | /* Setup the INTC */ | ||
215 | ctrl_outl(0x00200000, INTC_ICR0); /* INTC SH-4 Mode */ | ||
216 | ctrl_outl(0x00078000, INTC_INT2MSKCR); /* enable PCIINTA - PCIINTD */ | ||
217 | ctrl_outl(0x40000000, INTC_INTMSK1); /* disable IRL4-7 Interrupt */ | ||
218 | ctrl_outl(0x0000fffe, INTC_INTMSK2); /* disable IRL4-7 Interrupt */ | ||
219 | ctrl_outl(0x80000000, INTC_INTMSKCLR1); /* enable IRL0-3 Interrupt */ | ||
220 | ctrl_outl(0xfffe0000, INTC_INTMSKCLR2); /* enable IRL0-3 Interrupt */ | ||
221 | |||
222 | if ((ret = pci_check_direct()) != 0) | ||
223 | return ret; | ||
224 | |||
225 | return pcibios_init_platform(); | ||
226 | } | ||
227 | |||
228 | core_initcall(sh7780_pci_init); | ||
229 | |||
230 | int __init sh7780_pcic_init(struct sh7780_pci_address_map *map) | ||
231 | { | ||
232 | u32 word; | ||
233 | |||
234 | /* | ||
235 | * This code is unused for some boards as it is done in the | ||
236 | * bootloader and doing it here means the MAC addresses loaded | ||
237 | * by the bootloader get lost. | ||
238 | */ | ||
239 | if (!(map->flags & SH7780_PCIC_NO_RESET)) { | ||
240 | /* toggle PCI reset pin */ | ||
241 | word = SH7780_PCICR_PREFIX | SH7780_PCICR_PRST; | ||
242 | outl(word,PCI_REG(SH7780_PCICR)); | ||
243 | /* Wait for a long time... not 1 sec. but long enough */ | ||
244 | mdelay(100); | ||
245 | word = SH7780_PCICR_PREFIX; | ||
246 | outl(word,PCI_REG(SH7780_PCICR)); | ||
247 | } | ||
248 | |||
249 | /* set the command/status bits to: | ||
250 | * Wait Cycle Control + Parity Enable + Bus Master + | ||
251 | * Mem space enable | ||
252 | */ | ||
253 | outl(0x00000046, PCI_REG(SH7780_PCICMD)); | ||
254 | |||
255 | /* define this host as the host bridge */ | ||
256 | word = SH7780_PCI_HOST_BRIDGE << 24; | ||
257 | outl(word, PCI_REG(SH7780_PCIRID)); | ||
258 | |||
259 | /* Set IO and Mem windows to local address | ||
260 | * Make PCI and local address the same for easy 1 to 1 mapping | ||
261 | * Window0 = map->window0.size @ non-cached area base = SDRAM | ||
262 | * Window1 = map->window1.size @ cached area base = SDRAM | ||
263 | */ | ||
264 | word = ((map->window0.size - 1) & 0x1ff00001) | 0x01; | ||
265 | outl(0x07f00001, PCI_REG(SH7780_PCILSR0)); | ||
266 | word = ((map->window1.size - 1) & 0x1ff00001) | 0x01; | ||
267 | outl(0x00000001, PCI_REG(SH7780_PCILSR1)); | ||
268 | /* Set the values on window 0 PCI config registers */ | ||
269 | word = P2SEGADDR(map->window0.base); | ||
270 | outl(0xa8000000, PCI_REG(SH7780_PCILAR0)); | ||
271 | outl(0x08000000, PCI_REG(SH7780_PCIMBAR0)); | ||
272 | /* Set the values on window 1 PCI config registers */ | ||
273 | word = P2SEGADDR(map->window1.base); | ||
274 | outl(0x00000000, PCI_REG(SH7780_PCILAR1)); | ||
275 | outl(0x00000000, PCI_REG(SH7780_PCIMBAR1)); | ||
276 | |||
277 | /* Map IO space into PCI IO window | ||
278 | * The IO window is 64K-PCIBIOS_MIN_IO in size | ||
279 | * IO addresses will be translated to the | ||
280 | * PCI IO window base address | ||
281 | */ | ||
282 | PCIDBG(3,"PCI: Mapping IO address 0x%x - 0x%x to base 0x%x\n", PCIBIOS_MIN_IO, | ||
283 | (64*1024), SH7780_PCI_IO_BASE+PCIBIOS_MIN_IO); | ||
284 | |||
285 | /* NOTE: I'm ignoring the PCI error IRQs for now.. | ||
286 | * TODO: add support for the internal error interrupts and | ||
287 | * DMA interrupts... | ||
288 | */ | ||
289 | |||
290 | #ifdef CONFIG_SH_R7780RP | ||
291 | pci_fixup_pcic(); | ||
292 | #endif | ||
293 | |||
294 | /* SH7780 init done, set central function init complete */ | ||
295 | /* use round robin mode to stop a device starving/overruning */ | ||
296 | word = SH7780_PCICR_PREFIX | SH7780_PCICR_CFIN | /* SH7780_PCICR_ARBM |*/ SH7780_PCICR_FTO; | ||
297 | outl(word, PCI_REG(SH7780_PCICR)); | ||
298 | |||
299 | return 1; | ||
300 | } | ||
301 | |||
302 | char * __init pcibios_setup(char *str) | ||
303 | { | ||
304 | if (!strcmp(str, "off")) { | ||
305 | pci_probe = 0; | ||
306 | return NULL; | ||
307 | } | ||
308 | |||
309 | return str; | ||
310 | } | ||
311 | |||
312 | /* | ||
313 | * IRQ functions | ||
314 | */ | ||
315 | static u8 __init sh7780_no_swizzle(struct pci_dev *dev, u8 *pin) | ||
316 | { | ||
317 | /* no swizzling */ | ||
318 | return PCI_SLOT(dev->devfn); | ||
319 | } | ||
320 | |||
321 | static int sh7780_pci_lookup_irq(struct pci_dev *dev, u8 slot, u8 pin) | ||
322 | { | ||
323 | int irq = -1; | ||
324 | |||
325 | /* now lookup the actual IRQ on a platform specific basis (pci-'platform'.c) */ | ||
326 | irq = pcibios_map_platform_irq(slot,pin); | ||
327 | if( irq < 0 ) { | ||
328 | pr_debug("PCI: Error mapping IRQ on device %s\n", pci_name(dev)); | ||
329 | return irq; | ||
330 | } | ||
331 | |||
332 | pr_debug("Setting IRQ for slot %s to %d\n", pci_name(dev), irq); | ||
333 | |||
334 | return irq; | ||
335 | } | ||
336 | |||
337 | void __init pcibios_fixup_irqs(void) | ||
338 | { | ||
339 | pci_fixup_irqs(sh7780_no_swizzle, sh7780_pci_lookup_irq); | ||
340 | } | ||
341 | |||