diff options
Diffstat (limited to 'arch/ppc/platforms/apus_pci.c')
-rw-r--r-- | arch/ppc/platforms/apus_pci.c | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/arch/ppc/platforms/apus_pci.c b/arch/ppc/platforms/apus_pci.c new file mode 100644 index 000000000000..33dad6db8243 --- /dev/null +++ b/arch/ppc/platforms/apus_pci.c | |||
@@ -0,0 +1,208 @@ | |||
1 | /* | ||
2 | * Copyright (C) Michel Dänzer <michdaen@iiic.ethz.ch> | ||
3 | * | ||
4 | * APUS PCI routines. | ||
5 | * | ||
6 | * Currently, only B/CVisionPPC cards (Permedia2) are supported. | ||
7 | * | ||
8 | * Thanks to Geert Uytterhoeven for the idea: | ||
9 | * Read values from given config space(s) for the first devices, -1 otherwise | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #ifdef CONFIG_AMIGA | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/pci.h> | ||
18 | #include <linux/delay.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/init.h> | ||
21 | |||
22 | #include <asm/io.h> | ||
23 | #include <asm/pci-bridge.h> | ||
24 | #include <asm/machdep.h> | ||
25 | |||
26 | #include "apus_pci.h" | ||
27 | |||
28 | |||
29 | /* These definitions are mostly adapted from pm2fb.c */ | ||
30 | |||
31 | #undef APUS_PCI_MASTER_DEBUG | ||
32 | #ifdef APUS_PCI_MASTER_DEBUG | ||
33 | #define DPRINTK(a,b...) printk(KERN_DEBUG "apus_pci: %s: " a, __FUNCTION__ , ## b) | ||
34 | #else | ||
35 | #define DPRINTK(a,b...) | ||
36 | #endif | ||
37 | |||
38 | /* | ||
39 | * The _DEFINITIVE_ memory mapping/unmapping functions. | ||
40 | * This is due to the fact that they're changing soooo often... | ||
41 | */ | ||
42 | #define DEFW() wmb() | ||
43 | #define DEFR() rmb() | ||
44 | #define DEFRW() mb() | ||
45 | |||
46 | #define DEVNO(d) ((d)>>3) | ||
47 | #define FNNO(d) ((d)&7) | ||
48 | |||
49 | |||
50 | extern unsigned long powerup_PCI_present; | ||
51 | |||
52 | static struct pci_controller *apus_hose; | ||
53 | |||
54 | |||
55 | void *pci_io_base(unsigned int bus) | ||
56 | { | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | |||
61 | int | ||
62 | apus_pcibios_read_config(struct pci_bus *bus, int devfn, int offset, | ||
63 | int len, u32 *val) | ||
64 | { | ||
65 | int fnno = FNNO(devfn); | ||
66 | int devno = DEVNO(devfn); | ||
67 | volatile unsigned char *cfg_data; | ||
68 | |||
69 | if (bus->number > 0 || devno != 1) { | ||
70 | *val = ~0; | ||
71 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
72 | } | ||
73 | /* base address + function offset + offset ^ endianness conversion */ | ||
74 | /* XXX the fnno<<5 bit seems wacky -- paulus */ | ||
75 | cfg_data = apus_hose->cfg_data + (fnno<<5) + (offset ^ (len - 1)); | ||
76 | switch (len) { | ||
77 | case 1: | ||
78 | *val = readb(cfg_data); | ||
79 | break; | ||
80 | case 2: | ||
81 | *val = readw(cfg_data); | ||
82 | break; | ||
83 | default: | ||
84 | *val = readl(cfg_data); | ||
85 | break; | ||
86 | } | ||
87 | |||
88 | DPRINTK("read b: 0x%x, d: 0x%x, f: 0x%x, o: 0x%x, l: %d, v: 0x%x\n", | ||
89 | bus->number, devfn>>3, devfn&7, offset, len, *val); | ||
90 | return PCIBIOS_SUCCESSFUL; | ||
91 | } | ||
92 | |||
93 | int | ||
94 | apus_pcibios_write_config(struct pci_bus *bus, int devfn, int offset, | ||
95 | int len, u32 *val) | ||
96 | { | ||
97 | int fnno = FNNO(devfn); | ||
98 | int devno = DEVNO(devfn); | ||
99 | volatile unsigned char *cfg_data; | ||
100 | |||
101 | if (bus->number > 0 || devno != 1) { | ||
102 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
103 | } | ||
104 | /* base address + function offset + offset ^ endianness conversion */ | ||
105 | /* XXX the fnno<<5 bit seems wacky -- paulus */ | ||
106 | cfg_data = apus_hose->cfg_data + (fnno<<5) + (offset ^ (len - 1)); | ||
107 | switch (len) { | ||
108 | case 1: | ||
109 | writeb(val, cfg_data); DEFW(); | ||
110 | break; | ||
111 | case 2: | ||
112 | writew(val, cfg_data); DEFW(); | ||
113 | break; | ||
114 | default: | ||
115 | writel(val, cfg_data); DEFW(); | ||
116 | break; | ||
117 | } | ||
118 | |||
119 | DPRINTK("write b: 0x%x, d: 0x%x, f: 0x%x, o: 0x%x, l: %d, v: 0x%x\n", | ||
120 | bus->number, devfn>>3, devfn&7, offset, len, val); | ||
121 | return PCIBIOS_SUCCESSFUL; | ||
122 | } | ||
123 | |||
124 | static struct pci_ops apus_pci_ops = { | ||
125 | apus_pcibios_read_config, | ||
126 | apus_pcibios_write_config | ||
127 | }; | ||
128 | |||
129 | static struct resource pci_mem = { "B/CVisionPPC PCI mem", CVPPC_FB_APERTURE_ONE, CVPPC_PCI_CONFIG, IORESOURCE_MEM }; | ||
130 | |||
131 | void __init | ||
132 | apus_pcibios_fixup(void) | ||
133 | { | ||
134 | /* struct pci_dev *dev = pci_find_slot(0, 1<<3); | ||
135 | unsigned int reg, val, offset;*/ | ||
136 | |||
137 | /* FIXME: interrupt? */ | ||
138 | /*dev->interrupt = xxx;*/ | ||
139 | |||
140 | request_resource(&iomem_resource, &pci_mem); | ||
141 | printk("%s: PCI mem resource requested\n", __FUNCTION__); | ||
142 | } | ||
143 | |||
144 | static void __init apus_pcibios_fixup_bus(struct pci_bus *bus) | ||
145 | { | ||
146 | bus->resource[1] = &pci_mem; | ||
147 | } | ||
148 | |||
149 | |||
150 | /* | ||
151 | * This is from pm2fb.c again | ||
152 | * | ||
153 | * Check if PCI (B/CVisionPPC) is available, initialize it and set up | ||
154 | * the pcibios_* pointers | ||
155 | */ | ||
156 | |||
157 | |||
158 | void __init | ||
159 | apus_setup_pci_ptrs(void) | ||
160 | { | ||
161 | if (!powerup_PCI_present) { | ||
162 | DPRINTK("no PCI bridge detected\n"); | ||
163 | return; | ||
164 | } | ||
165 | DPRINTK("Phase5 B/CVisionPPC PCI bridge detected.\n"); | ||
166 | |||
167 | apus_hose = pcibios_alloc_controller(); | ||
168 | if (!apus_hose) { | ||
169 | printk("apus_pci: Can't allocate PCI controller structure\n"); | ||
170 | return; | ||
171 | } | ||
172 | |||
173 | if (!(apus_hose->cfg_data = ioremap(CVPPC_PCI_CONFIG, 256))) { | ||
174 | printk("apus_pci: unable to map PCI config region\n"); | ||
175 | return; | ||
176 | } | ||
177 | |||
178 | if (!(apus_hose->cfg_addr = ioremap(CSPPC_PCI_BRIDGE, 256))) { | ||
179 | printk("apus_pci: unable to map PCI bridge\n"); | ||
180 | return; | ||
181 | } | ||
182 | |||
183 | writel(CSPPCF_BRIDGE_BIG_ENDIAN, apus_hose->cfg_addr + CSPPC_BRIDGE_ENDIAN); | ||
184 | DEFW(); | ||
185 | |||
186 | writel(CVPPC_REGS_REGION, apus_hose->cfg_data+ PCI_BASE_ADDRESS_0); | ||
187 | DEFW(); | ||
188 | writel(CVPPC_FB_APERTURE_ONE, apus_hose->cfg_data + PCI_BASE_ADDRESS_1); | ||
189 | DEFW(); | ||
190 | writel(CVPPC_FB_APERTURE_TWO, apus_hose->cfg_data + PCI_BASE_ADDRESS_2); | ||
191 | DEFW(); | ||
192 | writel(CVPPC_ROM_ADDRESS, apus_hose->cfg_data + PCI_ROM_ADDRESS); | ||
193 | DEFW(); | ||
194 | |||
195 | writel(0xef000000 | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | | ||
196 | PCI_COMMAND_MASTER, apus_hose->cfg_data + PCI_COMMAND); | ||
197 | DEFW(); | ||
198 | |||
199 | apus_hose->first_busno = 0; | ||
200 | apus_hose->last_busno = 0; | ||
201 | apus_hose->ops = &apus_pci_ops; | ||
202 | ppc_md.pcibios_fixup = apus_pcibios_fixup; | ||
203 | ppc_md.pcibios_fixup_bus = apus_pcibios_fixup_bus; | ||
204 | |||
205 | return; | ||
206 | } | ||
207 | |||
208 | #endif /* CONFIG_AMIGA */ | ||