diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc/syslib |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/ppc/syslib')
79 files changed, 23351 insertions, 0 deletions
diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile new file mode 100644 index 000000000000..dd418ea3426c --- /dev/null +++ b/arch/ppc/syslib/Makefile | |||
@@ -0,0 +1,115 @@ | |||
1 | # | ||
2 | # Makefile for the linux kernel. | ||
3 | # | ||
4 | |||
5 | CFLAGS_prom_init.o += -fPIC | ||
6 | CFLAGS_btext.o += -fPIC | ||
7 | |||
8 | wdt-mpc8xx-$(CONFIG_8xx_WDT) += m8xx_wdt.o | ||
9 | |||
10 | obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o | ||
11 | obj-$(CONFIG_PPC_OCP) += ocp.o | ||
12 | obj-$(CONFIG_IBM_OCP) += ibm_ocp.o | ||
13 | obj-$(CONFIG_44x) += ibm44x_common.o | ||
14 | obj-$(CONFIG_440GP) += ibm440gp_common.o | ||
15 | obj-$(CONFIG_440GX) += ibm440gx_common.o | ||
16 | obj-$(CONFIG_440SP) += ibm440gx_common.o ibm440sp_common.o | ||
17 | ifeq ($(CONFIG_4xx),y) | ||
18 | ifeq ($(CONFIG_VIRTEX_II_PRO),y) | ||
19 | obj-$(CONFIG_40x) += xilinx_pic.o | ||
20 | else | ||
21 | ifeq ($(CONFIG_403),y) | ||
22 | obj-$(CONFIG_40x) += ppc403_pic.o | ||
23 | else | ||
24 | obj-$(CONFIG_40x) += ppc4xx_pic.o | ||
25 | endif | ||
26 | endif | ||
27 | obj-$(CONFIG_44x) += ppc4xx_pic.o | ||
28 | obj-$(CONFIG_40x) += ppc4xx_setup.o | ||
29 | obj-$(CONFIG_GEN_RTC) += todc_time.o | ||
30 | obj-$(CONFIG_PPC4xx_DMA) += ppc4xx_dma.o | ||
31 | obj-$(CONFIG_PPC4xx_EDMA) += ppc4xx_sgdma.o | ||
32 | ifeq ($(CONFIG_40x),y) | ||
33 | obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o ppc405_pci.o | ||
34 | endif | ||
35 | endif | ||
36 | obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o $(wdt-mpc8xx-y) | ||
37 | ifeq ($(CONFIG_8xx),y) | ||
38 | obj-$(CONFIG_PCI) += qspan_pci.o i8259.o | ||
39 | endif | ||
40 | obj-$(CONFIG_PPC_OF) += prom_init.o prom.o of_device.o | ||
41 | obj-$(CONFIG_PPC_PMAC) += open_pic.o indirect_pci.o | ||
42 | obj-$(CONFIG_POWER4) += open_pic2.o | ||
43 | obj-$(CONFIG_PPC_CHRP) += open_pic.o indirect_pci.o i8259.o | ||
44 | obj-$(CONFIG_PPC_PREP) += open_pic.o indirect_pci.o i8259.o todc_time.o | ||
45 | obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \ | ||
46 | todc_time.o | ||
47 | obj-$(CONFIG_CPCI690) += todc_time.o pci_auto.o | ||
48 | obj-$(CONFIG_EBONY) += indirect_pci.o pci_auto.o todc_time.o | ||
49 | obj-$(CONFIG_EV64260) += todc_time.o pci_auto.o | ||
50 | obj-$(CONFIG_CHESTNUT) += mv64360_pic.o pci_auto.o | ||
51 | obj-$(CONFIG_GEMINI) += open_pic.o indirect_pci.o | ||
52 | obj-$(CONFIG_GT64260) += gt64260_pic.o | ||
53 | obj-$(CONFIG_K2) += i8259.o indirect_pci.o todc_time.o \ | ||
54 | pci_auto.o | ||
55 | obj-$(CONFIG_LOPEC) += i8259.o pci_auto.o todc_time.o | ||
56 | obj-$(CONFIG_HDPU) += pci_auto.o | ||
57 | obj-$(CONFIG_LUAN) += indirect_pci.o pci_auto.o todc_time.o | ||
58 | obj-$(CONFIG_KATANA) += pci_auto.o | ||
59 | obj-$(CONFIG_MCPN765) += todc_time.o indirect_pci.o pci_auto.o \ | ||
60 | open_pic.o i8259.o hawk_common.o | ||
61 | obj-$(CONFIG_MENF1) += todc_time.o i8259.o mpc10x_common.o \ | ||
62 | pci_auto.o indirect_pci.o | ||
63 | obj-$(CONFIG_MV64360) += mv64360_pic.o | ||
64 | obj-$(CONFIG_MV64X60) += mv64x60.o mv64x60_win.o indirect_pci.o | ||
65 | obj-$(CONFIG_MVME5100) += open_pic.o todc_time.o indirect_pci.o \ | ||
66 | pci_auto.o hawk_common.o | ||
67 | obj-$(CONFIG_MVME5100_IPMC761_PRESENT) += i8259.o | ||
68 | obj-$(CONFIG_OCOTEA) += indirect_pci.o pci_auto.o todc_time.o | ||
69 | obj-$(CONFIG_PAL4) += cpc700_pic.o | ||
70 | obj-$(CONFIG_PCORE) += todc_time.o i8259.o pci_auto.o | ||
71 | obj-$(CONFIG_POWERPMC250) += pci_auto.o | ||
72 | obj-$(CONFIG_PPLUS) += hawk_common.o open_pic.o i8259.o \ | ||
73 | indirect_pci.o todc_time.o pci_auto.o | ||
74 | obj-$(CONFIG_PRPMC750) += open_pic.o indirect_pci.o pci_auto.o \ | ||
75 | hawk_common.o | ||
76 | obj-$(CONFIG_HARRIER) += harrier.o | ||
77 | obj-$(CONFIG_PRPMC800) += open_pic.o indirect_pci.o pci_auto.o | ||
78 | obj-$(CONFIG_RADSTONE_PPC7D) += i8259.o pci_auto.o | ||
79 | obj-$(CONFIG_SANDPOINT) += i8259.o pci_auto.o todc_time.o | ||
80 | obj-$(CONFIG_SBC82xx) += todc_time.o | ||
81 | obj-$(CONFIG_SPRUCE) += cpc700_pic.o indirect_pci.o pci_auto.o \ | ||
82 | todc_time.o | ||
83 | obj-$(CONFIG_8260) += m8260_setup.o | ||
84 | obj-$(CONFIG_PCI_8260) += m8260_pci.o indirect_pci.o | ||
85 | obj-$(CONFIG_8260_PCI9) += m8260_pci_erratum9.o | ||
86 | obj-$(CONFIG_CPM2) += cpm2_common.o cpm2_pic.o | ||
87 | ifeq ($(CONFIG_PPC_GEN550),y) | ||
88 | obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o | ||
89 | obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o | ||
90 | endif | ||
91 | ifeq ($(CONFIG_SERIAL_MPSC_CONSOLE),y) | ||
92 | obj-$(CONFIG_SERIAL_TEXT_DEBUG) += mv64x60_dbg.o | ||
93 | endif | ||
94 | obj-$(CONFIG_BOOTX_TEXT) += btext.o | ||
95 | obj-$(CONFIG_MPC10X_BRIDGE) += mpc10x_common.o indirect_pci.o | ||
96 | obj-$(CONFIG_MPC10X_OPENPIC) += open_pic.o | ||
97 | obj-$(CONFIG_40x) += dcr.o | ||
98 | obj-$(CONFIG_BOOKE) += dcr.o | ||
99 | obj-$(CONFIG_85xx) += open_pic.o ppc85xx_common.o ppc85xx_setup.o \ | ||
100 | ppc_sys.o mpc85xx_sys.o \ | ||
101 | mpc85xx_devices.o | ||
102 | ifeq ($(CONFIG_85xx),y) | ||
103 | obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o | ||
104 | endif | ||
105 | obj-$(CONFIG_83xx) += ipic.o ppc83xx_setup.o ppc_sys.o \ | ||
106 | mpc83xx_sys.o mpc83xx_devices.o | ||
107 | ifeq ($(CONFIG_83xx),y) | ||
108 | obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o | ||
109 | endif | ||
110 | obj-$(CONFIG_MPC8555_CDS) += todc_time.o | ||
111 | obj-$(CONFIG_PPC_MPC52xx) += mpc52xx_setup.o mpc52xx_pic.o \ | ||
112 | mpc52xx_sys.o mpc52xx_devices.o ppc_sys.o | ||
113 | ifeq ($(CONFIG_PPC_MPC52xx),y) | ||
114 | obj-$(CONFIG_PCI) += mpc52xx_pci.o | ||
115 | endif | ||
diff --git a/arch/ppc/syslib/btext.c b/arch/ppc/syslib/btext.c new file mode 100644 index 000000000000..7734f6836174 --- /dev/null +++ b/arch/ppc/syslib/btext.c | |||
@@ -0,0 +1,861 @@ | |||
1 | /* | ||
2 | * Procedures for drawing on the screen early on in the boot process. | ||
3 | * | ||
4 | * Benjamin Herrenschmidt <benh@kernel.crashing.org> | ||
5 | */ | ||
6 | #include <linux/config.h> | ||
7 | #include <linux/kernel.h> | ||
8 | #include <linux/string.h> | ||
9 | #include <linux/init.h> | ||
10 | #include <linux/version.h> | ||
11 | |||
12 | #include <asm/sections.h> | ||
13 | #include <asm/bootx.h> | ||
14 | #include <asm/btext.h> | ||
15 | #include <asm/prom.h> | ||
16 | #include <asm/page.h> | ||
17 | #include <asm/mmu.h> | ||
18 | #include <asm/pgtable.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/reg.h> | ||
21 | |||
22 | #define NO_SCROLL | ||
23 | |||
24 | #ifndef NO_SCROLL | ||
25 | static void scrollscreen(void); | ||
26 | #endif | ||
27 | |||
28 | static void draw_byte(unsigned char c, long locX, long locY); | ||
29 | static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb); | ||
30 | static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb); | ||
31 | static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb); | ||
32 | |||
33 | static int g_loc_X; | ||
34 | static int g_loc_Y; | ||
35 | static int g_max_loc_X; | ||
36 | static int g_max_loc_Y; | ||
37 | |||
38 | unsigned long disp_BAT[2] __initdata = {0, 0}; | ||
39 | |||
40 | #define cmapsz (16*256) | ||
41 | |||
42 | static unsigned char vga_font[cmapsz]; | ||
43 | |||
44 | int boot_text_mapped; | ||
45 | int force_printk_to_btext = 0; | ||
46 | |||
47 | boot_infos_t disp_bi; | ||
48 | |||
49 | extern char *klimit; | ||
50 | |||
51 | /* | ||
52 | * Powermac can use btext_* after boot for xmon, | ||
53 | * chrp only uses it during early boot. | ||
54 | */ | ||
55 | #ifdef CONFIG_XMON | ||
56 | #define BTEXT __pmac | ||
57 | #define BTDATA __pmacdata | ||
58 | #else | ||
59 | #define BTEXT __init | ||
60 | #define BTDATA __initdata | ||
61 | #endif /* CONFIG_XMON */ | ||
62 | |||
63 | /* | ||
64 | * This is called only when we are booted via BootX. | ||
65 | */ | ||
66 | void __init | ||
67 | btext_init(boot_infos_t *bi) | ||
68 | { | ||
69 | g_loc_X = 0; | ||
70 | g_loc_Y = 0; | ||
71 | g_max_loc_X = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8; | ||
72 | g_max_loc_Y = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16; | ||
73 | disp_bi = *bi; | ||
74 | boot_text_mapped = 1; | ||
75 | } | ||
76 | |||
77 | void __init | ||
78 | btext_welcome(void) | ||
79 | { | ||
80 | unsigned long flags; | ||
81 | unsigned long pvr; | ||
82 | boot_infos_t* bi = &disp_bi; | ||
83 | |||
84 | btext_drawstring("Welcome to Linux, kernel " UTS_RELEASE "\n"); | ||
85 | btext_drawstring("\nlinked at : 0x"); | ||
86 | btext_drawhex(KERNELBASE); | ||
87 | btext_drawstring("\nframe buffer at : 0x"); | ||
88 | btext_drawhex((unsigned long)bi->dispDeviceBase); | ||
89 | btext_drawstring(" (phys), 0x"); | ||
90 | btext_drawhex((unsigned long)bi->logicalDisplayBase); | ||
91 | btext_drawstring(" (log)"); | ||
92 | btext_drawstring("\nklimit : 0x"); | ||
93 | btext_drawhex((unsigned long)klimit); | ||
94 | btext_drawstring("\nMSR : 0x"); | ||
95 | __asm__ __volatile__ ("mfmsr %0" : "=r" (flags)); | ||
96 | btext_drawhex(flags); | ||
97 | __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr)); | ||
98 | pvr >>= 16; | ||
99 | if (pvr > 1) { | ||
100 | btext_drawstring("\nHID0 : 0x"); | ||
101 | __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags)); | ||
102 | btext_drawhex(flags); | ||
103 | } | ||
104 | if (pvr == 8 || pvr == 12 || pvr == 0x800c) { | ||
105 | btext_drawstring("\nICTC : 0x"); | ||
106 | __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags)); | ||
107 | btext_drawhex(flags); | ||
108 | } | ||
109 | btext_drawstring("\n\n"); | ||
110 | } | ||
111 | |||
112 | /* Calc BAT values for mapping the display and store them | ||
113 | * in disp_BAT. Those values are then used from head.S to map | ||
114 | * the display during identify_machine() and MMU_Init() | ||
115 | * | ||
116 | * The display is mapped to virtual address 0xD0000000, rather | ||
117 | * than 1:1, because some some CHRP machines put the frame buffer | ||
118 | * in the region starting at 0xC0000000 (KERNELBASE). | ||
119 | * This mapping is temporary and will disappear as soon as the | ||
120 | * setup done by MMU_Init() is applied. | ||
121 | * | ||
122 | * For now, we align the BAT and then map 8Mb on 601 and 16Mb | ||
123 | * on other PPCs. This may cause trouble if the framebuffer | ||
124 | * is really badly aligned, but I didn't encounter this case | ||
125 | * yet. | ||
126 | */ | ||
127 | void __init | ||
128 | btext_prepare_BAT(void) | ||
129 | { | ||
130 | boot_infos_t* bi = &disp_bi; | ||
131 | unsigned long vaddr = KERNELBASE + 0x10000000; | ||
132 | unsigned long addr; | ||
133 | unsigned long lowbits; | ||
134 | |||
135 | addr = (unsigned long)bi->dispDeviceBase; | ||
136 | if (!addr) { | ||
137 | boot_text_mapped = 0; | ||
138 | return; | ||
139 | } | ||
140 | if (PVR_VER(mfspr(SPRN_PVR)) != 1) { | ||
141 | /* 603, 604, G3, G4, ... */ | ||
142 | lowbits = addr & ~0xFF000000UL; | ||
143 | addr &= 0xFF000000UL; | ||
144 | disp_BAT[0] = vaddr | (BL_16M<<2) | 2; | ||
145 | disp_BAT[1] = addr | (_PAGE_NO_CACHE | _PAGE_GUARDED | BPP_RW); | ||
146 | } else { | ||
147 | /* 601 */ | ||
148 | lowbits = addr & ~0xFF800000UL; | ||
149 | addr &= 0xFF800000UL; | ||
150 | disp_BAT[0] = vaddr | (_PAGE_NO_CACHE | PP_RWXX) | 4; | ||
151 | disp_BAT[1] = addr | BL_8M | 0x40; | ||
152 | } | ||
153 | bi->logicalDisplayBase = (void *) (vaddr + lowbits); | ||
154 | } | ||
155 | |||
156 | /* This function will enable the early boot text when doing OF booting. This | ||
157 | * way, xmon output should work too | ||
158 | */ | ||
159 | void __init | ||
160 | btext_setup_display(int width, int height, int depth, int pitch, | ||
161 | unsigned long address) | ||
162 | { | ||
163 | boot_infos_t* bi = &disp_bi; | ||
164 | |||
165 | g_loc_X = 0; | ||
166 | g_loc_Y = 0; | ||
167 | g_max_loc_X = width / 8; | ||
168 | g_max_loc_Y = height / 16; | ||
169 | bi->logicalDisplayBase = (unsigned char *)address; | ||
170 | bi->dispDeviceBase = (unsigned char *)address; | ||
171 | bi->dispDeviceRowBytes = pitch; | ||
172 | bi->dispDeviceDepth = depth; | ||
173 | bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0; | ||
174 | bi->dispDeviceRect[2] = width; | ||
175 | bi->dispDeviceRect[3] = height; | ||
176 | boot_text_mapped = 1; | ||
177 | } | ||
178 | |||
179 | /* Here's a small text engine to use during early boot | ||
180 | * or for debugging purposes | ||
181 | * | ||
182 | * todo: | ||
183 | * | ||
184 | * - build some kind of vgacon with it to enable early printk | ||
185 | * - move to a separate file | ||
186 | * - add a few video driver hooks to keep in sync with display | ||
187 | * changes. | ||
188 | */ | ||
189 | |||
190 | void __openfirmware | ||
191 | map_boot_text(void) | ||
192 | { | ||
193 | unsigned long base, offset, size; | ||
194 | boot_infos_t *bi = &disp_bi; | ||
195 | unsigned char *vbase; | ||
196 | |||
197 | /* By default, we are no longer mapped */ | ||
198 | boot_text_mapped = 0; | ||
199 | if (bi->dispDeviceBase == 0) | ||
200 | return; | ||
201 | base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL; | ||
202 | offset = ((unsigned long) bi->dispDeviceBase) - base; | ||
203 | size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset | ||
204 | + bi->dispDeviceRect[0]; | ||
205 | vbase = ioremap(base, size); | ||
206 | if (vbase == 0) | ||
207 | return; | ||
208 | bi->logicalDisplayBase = vbase + offset; | ||
209 | boot_text_mapped = 1; | ||
210 | } | ||
211 | |||
212 | /* Calc the base address of a given point (x,y) */ | ||
213 | static unsigned char * BTEXT | ||
214 | calc_base(boot_infos_t *bi, int x, int y) | ||
215 | { | ||
216 | unsigned char *base; | ||
217 | |||
218 | base = bi->logicalDisplayBase; | ||
219 | if (base == 0) | ||
220 | base = bi->dispDeviceBase; | ||
221 | base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3); | ||
222 | base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes; | ||
223 | return base; | ||
224 | } | ||
225 | |||
226 | /* Adjust the display to a new resolution */ | ||
227 | void | ||
228 | btext_update_display(unsigned long phys, int width, int height, | ||
229 | int depth, int pitch) | ||
230 | { | ||
231 | boot_infos_t *bi = &disp_bi; | ||
232 | |||
233 | if (bi->dispDeviceBase == 0) | ||
234 | return; | ||
235 | |||
236 | /* check it's the same frame buffer (within 256MB) */ | ||
237 | if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000) | ||
238 | return; | ||
239 | |||
240 | bi->dispDeviceBase = (__u8 *) phys; | ||
241 | bi->dispDeviceRect[0] = 0; | ||
242 | bi->dispDeviceRect[1] = 0; | ||
243 | bi->dispDeviceRect[2] = width; | ||
244 | bi->dispDeviceRect[3] = height; | ||
245 | bi->dispDeviceDepth = depth; | ||
246 | bi->dispDeviceRowBytes = pitch; | ||
247 | if (boot_text_mapped) { | ||
248 | iounmap(bi->logicalDisplayBase); | ||
249 | boot_text_mapped = 0; | ||
250 | } | ||
251 | map_boot_text(); | ||
252 | g_loc_X = 0; | ||
253 | g_loc_Y = 0; | ||
254 | g_max_loc_X = width / 8; | ||
255 | g_max_loc_Y = height / 16; | ||
256 | } | ||
257 | |||
258 | void BTEXT btext_clearscreen(void) | ||
259 | { | ||
260 | boot_infos_t* bi = &disp_bi; | ||
261 | unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); | ||
262 | unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * | ||
263 | (bi->dispDeviceDepth >> 3)) >> 2; | ||
264 | int i,j; | ||
265 | |||
266 | for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) | ||
267 | { | ||
268 | unsigned long *ptr = base; | ||
269 | for(j=width; j; --j) | ||
270 | *(ptr++) = 0; | ||
271 | base += (bi->dispDeviceRowBytes >> 2); | ||
272 | } | ||
273 | } | ||
274 | |||
275 | __inline__ void dcbst(const void* addr) | ||
276 | { | ||
277 | __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr)); | ||
278 | } | ||
279 | |||
280 | void BTEXT btext_flushscreen(void) | ||
281 | { | ||
282 | boot_infos_t* bi = &disp_bi; | ||
283 | unsigned long *base = (unsigned long *)calc_base(bi, 0, 0); | ||
284 | unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * | ||
285 | (bi->dispDeviceDepth >> 3)) >> 2; | ||
286 | int i,j; | ||
287 | |||
288 | for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++) | ||
289 | { | ||
290 | unsigned long *ptr = base; | ||
291 | for(j=width; j>0; j-=8) { | ||
292 | dcbst(ptr); | ||
293 | ptr += 8; | ||
294 | } | ||
295 | base += (bi->dispDeviceRowBytes >> 2); | ||
296 | } | ||
297 | } | ||
298 | |||
299 | #ifndef NO_SCROLL | ||
300 | static BTEXT void | ||
301 | scrollscreen(void) | ||
302 | { | ||
303 | boot_infos_t* bi = &disp_bi; | ||
304 | unsigned long *src = (unsigned long *)calc_base(bi,0,16); | ||
305 | unsigned long *dst = (unsigned long *)calc_base(bi,0,0); | ||
306 | unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) * | ||
307 | (bi->dispDeviceDepth >> 3)) >> 2; | ||
308 | int i,j; | ||
309 | |||
310 | #ifdef CONFIG_ADB_PMU | ||
311 | pmu_suspend(); /* PMU will not shut us down ! */ | ||
312 | #endif | ||
313 | for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++) | ||
314 | { | ||
315 | unsigned long *src_ptr = src; | ||
316 | unsigned long *dst_ptr = dst; | ||
317 | for(j=width; j; --j) | ||
318 | *(dst_ptr++) = *(src_ptr++); | ||
319 | src += (bi->dispDeviceRowBytes >> 2); | ||
320 | dst += (bi->dispDeviceRowBytes >> 2); | ||
321 | } | ||
322 | for (i=0; i<16; i++) | ||
323 | { | ||
324 | unsigned long *dst_ptr = dst; | ||
325 | for(j=width; j; --j) | ||
326 | *(dst_ptr++) = 0; | ||
327 | dst += (bi->dispDeviceRowBytes >> 2); | ||
328 | } | ||
329 | #ifdef CONFIG_ADB_PMU | ||
330 | pmu_resume(); /* PMU will not shut us down ! */ | ||
331 | #endif | ||
332 | } | ||
333 | #endif /* ndef NO_SCROLL */ | ||
334 | |||
335 | void BTEXT btext_drawchar(char c) | ||
336 | { | ||
337 | int cline = 0, x; | ||
338 | |||
339 | if (!boot_text_mapped) | ||
340 | return; | ||
341 | |||
342 | switch (c) { | ||
343 | case '\b': | ||
344 | if (g_loc_X > 0) | ||
345 | --g_loc_X; | ||
346 | break; | ||
347 | case '\t': | ||
348 | g_loc_X = (g_loc_X & -8) + 8; | ||
349 | break; | ||
350 | case '\r': | ||
351 | g_loc_X = 0; | ||
352 | break; | ||
353 | case '\n': | ||
354 | g_loc_X = 0; | ||
355 | g_loc_Y++; | ||
356 | cline = 1; | ||
357 | break; | ||
358 | default: | ||
359 | draw_byte(c, g_loc_X++, g_loc_Y); | ||
360 | } | ||
361 | if (g_loc_X >= g_max_loc_X) { | ||
362 | g_loc_X = 0; | ||
363 | g_loc_Y++; | ||
364 | cline = 1; | ||
365 | } | ||
366 | #ifndef NO_SCROLL | ||
367 | while (g_loc_Y >= g_max_loc_Y) { | ||
368 | scrollscreen(); | ||
369 | g_loc_Y--; | ||
370 | } | ||
371 | #else | ||
372 | /* wrap around from bottom to top of screen so we don't | ||
373 | waste time scrolling each line. -- paulus. */ | ||
374 | if (g_loc_Y >= g_max_loc_Y) | ||
375 | g_loc_Y = 0; | ||
376 | if (cline) { | ||
377 | for (x = 0; x < g_max_loc_X; ++x) | ||
378 | draw_byte(' ', x, g_loc_Y); | ||
379 | } | ||
380 | #endif | ||
381 | } | ||
382 | |||
383 | void BTEXT | ||
384 | btext_drawstring(const char *c) | ||
385 | { | ||
386 | if (!boot_text_mapped) | ||
387 | return; | ||
388 | while (*c) | ||
389 | btext_drawchar(*c++); | ||
390 | } | ||
391 | |||
392 | void BTEXT | ||
393 | btext_drawhex(unsigned long v) | ||
394 | { | ||
395 | static char hex_table[] = "0123456789abcdef"; | ||
396 | |||
397 | if (!boot_text_mapped) | ||
398 | return; | ||
399 | btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]); | ||
400 | btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]); | ||
401 | btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]); | ||
402 | btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]); | ||
403 | btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]); | ||
404 | btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]); | ||
405 | btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]); | ||
406 | btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]); | ||
407 | btext_drawchar(' '); | ||
408 | } | ||
409 | |||
410 | static void BTEXT | ||
411 | draw_byte(unsigned char c, long locX, long locY) | ||
412 | { | ||
413 | boot_infos_t* bi = &disp_bi; | ||
414 | unsigned char *base = calc_base(bi, locX << 3, locY << 4); | ||
415 | unsigned char *font = &vga_font[((unsigned long)c) * 16]; | ||
416 | int rb = bi->dispDeviceRowBytes; | ||
417 | |||
418 | switch(bi->dispDeviceDepth) { | ||
419 | case 24: | ||
420 | case 32: | ||
421 | draw_byte_32(font, (unsigned long *)base, rb); | ||
422 | break; | ||
423 | case 15: | ||
424 | case 16: | ||
425 | draw_byte_16(font, (unsigned long *)base, rb); | ||
426 | break; | ||
427 | case 8: | ||
428 | draw_byte_8(font, (unsigned long *)base, rb); | ||
429 | break; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | static unsigned long expand_bits_8[16] BTDATA = { | ||
434 | 0x00000000, | ||
435 | 0x000000ff, | ||
436 | 0x0000ff00, | ||
437 | 0x0000ffff, | ||
438 | 0x00ff0000, | ||
439 | 0x00ff00ff, | ||
440 | 0x00ffff00, | ||
441 | 0x00ffffff, | ||
442 | 0xff000000, | ||
443 | 0xff0000ff, | ||
444 | 0xff00ff00, | ||
445 | 0xff00ffff, | ||
446 | 0xffff0000, | ||
447 | 0xffff00ff, | ||
448 | 0xffffff00, | ||
449 | 0xffffffff | ||
450 | }; | ||
451 | |||
452 | static unsigned long expand_bits_16[4] BTDATA = { | ||
453 | 0x00000000, | ||
454 | 0x0000ffff, | ||
455 | 0xffff0000, | ||
456 | 0xffffffff | ||
457 | }; | ||
458 | |||
459 | |||
460 | static void BTEXT | ||
461 | draw_byte_32(unsigned char *font, unsigned long *base, int rb) | ||
462 | { | ||
463 | int l, bits; | ||
464 | int fg = 0xFFFFFFFFUL; | ||
465 | int bg = 0x00000000UL; | ||
466 | |||
467 | for (l = 0; l < 16; ++l) | ||
468 | { | ||
469 | bits = *font++; | ||
470 | base[0] = (-(bits >> 7) & fg) ^ bg; | ||
471 | base[1] = (-((bits >> 6) & 1) & fg) ^ bg; | ||
472 | base[2] = (-((bits >> 5) & 1) & fg) ^ bg; | ||
473 | base[3] = (-((bits >> 4) & 1) & fg) ^ bg; | ||
474 | base[4] = (-((bits >> 3) & 1) & fg) ^ bg; | ||
475 | base[5] = (-((bits >> 2) & 1) & fg) ^ bg; | ||
476 | base[6] = (-((bits >> 1) & 1) & fg) ^ bg; | ||
477 | base[7] = (-(bits & 1) & fg) ^ bg; | ||
478 | base = (unsigned long *) ((char *)base + rb); | ||
479 | } | ||
480 | } | ||
481 | |||
482 | static void BTEXT | ||
483 | draw_byte_16(unsigned char *font, unsigned long *base, int rb) | ||
484 | { | ||
485 | int l, bits; | ||
486 | int fg = 0xFFFFFFFFUL; | ||
487 | int bg = 0x00000000UL; | ||
488 | unsigned long *eb = expand_bits_16; | ||
489 | |||
490 | for (l = 0; l < 16; ++l) | ||
491 | { | ||
492 | bits = *font++; | ||
493 | base[0] = (eb[bits >> 6] & fg) ^ bg; | ||
494 | base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg; | ||
495 | base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg; | ||
496 | base[3] = (eb[bits & 3] & fg) ^ bg; | ||
497 | base = (unsigned long *) ((char *)base + rb); | ||
498 | } | ||
499 | } | ||
500 | |||
501 | static void BTEXT | ||
502 | draw_byte_8(unsigned char *font, unsigned long *base, int rb) | ||
503 | { | ||
504 | int l, bits; | ||
505 | int fg = 0x0F0F0F0FUL; | ||
506 | int bg = 0x00000000UL; | ||
507 | unsigned long *eb = expand_bits_8; | ||
508 | |||
509 | for (l = 0; l < 16; ++l) | ||
510 | { | ||
511 | bits = *font++; | ||
512 | base[0] = (eb[bits >> 4] & fg) ^ bg; | ||
513 | base[1] = (eb[bits & 0xf] & fg) ^ bg; | ||
514 | base = (unsigned long *) ((char *)base + rb); | ||
515 | } | ||
516 | } | ||
517 | |||
518 | static unsigned char vga_font[cmapsz] BTDATA = { | ||
519 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
520 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd, | ||
521 | 0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff, | ||
522 | 0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00, | ||
523 | 0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, | ||
524 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe, | ||
525 | 0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, | ||
526 | 0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, | ||
527 | 0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c, | ||
528 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, | ||
529 | 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, | ||
530 | 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
531 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00, | ||
532 | 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd, | ||
533 | 0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e, | ||
534 | 0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, | ||
535 | 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18, | ||
536 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30, | ||
537 | 0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63, | ||
538 | 0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00, | ||
539 | 0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18, | ||
540 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8, | ||
541 | 0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e, | ||
542 | 0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, | ||
543 | 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00, | ||
544 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, | ||
545 | 0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb, | ||
546 | 0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00, | ||
547 | 0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6, | ||
548 | 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
549 | 0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, | ||
550 | 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, | ||
551 | 0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
552 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
553 | 0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
554 | 0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
555 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00, | ||
556 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0, | ||
557 | 0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
558 | 0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
559 | 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00, | ||
560 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c, | ||
561 | 0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
562 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
563 | 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, | ||
564 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00, | ||
565 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, | ||
566 | 0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, | ||
567 | 0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c, | ||
568 | 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18, | ||
569 | 0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, | ||
570 | 0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, | ||
571 | 0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
572 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30, | ||
573 | 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, | ||
574 | 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, | ||
575 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00, | ||
576 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, | ||
577 | 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
578 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, | ||
579 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, | ||
580 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
581 | 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
582 | 0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, | ||
583 | 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c, | ||
584 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18, | ||
585 | 0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, | ||
586 | 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, | ||
587 | 0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c, | ||
588 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe, | ||
589 | 0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, | ||
590 | 0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, | ||
591 | 0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, | ||
592 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18, | ||
593 | 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, | ||
594 | 0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, | ||
595 | 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78, | ||
596 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, | ||
597 | 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
598 | 0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00, | ||
599 | 0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06, | ||
600 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, | ||
601 | 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, | ||
602 | 0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, | ||
603 | 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, | ||
604 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde, | ||
605 | 0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, | ||
606 | 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, | ||
607 | 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc, | ||
608 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0, | ||
609 | 0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c, | ||
610 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00, | ||
611 | 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe, | ||
612 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, | ||
613 | 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, | ||
614 | 0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00, | ||
615 | 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, | ||
616 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
617 | 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c, | ||
618 | 0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00, | ||
619 | 0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6, | ||
620 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60, | ||
621 | 0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7, | ||
622 | 0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, | ||
623 | 0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6, | ||
624 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, | ||
625 | 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, | ||
626 | 0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, | ||
627 | 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c, | ||
628 | 0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c, | ||
629 | 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, | ||
630 | 0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, | ||
631 | 0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, | ||
632 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, | ||
633 | 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, | ||
634 | 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, | ||
635 | 0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66, | ||
636 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18, | ||
637 | 0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, | ||
638 | 0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, | ||
639 | 0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff, | ||
640 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30, | ||
641 | 0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, | ||
642 | 0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00, | ||
643 | 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c, | ||
644 | 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00, | ||
645 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
646 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, | ||
647 | 0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
648 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c, | ||
649 | 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60, | ||
650 | 0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00, | ||
651 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c, | ||
652 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc, | ||
653 | 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
654 | 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, | ||
655 | 0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0, | ||
656 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc, | ||
657 | 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60, | ||
658 | 0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, | ||
659 | 0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, | ||
660 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06, | ||
661 | 0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60, | ||
662 | 0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, | ||
663 | 0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, | ||
664 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb, | ||
665 | 0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
666 | 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, | ||
667 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, | ||
668 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66, | ||
669 | 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
670 | 0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00, | ||
671 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0, | ||
672 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60, | ||
673 | 0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30, | ||
674 | 0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00, | ||
675 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, | ||
676 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3, | ||
677 | 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
678 | 0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00, | ||
679 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3, | ||
680 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, | ||
681 | 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
682 | 0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, | ||
683 | 0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e, | ||
684 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18, | ||
685 | 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18, | ||
686 | 0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00, | ||
687 | 0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
688 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, | ||
689 | 0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, | ||
690 | 0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00, | ||
691 | 0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, | ||
692 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe, | ||
693 | 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, | ||
694 | 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, | ||
695 | 0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, | ||
696 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c, | ||
697 | 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, | ||
698 | 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, | ||
699 | 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06, | ||
700 | 0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe, | ||
701 | 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, | ||
702 | 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, | ||
703 | 0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, | ||
704 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18, | ||
705 | 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66, | ||
706 | 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, | ||
707 | 0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, | ||
708 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6, | ||
709 | 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00, | ||
710 | 0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, | ||
711 | 0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe, | ||
712 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b, | ||
713 | 0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c, | ||
714 | 0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00, | ||
715 | 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, | ||
716 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6, | ||
717 | 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, | ||
718 | 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, | ||
719 | 0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76, | ||
720 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc, | ||
721 | 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, | ||
722 | 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00, | ||
723 | 0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, | ||
724 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, | ||
725 | 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, | ||
726 | 0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, | ||
727 | 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc, | ||
728 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18, | ||
729 | 0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, | ||
730 | 0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00, | ||
731 | 0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
732 | 0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c, | ||
733 | 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, | ||
734 | 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, | ||
735 | 0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, | ||
736 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc, | ||
737 | 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, | ||
738 | 0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, | ||
739 | 0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, | ||
740 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00, | ||
741 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, | ||
742 | 0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
743 | 0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c, | ||
744 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0, | ||
745 | 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
746 | 0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
747 | 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06, | ||
748 | 0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, | ||
749 | 0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, | ||
750 | 0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, | ||
751 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00, | ||
752 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36, | ||
753 | 0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44, | ||
754 | 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, | ||
755 | 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, | ||
756 | 0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, | ||
757 | 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18, | ||
758 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
759 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18, | ||
760 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, | ||
761 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, | ||
762 | 0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
763 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36, | ||
764 | 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8, | ||
765 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, | ||
766 | 0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
767 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
768 | 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6, | ||
769 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
770 | 0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
771 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00, | ||
772 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8, | ||
773 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
774 | 0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
775 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, | ||
776 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, | ||
777 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
778 | 0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
779 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, | ||
780 | 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, | ||
781 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, | ||
782 | 0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
783 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, | ||
784 | 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, | ||
785 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
786 | 0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
787 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, | ||
788 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff, | ||
789 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
790 | 0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
791 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36, | ||
792 | 0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, | ||
793 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, | ||
794 | 0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
795 | 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, | ||
796 | 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff, | ||
797 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
798 | 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
799 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36, | ||
800 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f, | ||
801 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, | ||
802 | 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
803 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18, | ||
804 | 0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, | ||
805 | 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
806 | 0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, | ||
807 | 0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, | ||
808 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, | ||
809 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
810 | 0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
811 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
812 | 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, | ||
813 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, | ||
814 | 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, | ||
815 | 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, | ||
816 | 0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, | ||
817 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
818 | 0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00, | ||
819 | 0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc, | ||
820 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0, | ||
821 | 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
822 | 0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, | ||
823 | 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe, | ||
824 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8, | ||
825 | 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
826 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00, | ||
827 | 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
828 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66, | ||
829 | 0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, | ||
830 | 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00, | ||
831 | 0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee, | ||
832 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66, | ||
833 | 0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
834 | 0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
835 | 0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0, | ||
836 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60, | ||
837 | 0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, | ||
838 | 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, | ||
839 | 0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, | ||
840 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, | ||
841 | 0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, | ||
842 | 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, | ||
843 | 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e, | ||
844 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18, | ||
845 | 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, | ||
846 | 0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, | ||
847 | 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00, | ||
848 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00, | ||
849 | 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c, | ||
850 | 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
851 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, | ||
852 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
853 | 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c, | ||
854 | 0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00, | ||
855 | 0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
856 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00, | ||
857 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
858 | 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
859 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
860 | 0x00, 0x00, 0x00, 0x00, | ||
861 | }; | ||
diff --git a/arch/ppc/syslib/cpc700.h b/arch/ppc/syslib/cpc700.h new file mode 100644 index 000000000000..f2c002531019 --- /dev/null +++ b/arch/ppc/syslib/cpc700.h | |||
@@ -0,0 +1,98 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/cpc700.h | ||
3 | * | ||
4 | * Header file for IBM CPC700 Host Bridge, et. al. | ||
5 | * | ||
6 | * Author: Mark A. Greer | ||
7 | * mgreer@mvista.com | ||
8 | * | ||
9 | * 2000-2002 (c) MontaVista, Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | |||
15 | /* | ||
16 | * This file contains the defines and macros for the IBM CPC700 host bridge, | ||
17 | * memory controller, PIC, UARTs, IIC, and Timers. | ||
18 | */ | ||
19 | |||
20 | #ifndef __PPC_SYSLIB_CPC700_H__ | ||
21 | #define __PPC_SYSLIB_CPC700_H__ | ||
22 | |||
23 | #include <linux/stddef.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/init.h> | ||
26 | |||
27 | /* XXX no barriers? not even any volatiles? -- paulus */ | ||
28 | #define CPC700_OUT_32(a,d) (*(u_int *)a = d) | ||
29 | #define CPC700_IN_32(a) (*(u_int *)a) | ||
30 | |||
31 | /* | ||
32 | * PCI Section | ||
33 | */ | ||
34 | #define CPC700_PCI_CONFIG_ADDR 0xfec00000 | ||
35 | #define CPC700_PCI_CONFIG_DATA 0xfec00004 | ||
36 | |||
37 | /* CPU -> PCI memory window 0 */ | ||
38 | #define CPC700_PMM0_LOCAL 0xff400000 /* CPU physical addr */ | ||
39 | #define CPC700_PMM0_MASK_ATTR 0xff400004 /* size and attrs */ | ||
40 | #define CPC700_PMM0_PCI_LOW 0xff400008 /* PCI addr, low word */ | ||
41 | #define CPC700_PMM0_PCI_HIGH 0xff40000c /* PCI addr, high wd */ | ||
42 | /* CPU -> PCI memory window 1 */ | ||
43 | #define CPC700_PMM1_LOCAL 0xff400010 | ||
44 | #define CPC700_PMM1_MASK_ATTR 0xff400014 | ||
45 | #define CPC700_PMM1_PCI_LOW 0xff400018 | ||
46 | #define CPC700_PMM1_PCI_HIGH 0xff40001c | ||
47 | /* CPU -> PCI memory window 2 */ | ||
48 | #define CPC700_PMM2_LOCAL 0xff400020 | ||
49 | #define CPC700_PMM2_MASK_ATTR 0xff400024 | ||
50 | #define CPC700_PMM2_PCI_LOW 0xff400028 | ||
51 | #define CPC700_PMM2_PCI_HIGH 0xff40002c | ||
52 | /* PCI memory -> CPU window 1 */ | ||
53 | #define CPC700_PTM1_MEMSIZE 0xff400030 /* window size */ | ||
54 | #define CPC700_PTM1_LOCAL 0xff400034 /* CPU phys addr */ | ||
55 | /* PCI memory -> CPU window 2 */ | ||
56 | #define CPC700_PTM2_MEMSIZE 0xff400038 /* size and enable */ | ||
57 | #define CPC700_PTM2_LOCAL 0xff40003c | ||
58 | |||
59 | /* | ||
60 | * PIC Section | ||
61 | * | ||
62 | * IBM calls the CPC700's programmable interrupt controller the Universal | ||
63 | * Interrupt Controller or UIC. | ||
64 | */ | ||
65 | |||
66 | /* | ||
67 | * UIC Register Addresses. | ||
68 | */ | ||
69 | #define CPC700_UIC_UICSR 0xff500880 /* Status Reg (Rd/Clr)*/ | ||
70 | #define CPC700_UIC_UICSRS 0xff500884 /* Status Reg (Set) */ | ||
71 | #define CPC700_UIC_UICER 0xff500888 /* Enable Reg */ | ||
72 | #define CPC700_UIC_UICCR 0xff50088c /* Critical Reg */ | ||
73 | #define CPC700_UIC_UICPR 0xff500890 /* Polarity Reg */ | ||
74 | #define CPC700_UIC_UICTR 0xff500894 /* Trigger Reg */ | ||
75 | #define CPC700_UIC_UICMSR 0xff500898 /* Masked Status Reg */ | ||
76 | #define CPC700_UIC_UICVR 0xff50089c /* Vector Reg */ | ||
77 | #define CPC700_UIC_UICVCR 0xff5008a0 /* Vector Config Reg */ | ||
78 | |||
79 | #define CPC700_UIC_UICER_ENABLE 0x00000001 /* Enable an IRQ */ | ||
80 | |||
81 | #define CPC700_UIC_UICVCR_31_HI 0x00000000 /* IRQ 31 hi priority */ | ||
82 | #define CPC700_UIC_UICVCR_0_HI 0x00000001 /* IRQ 0 hi priority */ | ||
83 | #define CPC700_UIC_UICVCR_BASE_MASK 0xfffffffc | ||
84 | #define CPC700_UIC_UICVCR_ORDER_MASK 0x00000001 | ||
85 | |||
86 | /* Specify value of a bit for an IRQ. */ | ||
87 | #define CPC700_UIC_IRQ_BIT(i) ((0x00000001) << (31 - (i))) | ||
88 | |||
89 | /* | ||
90 | * UIC Exports... | ||
91 | */ | ||
92 | extern struct hw_interrupt_type cpc700_pic; | ||
93 | extern unsigned int cpc700_irq_assigns[32][2]; | ||
94 | |||
95 | extern void __init cpc700_init_IRQ(void); | ||
96 | extern int cpc700_get_irq(struct pt_regs *); | ||
97 | |||
98 | #endif /* __PPC_SYSLIB_CPC700_H__ */ | ||
diff --git a/arch/ppc/syslib/cpc700_pic.c b/arch/ppc/syslib/cpc700_pic.c new file mode 100644 index 000000000000..774709807538 --- /dev/null +++ b/arch/ppc/syslib/cpc700_pic.c | |||
@@ -0,0 +1,187 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/cpc700_pic.c | ||
3 | * | ||
4 | * Interrupt controller support for IBM Spruce | ||
5 | * | ||
6 | * Authors: Mark Greer, Matt Porter, and Johnnie Peters | ||
7 | * mgreer@mvista.com | ||
8 | * mporter@mvista.com | ||
9 | * jpeters@mvista.com | ||
10 | * | ||
11 | * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under | ||
12 | * the terms of the GNU General Public License version 2. This program | ||
13 | * is licensed "as is" without any warranty of any kind, whether express | ||
14 | * or implied. | ||
15 | */ | ||
16 | |||
17 | #include <linux/stddef.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/signal.h> | ||
21 | #include <linux/irq.h> | ||
22 | |||
23 | #include <asm/io.h> | ||
24 | #include <asm/system.h> | ||
25 | #include <asm/irq.h> | ||
26 | |||
27 | #include "cpc700.h" | ||
28 | |||
29 | static void | ||
30 | cpc700_unmask_irq(unsigned int irq) | ||
31 | { | ||
32 | unsigned int tr_bits; | ||
33 | |||
34 | /* | ||
35 | * IRQ 31 is largest IRQ supported. | ||
36 | * IRQs 17-19 are reserved. | ||
37 | */ | ||
38 | if ((irq <= 31) && ((irq < 17) || (irq > 19))) { | ||
39 | tr_bits = CPC700_IN_32(CPC700_UIC_UICTR); | ||
40 | |||
41 | if ((tr_bits & (1 << (31 - irq))) == 0) { | ||
42 | /* level trigger interrupt, clear bit in status | ||
43 | * register */ | ||
44 | CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq)); | ||
45 | } | ||
46 | |||
47 | /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ | ||
48 | ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq); | ||
49 | |||
50 | CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); | ||
51 | } | ||
52 | return; | ||
53 | } | ||
54 | |||
55 | static void | ||
56 | cpc700_mask_irq(unsigned int irq) | ||
57 | { | ||
58 | /* | ||
59 | * IRQ 31 is largest IRQ supported. | ||
60 | * IRQs 17-19 are reserved. | ||
61 | */ | ||
62 | if ((irq <= 31) && ((irq < 17) || (irq > 19))) { | ||
63 | /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ | ||
64 | ppc_cached_irq_mask[0] &= | ||
65 | ~CPC700_UIC_IRQ_BIT(irq); | ||
66 | |||
67 | CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); | ||
68 | } | ||
69 | return; | ||
70 | } | ||
71 | |||
72 | static void | ||
73 | cpc700_mask_and_ack_irq(unsigned int irq) | ||
74 | { | ||
75 | u_int bit; | ||
76 | |||
77 | /* | ||
78 | * IRQ 31 is largest IRQ supported. | ||
79 | * IRQs 17-19 are reserved. | ||
80 | */ | ||
81 | if ((irq <= 31) && ((irq < 17) || (irq > 19))) { | ||
82 | /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */ | ||
83 | bit = CPC700_UIC_IRQ_BIT(irq); | ||
84 | |||
85 | ppc_cached_irq_mask[0] &= ~bit; | ||
86 | CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]); | ||
87 | CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */ | ||
88 | } | ||
89 | return; | ||
90 | } | ||
91 | |||
92 | static struct hw_interrupt_type cpc700_pic = { | ||
93 | "CPC700 PIC", | ||
94 | NULL, | ||
95 | NULL, | ||
96 | cpc700_unmask_irq, | ||
97 | cpc700_mask_irq, | ||
98 | cpc700_mask_and_ack_irq, | ||
99 | NULL, | ||
100 | NULL | ||
101 | }; | ||
102 | |||
103 | __init static void | ||
104 | cpc700_pic_init_irq(unsigned int irq) | ||
105 | { | ||
106 | unsigned int tmp; | ||
107 | |||
108 | /* Set interrupt sense */ | ||
109 | tmp = CPC700_IN_32(CPC700_UIC_UICTR); | ||
110 | if (cpc700_irq_assigns[irq][0] == 0) { | ||
111 | tmp &= ~CPC700_UIC_IRQ_BIT(irq); | ||
112 | } else { | ||
113 | tmp |= CPC700_UIC_IRQ_BIT(irq); | ||
114 | } | ||
115 | CPC700_OUT_32(CPC700_UIC_UICTR, tmp); | ||
116 | |||
117 | /* Set interrupt polarity */ | ||
118 | tmp = CPC700_IN_32(CPC700_UIC_UICPR); | ||
119 | if (cpc700_irq_assigns[irq][1]) { | ||
120 | tmp |= CPC700_UIC_IRQ_BIT(irq); | ||
121 | } else { | ||
122 | tmp &= ~CPC700_UIC_IRQ_BIT(irq); | ||
123 | } | ||
124 | CPC700_OUT_32(CPC700_UIC_UICPR, tmp); | ||
125 | |||
126 | /* Set interrupt critical */ | ||
127 | tmp = CPC700_IN_32(CPC700_UIC_UICCR); | ||
128 | tmp |= CPC700_UIC_IRQ_BIT(irq); | ||
129 | CPC700_OUT_32(CPC700_UIC_UICCR, tmp); | ||
130 | |||
131 | return; | ||
132 | } | ||
133 | |||
134 | __init void | ||
135 | cpc700_init_IRQ(void) | ||
136 | { | ||
137 | int i; | ||
138 | |||
139 | ppc_cached_irq_mask[0] = 0; | ||
140 | CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000); /* Disable all irq's */ | ||
141 | CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff); /* Clear cur intrs */ | ||
142 | CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff); /* Gen INT not MCP */ | ||
143 | CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000); /* Active low */ | ||
144 | CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000); /* Level Sensitive */ | ||
145 | CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI); | ||
146 | /* IRQ 0 is highest */ | ||
147 | |||
148 | for (i = 0; i < 17; i++) { | ||
149 | irq_desc[i].handler = &cpc700_pic; | ||
150 | cpc700_pic_init_irq(i); | ||
151 | } | ||
152 | |||
153 | for (i = 20; i < 32; i++) { | ||
154 | irq_desc[i].handler = &cpc700_pic; | ||
155 | cpc700_pic_init_irq(i); | ||
156 | } | ||
157 | |||
158 | return; | ||
159 | } | ||
160 | |||
161 | |||
162 | |||
163 | /* | ||
164 | * Find the highest IRQ that generating an interrupt, if any. | ||
165 | */ | ||
166 | int | ||
167 | cpc700_get_irq(struct pt_regs *regs) | ||
168 | { | ||
169 | int irq = 0; | ||
170 | u_int irq_status, irq_test = 1; | ||
171 | |||
172 | irq_status = CPC700_IN_32(CPC700_UIC_UICMSR); | ||
173 | |||
174 | do | ||
175 | { | ||
176 | if (irq_status & irq_test) | ||
177 | break; | ||
178 | irq++; | ||
179 | irq_test <<= 1; | ||
180 | } while (irq < NR_IRQS); | ||
181 | |||
182 | |||
183 | if (irq == NR_IRQS) | ||
184 | irq = 33; | ||
185 | |||
186 | return (31 - irq); | ||
187 | } | ||
diff --git a/arch/ppc/syslib/cpc710.h b/arch/ppc/syslib/cpc710.h new file mode 100644 index 000000000000..cc0afd804029 --- /dev/null +++ b/arch/ppc/syslib/cpc710.h | |||
@@ -0,0 +1,83 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/cpc710.h | ||
3 | * | ||
4 | * Definitions for the IBM CPC710 PCI Host Bridge | ||
5 | * | ||
6 | * Author: Matt Porter <mporter@mvista.com> | ||
7 | * | ||
8 | * 2001 (c) MontaVista, Software, Inc. This file is licensed under | ||
9 | * the terms of the GNU General Public License version 2. This program | ||
10 | * is licensed "as is" without any warranty of any kind, whether express | ||
11 | * or implied. | ||
12 | */ | ||
13 | |||
14 | #ifndef __PPC_PLATFORMS_CPC710_H | ||
15 | #define __PPC_PLATFORMS_CPC710_H | ||
16 | |||
17 | /* General bridge and memory controller registers */ | ||
18 | #define PIDR 0xff000008 | ||
19 | #define CNFR 0xff00000c | ||
20 | #define RSTR 0xff000010 | ||
21 | #define UCTL 0xff001000 | ||
22 | #define MPSR 0xff001010 | ||
23 | #define SIOC 0xff001020 | ||
24 | #define ABCNTL 0xff001030 | ||
25 | #define SRST 0xff001040 | ||
26 | #define ERRC 0xff001050 | ||
27 | #define SESR 0xff001060 | ||
28 | #define SEAR 0xff001070 | ||
29 | #define SIOC1 0xff001090 | ||
30 | #define PGCHP 0xff001100 | ||
31 | #define GPDIR 0xff001130 | ||
32 | #define GPOUT 0xff001150 | ||
33 | #define ATAS 0xff001160 | ||
34 | #define AVDG 0xff001170 | ||
35 | #define MCCR 0xff001200 | ||
36 | #define MESR 0xff001220 | ||
37 | #define MEAR 0xff001230 | ||
38 | #define MCER0 0xff001300 | ||
39 | #define MCER1 0xff001310 | ||
40 | #define MCER2 0xff001320 | ||
41 | #define MCER3 0xff001330 | ||
42 | #define MCER4 0xff001340 | ||
43 | #define MCER5 0xff001350 | ||
44 | #define MCER6 0xff001360 | ||
45 | #define MCER7 0xff001370 | ||
46 | |||
47 | /* | ||
48 | * PCI32/64 configuration registers | ||
49 | * Given as offsets from their | ||
50 | * respective physical segment BAR | ||
51 | */ | ||
52 | #define PIBAR 0x000f7800 | ||
53 | #define PMBAR 0x000f7810 | ||
54 | #define MSIZE 0x000f7f40 | ||
55 | #define IOSIZE 0x000f7f60 | ||
56 | #define SMBAR 0x000f7f80 | ||
57 | #define SIBAR 0x000f7fc0 | ||
58 | #define PSSIZE 0x000f8100 | ||
59 | #define PPSIZE 0x000f8110 | ||
60 | #define BARPS 0x000f8120 | ||
61 | #define BARPP 0x000f8130 | ||
62 | #define PSBAR 0x000f8140 | ||
63 | #define PPBAR 0x000f8150 | ||
64 | #define BPMDLK 0x000f8200 /* Bottom of Peripheral Memory Space */ | ||
65 | #define TPMDLK 0x000f8210 /* Top of Peripheral Memory Space */ | ||
66 | #define BIODLK 0x000f8220 /* Bottom of Peripheral I/O Space */ | ||
67 | #define TIODLK 0x000f8230 /* Top of Perioheral I/O Space */ | ||
68 | #define DLKCTRL 0x000f8240 /* Deadlock control */ | ||
69 | #define DLKDEV 0x000f8250 /* Deadlock device */ | ||
70 | |||
71 | /* System standard configuration registers space */ | ||
72 | #define DCR 0xff200000 | ||
73 | #define DID 0xff200004 | ||
74 | #define BAR 0xff200018 | ||
75 | |||
76 | /* Device specific configuration space */ | ||
77 | #define PCIENB 0xff201000 | ||
78 | |||
79 | /* Configuration space registers */ | ||
80 | #define CPC710_BUS_NUMBER 0x40 | ||
81 | #define CPC710_SUB_BUS_NUMBER 0x41 | ||
82 | |||
83 | #endif /* __PPC_PLATFORMS_CPC710_H */ | ||
diff --git a/arch/ppc/syslib/cpm2_common.c b/arch/ppc/syslib/cpm2_common.c new file mode 100644 index 000000000000..ea5e77080e8d --- /dev/null +++ b/arch/ppc/syslib/cpm2_common.c | |||
@@ -0,0 +1,198 @@ | |||
1 | /* | ||
2 | * General Purpose functions for the global management of the | ||
3 | * 8260 Communication Processor Module. | ||
4 | * Copyright (c) 1999 Dan Malek (dmalek@jlc.net) | ||
5 | * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com) | ||
6 | * 2.3.99 Updates | ||
7 | * | ||
8 | * In addition to the individual control of the communication | ||
9 | * channels, there are a few functions that globally affect the | ||
10 | * communication processor. | ||
11 | * | ||
12 | * Buffer descriptors must be allocated from the dual ported memory | ||
13 | * space. The allocator for that is here. When the communication | ||
14 | * process is reset, we reclaim the memory available. There is | ||
15 | * currently no deallocator for this memory. | ||
16 | */ | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/param.h> | ||
21 | #include <linux/string.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/bootmem.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <asm/irq.h> | ||
27 | #include <asm/mpc8260.h> | ||
28 | #include <asm/page.h> | ||
29 | #include <asm/pgtable.h> | ||
30 | #include <asm/immap_cpm2.h> | ||
31 | #include <asm/cpm2.h> | ||
32 | #include <asm/rheap.h> | ||
33 | |||
34 | static void cpm2_dpinit(void); | ||
35 | cpm_cpm2_t *cpmp; /* Pointer to comm processor space */ | ||
36 | |||
37 | /* We allocate this here because it is used almost exclusively for | ||
38 | * the communication processor devices. | ||
39 | */ | ||
40 | cpm2_map_t *cpm2_immr; | ||
41 | |||
42 | #define CPM_MAP_SIZE (0x40000) /* 256k - the PQ3 reserve this amount | ||
43 | of space for CPM as it is larger | ||
44 | than on PQ2 */ | ||
45 | |||
46 | void | ||
47 | cpm2_reset(void) | ||
48 | { | ||
49 | cpm2_immr = (cpm2_map_t *)ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE); | ||
50 | |||
51 | /* Reclaim the DP memory for our use. | ||
52 | */ | ||
53 | cpm2_dpinit(); | ||
54 | |||
55 | /* Tell everyone where the comm processor resides. | ||
56 | */ | ||
57 | cpmp = &cpm2_immr->im_cpm; | ||
58 | } | ||
59 | |||
60 | /* Set a baud rate generator. This needs lots of work. There are | ||
61 | * eight BRGs, which can be connected to the CPM channels or output | ||
62 | * as clocks. The BRGs are in two different block of internal | ||
63 | * memory mapped space. | ||
64 | * The baud rate clock is the system clock divided by something. | ||
65 | * It was set up long ago during the initial boot phase and is | ||
66 | * is given to us. | ||
67 | * Baud rate clocks are zero-based in the driver code (as that maps | ||
68 | * to port numbers). Documentation uses 1-based numbering. | ||
69 | */ | ||
70 | #define BRG_INT_CLK (((bd_t *)__res)->bi_brgfreq) | ||
71 | #define BRG_UART_CLK (BRG_INT_CLK/16) | ||
72 | |||
73 | /* This function is used by UARTS, or anything else that uses a 16x | ||
74 | * oversampled clock. | ||
75 | */ | ||
76 | void | ||
77 | cpm_setbrg(uint brg, uint rate) | ||
78 | { | ||
79 | volatile uint *bp; | ||
80 | |||
81 | /* This is good enough to get SMCs running..... | ||
82 | */ | ||
83 | if (brg < 4) { | ||
84 | bp = (uint *)&cpm2_immr->im_brgc1; | ||
85 | } | ||
86 | else { | ||
87 | bp = (uint *)&cpm2_immr->im_brgc5; | ||
88 | brg -= 4; | ||
89 | } | ||
90 | bp += brg; | ||
91 | *bp = ((BRG_UART_CLK / rate) << 1) | CPM_BRG_EN; | ||
92 | } | ||
93 | |||
94 | /* This function is used to set high speed synchronous baud rate | ||
95 | * clocks. | ||
96 | */ | ||
97 | void | ||
98 | cpm2_fastbrg(uint brg, uint rate, int div16) | ||
99 | { | ||
100 | volatile uint *bp; | ||
101 | |||
102 | if (brg < 4) { | ||
103 | bp = (uint *)&cpm2_immr->im_brgc1; | ||
104 | } | ||
105 | else { | ||
106 | bp = (uint *)&cpm2_immr->im_brgc5; | ||
107 | brg -= 4; | ||
108 | } | ||
109 | bp += brg; | ||
110 | *bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN; | ||
111 | if (div16) | ||
112 | *bp |= CPM_BRG_DIV16; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * dpalloc / dpfree bits. | ||
117 | */ | ||
118 | static spinlock_t cpm_dpmem_lock; | ||
119 | /* 16 blocks should be enough to satisfy all requests | ||
120 | * until the memory subsystem goes up... */ | ||
121 | static rh_block_t cpm_boot_dpmem_rh_block[16]; | ||
122 | static rh_info_t cpm_dpmem_info; | ||
123 | |||
124 | static void cpm2_dpinit(void) | ||
125 | { | ||
126 | spin_lock_init(&cpm_dpmem_lock); | ||
127 | |||
128 | /* initialize the info header */ | ||
129 | rh_init(&cpm_dpmem_info, 1, | ||
130 | sizeof(cpm_boot_dpmem_rh_block) / | ||
131 | sizeof(cpm_boot_dpmem_rh_block[0]), | ||
132 | cpm_boot_dpmem_rh_block); | ||
133 | |||
134 | /* Attach the usable dpmem area */ | ||
135 | /* XXX: This is actually crap. CPM_DATAONLY_BASE and | ||
136 | * CPM_DATAONLY_SIZE is only a subset of the available dpram. It | ||
137 | * varies with the processor and the microcode patches activated. | ||
138 | * But the following should be at least safe. | ||
139 | */ | ||
140 | rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE, | ||
141 | CPM_DATAONLY_SIZE); | ||
142 | } | ||
143 | |||
144 | /* This function returns an index into the DPRAM area. | ||
145 | */ | ||
146 | uint cpm_dpalloc(uint size, uint align) | ||
147 | { | ||
148 | void *start; | ||
149 | unsigned long flags; | ||
150 | |||
151 | spin_lock_irqsave(&cpm_dpmem_lock, flags); | ||
152 | cpm_dpmem_info.alignment = align; | ||
153 | start = rh_alloc(&cpm_dpmem_info, size, "commproc"); | ||
154 | spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | ||
155 | |||
156 | return (uint)start; | ||
157 | } | ||
158 | EXPORT_SYMBOL(cpm_dpalloc); | ||
159 | |||
160 | int cpm_dpfree(uint offset) | ||
161 | { | ||
162 | int ret; | ||
163 | unsigned long flags; | ||
164 | |||
165 | spin_lock_irqsave(&cpm_dpmem_lock, flags); | ||
166 | ret = rh_free(&cpm_dpmem_info, (void *)offset); | ||
167 | spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | ||
168 | |||
169 | return ret; | ||
170 | } | ||
171 | EXPORT_SYMBOL(cpm_dpfree); | ||
172 | |||
173 | /* not sure if this is ever needed */ | ||
174 | uint cpm_dpalloc_fixed(uint offset, uint size, uint align) | ||
175 | { | ||
176 | void *start; | ||
177 | unsigned long flags; | ||
178 | |||
179 | spin_lock_irqsave(&cpm_dpmem_lock, flags); | ||
180 | cpm_dpmem_info.alignment = align; | ||
181 | start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc"); | ||
182 | spin_unlock_irqrestore(&cpm_dpmem_lock, flags); | ||
183 | |||
184 | return (uint)start; | ||
185 | } | ||
186 | EXPORT_SYMBOL(cpm_dpalloc_fixed); | ||
187 | |||
188 | void cpm_dpdump(void) | ||
189 | { | ||
190 | rh_dump(&cpm_dpmem_info); | ||
191 | } | ||
192 | EXPORT_SYMBOL(cpm_dpdump); | ||
193 | |||
194 | void *cpm_dpram_addr(uint offset) | ||
195 | { | ||
196 | return (void *)&cpm2_immr->im_dprambase[offset]; | ||
197 | } | ||
198 | EXPORT_SYMBOL(cpm_dpram_addr); | ||
diff --git a/arch/ppc/syslib/cpm2_pic.c b/arch/ppc/syslib/cpm2_pic.c new file mode 100644 index 000000000000..954b07fc1df3 --- /dev/null +++ b/arch/ppc/syslib/cpm2_pic.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* The CPM2 internal interrupt controller. It is usually | ||
2 | * the only interrupt controller. | ||
3 | * There are two 32-bit registers (high/low) for up to 64 | ||
4 | * possible interrupts. | ||
5 | * | ||
6 | * Now, the fun starts.....Interrupt Numbers DO NOT MAP | ||
7 | * in a simple arithmetic fashion to mask or pending registers. | ||
8 | * That is, interrupt 4 does not map to bit position 4. | ||
9 | * We create two tables, indexed by vector number, to indicate | ||
10 | * which register to use and which bit in the register to use. | ||
11 | */ | ||
12 | |||
13 | #include <linux/stddef.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/signal.h> | ||
17 | #include <linux/irq.h> | ||
18 | |||
19 | #include <asm/immap_cpm2.h> | ||
20 | #include <asm/mpc8260.h> | ||
21 | |||
22 | #include "cpm2_pic.h" | ||
23 | |||
24 | static u_char irq_to_siureg[] = { | ||
25 | 1, 1, 1, 1, 1, 1, 1, 1, | ||
26 | 1, 1, 1, 1, 1, 1, 1, 1, | ||
27 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
28 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
29 | 1, 1, 1, 1, 1, 1, 1, 1, | ||
30 | 1, 1, 1, 1, 1, 1, 1, 1, | ||
31 | 0, 0, 0, 0, 0, 0, 0, 0, | ||
32 | 0, 0, 0, 0, 0, 0, 0, 0 | ||
33 | }; | ||
34 | |||
35 | /* bit numbers do not match the docs, these are precomputed so the bit for | ||
36 | * a given irq is (1 << irq_to_siubit[irq]) */ | ||
37 | static u_char irq_to_siubit[] = { | ||
38 | 0, 15, 14, 13, 12, 11, 10, 9, | ||
39 | 8, 7, 6, 5, 4, 3, 2, 1, | ||
40 | 2, 1, 15, 14, 13, 12, 11, 10, | ||
41 | 9, 8, 7, 6, 5, 4, 3, 0, | ||
42 | 31, 30, 29, 28, 27, 26, 25, 24, | ||
43 | 23, 22, 21, 20, 19, 18, 17, 16, | ||
44 | 16, 17, 18, 19, 20, 21, 22, 23, | ||
45 | 24, 25, 26, 27, 28, 29, 30, 31, | ||
46 | }; | ||
47 | |||
48 | static void cpm2_mask_irq(unsigned int irq_nr) | ||
49 | { | ||
50 | int bit, word; | ||
51 | volatile uint *simr; | ||
52 | |||
53 | irq_nr -= CPM_IRQ_OFFSET; | ||
54 | |||
55 | bit = irq_to_siubit[irq_nr]; | ||
56 | word = irq_to_siureg[irq_nr]; | ||
57 | |||
58 | simr = &(cpm2_immr->im_intctl.ic_simrh); | ||
59 | ppc_cached_irq_mask[word] &= ~(1 << bit); | ||
60 | simr[word] = ppc_cached_irq_mask[word]; | ||
61 | } | ||
62 | |||
63 | static void cpm2_unmask_irq(unsigned int irq_nr) | ||
64 | { | ||
65 | int bit, word; | ||
66 | volatile uint *simr; | ||
67 | |||
68 | irq_nr -= CPM_IRQ_OFFSET; | ||
69 | |||
70 | bit = irq_to_siubit[irq_nr]; | ||
71 | word = irq_to_siureg[irq_nr]; | ||
72 | |||
73 | simr = &(cpm2_immr->im_intctl.ic_simrh); | ||
74 | ppc_cached_irq_mask[word] |= 1 << bit; | ||
75 | simr[word] = ppc_cached_irq_mask[word]; | ||
76 | } | ||
77 | |||
78 | static void cpm2_mask_and_ack(unsigned int irq_nr) | ||
79 | { | ||
80 | int bit, word; | ||
81 | volatile uint *simr, *sipnr; | ||
82 | |||
83 | irq_nr -= CPM_IRQ_OFFSET; | ||
84 | |||
85 | bit = irq_to_siubit[irq_nr]; | ||
86 | word = irq_to_siureg[irq_nr]; | ||
87 | |||
88 | simr = &(cpm2_immr->im_intctl.ic_simrh); | ||
89 | sipnr = &(cpm2_immr->im_intctl.ic_sipnrh); | ||
90 | ppc_cached_irq_mask[word] &= ~(1 << bit); | ||
91 | simr[word] = ppc_cached_irq_mask[word]; | ||
92 | sipnr[word] = 1 << bit; | ||
93 | } | ||
94 | |||
95 | static void cpm2_end_irq(unsigned int irq_nr) | ||
96 | { | ||
97 | int bit, word; | ||
98 | volatile uint *simr; | ||
99 | |||
100 | if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) | ||
101 | && irq_desc[irq_nr].action) { | ||
102 | |||
103 | irq_nr -= CPM_IRQ_OFFSET; | ||
104 | bit = irq_to_siubit[irq_nr]; | ||
105 | word = irq_to_siureg[irq_nr]; | ||
106 | |||
107 | simr = &(cpm2_immr->im_intctl.ic_simrh); | ||
108 | ppc_cached_irq_mask[word] |= 1 << bit; | ||
109 | simr[word] = ppc_cached_irq_mask[word]; | ||
110 | } | ||
111 | } | ||
112 | |||
113 | static struct hw_interrupt_type cpm2_pic = { | ||
114 | .typename = " CPM2 SIU ", | ||
115 | .enable = cpm2_unmask_irq, | ||
116 | .disable = cpm2_mask_irq, | ||
117 | .ack = cpm2_mask_and_ack, | ||
118 | .end = cpm2_end_irq, | ||
119 | }; | ||
120 | |||
121 | int cpm2_get_irq(struct pt_regs *regs) | ||
122 | { | ||
123 | int irq; | ||
124 | unsigned long bits; | ||
125 | |||
126 | /* For CPM2, read the SIVEC register and shift the bits down | ||
127 | * to get the irq number. */ | ||
128 | bits = cpm2_immr->im_intctl.ic_sivec; | ||
129 | irq = bits >> 26; | ||
130 | |||
131 | if (irq == 0) | ||
132 | return(-1); | ||
133 | return irq+CPM_IRQ_OFFSET; | ||
134 | } | ||
135 | |||
136 | void cpm2_init_IRQ(void) | ||
137 | { | ||
138 | int i; | ||
139 | |||
140 | /* Clear the CPM IRQ controller, in case it has any bits set | ||
141 | * from the bootloader | ||
142 | */ | ||
143 | |||
144 | /* Mask out everything */ | ||
145 | cpm2_immr->im_intctl.ic_simrh = 0x00000000; | ||
146 | cpm2_immr->im_intctl.ic_simrl = 0x00000000; | ||
147 | wmb(); | ||
148 | |||
149 | /* Ack everything */ | ||
150 | cpm2_immr->im_intctl.ic_sipnrh = 0xffffffff; | ||
151 | cpm2_immr->im_intctl.ic_sipnrl = 0xffffffff; | ||
152 | wmb(); | ||
153 | |||
154 | /* Dummy read of the vector */ | ||
155 | i = cpm2_immr->im_intctl.ic_sivec; | ||
156 | rmb(); | ||
157 | |||
158 | /* Initialize the default interrupt mapping priorities, | ||
159 | * in case the boot rom changed something on us. | ||
160 | */ | ||
161 | cpm2_immr->im_intctl.ic_sicr = 0; | ||
162 | cpm2_immr->im_intctl.ic_scprrh = 0x05309770; | ||
163 | cpm2_immr->im_intctl.ic_scprrl = 0x05309770; | ||
164 | |||
165 | |||
166 | /* Enable chaining to OpenPIC, and make everything level | ||
167 | */ | ||
168 | for (i = 0; i < NR_CPM_INTS; i++) { | ||
169 | irq_desc[i+CPM_IRQ_OFFSET].handler = &cpm2_pic; | ||
170 | irq_desc[i+CPM_IRQ_OFFSET].status |= IRQ_LEVEL; | ||
171 | } | ||
172 | } | ||
diff --git a/arch/ppc/syslib/cpm2_pic.h b/arch/ppc/syslib/cpm2_pic.h new file mode 100644 index 000000000000..97cab8f13a1a --- /dev/null +++ b/arch/ppc/syslib/cpm2_pic.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _PPC_KERNEL_CPM2_H | ||
2 | #define _PPC_KERNEL_CPM2_H | ||
3 | |||
4 | extern int cpm2_get_irq(struct pt_regs *regs); | ||
5 | |||
6 | extern void cpm2_init_IRQ(void); | ||
7 | |||
8 | #endif /* _PPC_KERNEL_CPM2_H */ | ||
diff --git a/arch/ppc/syslib/dcr.S b/arch/ppc/syslib/dcr.S new file mode 100644 index 000000000000..895f10243a43 --- /dev/null +++ b/arch/ppc/syslib/dcr.S | |||
@@ -0,0 +1,41 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/dcr.S | ||
3 | * | ||
4 | * "Indirect" DCR access | ||
5 | * | ||
6 | * Copyright (c) 2004 Eugene Surovegin <ebs@ebshome.net> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <asm/ppc_asm.h> | ||
15 | #include <asm/processor.h> | ||
16 | |||
17 | #define DCR_ACCESS_PROLOG(table) \ | ||
18 | rlwinm r3,r3,4,18,27; \ | ||
19 | lis r5,table@h; \ | ||
20 | ori r5,r5,table@l; \ | ||
21 | add r3,r3,r5; \ | ||
22 | mtctr r3; \ | ||
23 | bctr | ||
24 | |||
25 | _GLOBAL(__mfdcr) | ||
26 | DCR_ACCESS_PROLOG(__mfdcr_table) | ||
27 | |||
28 | _GLOBAL(__mtdcr) | ||
29 | DCR_ACCESS_PROLOG(__mtdcr_table) | ||
30 | |||
31 | __mfdcr_table: | ||
32 | mfdcr r3,0; blr | ||
33 | __mtdcr_table: | ||
34 | mtdcr 0,r4; blr | ||
35 | |||
36 | dcr = 1 | ||
37 | .rept 1023 | ||
38 | mfdcr r3,dcr; blr | ||
39 | mtdcr dcr,r4; blr | ||
40 | dcr = dcr + 1 | ||
41 | .endr | ||
diff --git a/arch/ppc/syslib/gen550.h b/arch/ppc/syslib/gen550.h new file mode 100644 index 000000000000..039d249e19a8 --- /dev/null +++ b/arch/ppc/syslib/gen550.h | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/gen550.h | ||
3 | * | ||
4 | * gen550 prototypes | ||
5 | * | ||
6 | * Matt Porter <mporter@kernel.crashing.org> | ||
7 | * | ||
8 | * 2004 (c) MontaVista Software, Inc. This file is licensed under | ||
9 | * the terms of the GNU General Public License version 2. This program | ||
10 | * is licensed "as is" without any warranty of any kind, whether express | ||
11 | * or implied. | ||
12 | */ | ||
13 | |||
14 | extern void gen550_progress(char *, unsigned short); | ||
15 | extern void gen550_init(int, struct uart_port *); | ||
16 | extern void gen550_kgdb_map_scc(void); | ||
diff --git a/arch/ppc/syslib/gen550_dbg.c b/arch/ppc/syslib/gen550_dbg.c new file mode 100644 index 000000000000..9ef0113c83d1 --- /dev/null +++ b/arch/ppc/syslib/gen550_dbg.c | |||
@@ -0,0 +1,182 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/gen550_dbg.c | ||
3 | * | ||
4 | * A library of polled 16550 serial routines. These are intended to | ||
5 | * be used to support progress messages, xmon, kgdb, etc. on a | ||
6 | * variety of platforms. | ||
7 | * | ||
8 | * Adapted from lots of code ripped from the arch/ppc/boot/ polled | ||
9 | * 16550 support. | ||
10 | * | ||
11 | * Author: Matt Porter <mporter@mvista.com> | ||
12 | * | ||
13 | * 2002-2003 (c) MontaVista Software, Inc. This file is licensed under | ||
14 | * the terms of the GNU General Public License version 2. This program | ||
15 | * is licensed "as is" without any warranty of any kind, whether express | ||
16 | * or implied. | ||
17 | */ | ||
18 | |||
19 | #include <linux/config.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/serial.h> | ||
22 | #include <linux/tty.h> /* For linux/serial_core.h */ | ||
23 | #include <linux/serial_core.h> | ||
24 | #include <linux/serialP.h> | ||
25 | #include <linux/serial_reg.h> | ||
26 | #include <asm/machdep.h> | ||
27 | #include <asm/serial.h> | ||
28 | #include <asm/io.h> | ||
29 | |||
30 | #define SERIAL_BAUD 9600 | ||
31 | |||
32 | /* SERIAL_PORT_DFNS is defined in <asm/serial.h> */ | ||
33 | #ifndef SERIAL_PORT_DFNS | ||
34 | #define SERIAL_PORT_DFNS | ||
35 | #endif | ||
36 | |||
37 | static struct serial_state rs_table[RS_TABLE_SIZE] = { | ||
38 | SERIAL_PORT_DFNS /* defined in <asm/serial.h> */ | ||
39 | }; | ||
40 | |||
41 | static void (*serial_outb)(unsigned long, unsigned char); | ||
42 | static unsigned long (*serial_inb)(unsigned long); | ||
43 | |||
44 | static int shift; | ||
45 | |||
46 | unsigned long direct_inb(unsigned long addr) | ||
47 | { | ||
48 | return readb((void __iomem *)addr); | ||
49 | } | ||
50 | |||
51 | void direct_outb(unsigned long addr, unsigned char val) | ||
52 | { | ||
53 | writeb(val, (void __iomem *)addr); | ||
54 | } | ||
55 | |||
56 | unsigned long io_inb(unsigned long port) | ||
57 | { | ||
58 | return inb(port); | ||
59 | } | ||
60 | |||
61 | void io_outb(unsigned long port, unsigned char val) | ||
62 | { | ||
63 | outb(val, port); | ||
64 | } | ||
65 | |||
66 | unsigned long serial_init(int chan, void *ignored) | ||
67 | { | ||
68 | unsigned long com_port; | ||
69 | unsigned char lcr, dlm; | ||
70 | |||
71 | /* We need to find out which type io we're expecting. If it's | ||
72 | * 'SERIAL_IO_PORT', we get an offset from the isa_io_base. | ||
73 | * If it's 'SERIAL_IO_MEM', we can the exact location. -- Tom */ | ||
74 | switch (rs_table[chan].io_type) { | ||
75 | case SERIAL_IO_PORT: | ||
76 | com_port = rs_table[chan].port; | ||
77 | serial_outb = io_outb; | ||
78 | serial_inb = io_inb; | ||
79 | break; | ||
80 | case SERIAL_IO_MEM: | ||
81 | com_port = (unsigned long)rs_table[chan].iomem_base; | ||
82 | serial_outb = direct_outb; | ||
83 | serial_inb = direct_inb; | ||
84 | break; | ||
85 | default: | ||
86 | /* We can't deal with it. */ | ||
87 | return -1; | ||
88 | } | ||
89 | |||
90 | /* How far apart the registers are. */ | ||
91 | shift = rs_table[chan].iomem_reg_shift; | ||
92 | |||
93 | /* save the LCR */ | ||
94 | lcr = serial_inb(com_port + (UART_LCR << shift)); | ||
95 | |||
96 | /* Access baud rate */ | ||
97 | serial_outb(com_port + (UART_LCR << shift), UART_LCR_DLAB); | ||
98 | dlm = serial_inb(com_port + (UART_DLM << shift)); | ||
99 | |||
100 | /* | ||
101 | * Test if serial port is unconfigured | ||
102 | * We assume that no-one uses less than 110 baud or | ||
103 | * less than 7 bits per character these days. | ||
104 | * -- paulus. | ||
105 | */ | ||
106 | if ((dlm <= 4) && (lcr & 2)) { | ||
107 | /* port is configured, put the old LCR back */ | ||
108 | serial_outb(com_port + (UART_LCR << shift), lcr); | ||
109 | } | ||
110 | else { | ||
111 | /* Input clock. */ | ||
112 | serial_outb(com_port + (UART_DLL << shift), | ||
113 | (rs_table[chan].baud_base / SERIAL_BAUD) & 0xFF); | ||
114 | serial_outb(com_port + (UART_DLM << shift), | ||
115 | (rs_table[chan].baud_base / SERIAL_BAUD) >> 8); | ||
116 | /* 8 data, 1 stop, no parity */ | ||
117 | serial_outb(com_port + (UART_LCR << shift), 0x03); | ||
118 | /* RTS/DTR */ | ||
119 | serial_outb(com_port + (UART_MCR << shift), 0x03); | ||
120 | |||
121 | /* Clear & enable FIFOs */ | ||
122 | serial_outb(com_port + (UART_FCR << shift), 0x07); | ||
123 | } | ||
124 | |||
125 | return (com_port); | ||
126 | } | ||
127 | |||
128 | void | ||
129 | serial_putc(unsigned long com_port, unsigned char c) | ||
130 | { | ||
131 | while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_THRE) == 0) | ||
132 | ; | ||
133 | serial_outb(com_port, c); | ||
134 | } | ||
135 | |||
136 | unsigned char | ||
137 | serial_getc(unsigned long com_port) | ||
138 | { | ||
139 | while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) == 0) | ||
140 | ; | ||
141 | return serial_inb(com_port); | ||
142 | } | ||
143 | |||
144 | int | ||
145 | serial_tstc(unsigned long com_port) | ||
146 | { | ||
147 | return ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) != 0); | ||
148 | } | ||
149 | |||
150 | void | ||
151 | serial_close(unsigned long com_port) | ||
152 | { | ||
153 | } | ||
154 | |||
155 | void | ||
156 | gen550_init(int i, struct uart_port *serial_req) | ||
157 | { | ||
158 | rs_table[i].io_type = serial_req->iotype; | ||
159 | rs_table[i].port = serial_req->iobase; | ||
160 | rs_table[i].iomem_base = serial_req->membase; | ||
161 | rs_table[i].iomem_reg_shift = serial_req->regshift; | ||
162 | rs_table[i].baud_base = serial_req->uartclk ? serial_req->uartclk / 16 : BASE_BAUD; | ||
163 | } | ||
164 | |||
165 | #ifdef CONFIG_SERIAL_TEXT_DEBUG | ||
166 | void | ||
167 | gen550_progress(char *s, unsigned short hex) | ||
168 | { | ||
169 | volatile unsigned int progress_debugport; | ||
170 | volatile char c; | ||
171 | |||
172 | progress_debugport = serial_init(0, NULL); | ||
173 | |||
174 | serial_putc(progress_debugport, '\r'); | ||
175 | |||
176 | while ((c = *s++) != 0) | ||
177 | serial_putc(progress_debugport, c); | ||
178 | |||
179 | serial_putc(progress_debugport, '\n'); | ||
180 | serial_putc(progress_debugport, '\r'); | ||
181 | } | ||
182 | #endif /* CONFIG_SERIAL_TEXT_DEBUG */ | ||
diff --git a/arch/ppc/syslib/gen550_kgdb.c b/arch/ppc/syslib/gen550_kgdb.c new file mode 100644 index 000000000000..7239d5d7ddcd --- /dev/null +++ b/arch/ppc/syslib/gen550_kgdb.c | |||
@@ -0,0 +1,86 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/gen550_kgdb.c | ||
3 | * | ||
4 | * Generic 16550 kgdb support intended to be useful on a variety | ||
5 | * of platforms. To enable this support, it is necessary to set | ||
6 | * the CONFIG_GEN550 option. Any virtual mapping of the serial | ||
7 | * port(s) to be used can be accomplished by setting | ||
8 | * ppc_md.early_serial_map to a platform-specific mapping function. | ||
9 | * | ||
10 | * Adapted from ppc4xx_kgdb.c. | ||
11 | * | ||
12 | * Author: Matt Porter <mporter@kernel.crashing.org> | ||
13 | * | ||
14 | * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under | ||
15 | * the terms of the GNU General Public License version 2. This program | ||
16 | * is licensed "as is" without any warranty of any kind, whether express | ||
17 | * or implied. | ||
18 | */ | ||
19 | |||
20 | #include <linux/config.h> | ||
21 | #include <linux/types.h> | ||
22 | #include <linux/kernel.h> | ||
23 | |||
24 | #include <asm/machdep.h> | ||
25 | |||
26 | extern unsigned long serial_init(int, void *); | ||
27 | extern unsigned long serial_getc(unsigned long); | ||
28 | extern unsigned long serial_putc(unsigned long, unsigned char); | ||
29 | |||
30 | #if defined(CONFIG_KGDB_TTYS0) | ||
31 | #define KGDB_PORT 0 | ||
32 | #elif defined(CONFIG_KGDB_TTYS1) | ||
33 | #define KGDB_PORT 1 | ||
34 | #elif defined(CONFIG_KGDB_TTYS2) | ||
35 | #define KGDB_PORT 2 | ||
36 | #elif defined(CONFIG_KGDB_TTYS3) | ||
37 | #define KGDB_PORT 3 | ||
38 | #else | ||
39 | #error "invalid kgdb_tty port" | ||
40 | #endif | ||
41 | |||
42 | static volatile unsigned int kgdb_debugport; | ||
43 | |||
44 | void putDebugChar(unsigned char c) | ||
45 | { | ||
46 | if (kgdb_debugport == 0) | ||
47 | kgdb_debugport = serial_init(KGDB_PORT, NULL); | ||
48 | |||
49 | serial_putc(kgdb_debugport, c); | ||
50 | } | ||
51 | |||
52 | int getDebugChar(void) | ||
53 | { | ||
54 | if (kgdb_debugport == 0) | ||
55 | kgdb_debugport = serial_init(KGDB_PORT, NULL); | ||
56 | |||
57 | return(serial_getc(kgdb_debugport)); | ||
58 | } | ||
59 | |||
60 | void kgdb_interruptible(int enable) | ||
61 | { | ||
62 | return; | ||
63 | } | ||
64 | |||
65 | void putDebugString(char* str) | ||
66 | { | ||
67 | while (*str != '\0') { | ||
68 | putDebugChar(*str); | ||
69 | str++; | ||
70 | } | ||
71 | putDebugChar('\r'); | ||
72 | return; | ||
73 | } | ||
74 | |||
75 | /* | ||
76 | * Note: gen550_init() must be called already on the port we are going | ||
77 | * to use. | ||
78 | */ | ||
79 | void | ||
80 | gen550_kgdb_map_scc(void) | ||
81 | { | ||
82 | printk(KERN_DEBUG "kgdb init\n"); | ||
83 | if (ppc_md.early_serial_map) | ||
84 | ppc_md.early_serial_map(); | ||
85 | kgdb_debugport = serial_init(KGDB_PORT, NULL); | ||
86 | } | ||
diff --git a/arch/ppc/syslib/gt64260_pic.c b/arch/ppc/syslib/gt64260_pic.c new file mode 100644 index 000000000000..44aa87385451 --- /dev/null +++ b/arch/ppc/syslib/gt64260_pic.c | |||
@@ -0,0 +1,328 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/gt64260_pic.c | ||
3 | * | ||
4 | * Interrupt controller support for Galileo's GT64260. | ||
5 | * | ||
6 | * Author: Chris Zankel <source@mvista.com> | ||
7 | * Modified by: Mark A. Greer <mgreer@mvista.com> | ||
8 | * | ||
9 | * Based on sources from Rabeeh Khoury / Galileo Technology | ||
10 | * | ||
11 | * 2001 (c) MontaVista, Software, Inc. This file is licensed under | ||
12 | * the terms of the GNU General Public License version 2. This program | ||
13 | * is licensed "as is" without any warranty of any kind, whether express | ||
14 | * or implied. | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * This file contains the specific functions to support the GT64260 | ||
19 | * interrupt controller. | ||
20 | * | ||
21 | * The GT64260 has two main interrupt registers (high and low) that | ||
22 | * summarizes the interrupts generated by the units of the GT64260. | ||
23 | * Each bit is assigned to an interrupt number, where the low register | ||
24 | * are assigned from IRQ0 to IRQ31 and the high cause register | ||
25 | * from IRQ32 to IRQ63 | ||
26 | * The GPP (General Purpose Port) interrupts are assigned from IRQ64 (GPP0) | ||
27 | * to IRQ95 (GPP31). | ||
28 | * get_irq() returns the lowest interrupt number that is currently asserted. | ||
29 | * | ||
30 | * Note: | ||
31 | * - This driver does not initialize the GPP when used as an interrupt | ||
32 | * input. | ||
33 | */ | ||
34 | |||
35 | #include <linux/stddef.h> | ||
36 | #include <linux/init.h> | ||
37 | #include <linux/interrupt.h> | ||
38 | #include <linux/sched.h> | ||
39 | #include <linux/signal.h> | ||
40 | #include <linux/stddef.h> | ||
41 | #include <linux/delay.h> | ||
42 | #include <linux/irq.h> | ||
43 | |||
44 | #include <asm/io.h> | ||
45 | #include <asm/system.h> | ||
46 | #include <asm/irq.h> | ||
47 | #include <asm/mv64x60.h> | ||
48 | |||
49 | #define CPU_INTR_STR "gt64260 cpu interface error" | ||
50 | #define PCI0_INTR_STR "gt64260 pci 0 error" | ||
51 | #define PCI1_INTR_STR "gt64260 pci 1 error" | ||
52 | |||
53 | /* ========================== forward declaration ========================== */ | ||
54 | |||
55 | static void gt64260_unmask_irq(unsigned int); | ||
56 | static void gt64260_mask_irq(unsigned int); | ||
57 | |||
58 | /* ========================== local declarations =========================== */ | ||
59 | |||
60 | struct hw_interrupt_type gt64260_pic = { | ||
61 | .typename = " gt64260_pic ", | ||
62 | .enable = gt64260_unmask_irq, | ||
63 | .disable = gt64260_mask_irq, | ||
64 | .ack = gt64260_mask_irq, | ||
65 | .end = gt64260_unmask_irq, | ||
66 | }; | ||
67 | |||
68 | u32 gt64260_irq_base = 0; /* GT64260 handles the next 96 IRQs from here */ | ||
69 | |||
70 | static struct mv64x60_handle bh; | ||
71 | |||
72 | /* gt64260_init_irq() | ||
73 | * | ||
74 | * This function initializes the interrupt controller. It assigns | ||
75 | * all interrupts from IRQ0 to IRQ95 to the gt64260 interrupt controller. | ||
76 | * | ||
77 | * Note: | ||
78 | * We register all GPP inputs as interrupt source, but disable them. | ||
79 | */ | ||
80 | void __init | ||
81 | gt64260_init_irq(void) | ||
82 | { | ||
83 | int i; | ||
84 | |||
85 | if (ppc_md.progress) | ||
86 | ppc_md.progress("gt64260_init_irq: enter", 0x0); | ||
87 | |||
88 | bh.v_base = mv64x60_get_bridge_vbase(); | ||
89 | |||
90 | ppc_cached_irq_mask[0] = 0; | ||
91 | ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */ | ||
92 | ppc_cached_irq_mask[2] = 0; | ||
93 | |||
94 | /* disable all interrupts and clear current interrupts */ | ||
95 | mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]); | ||
96 | mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0); | ||
97 | mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]); | ||
98 | mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]); | ||
99 | |||
100 | /* use the gt64260 for all (possible) interrupt sources */ | ||
101 | for (i = gt64260_irq_base; i < (gt64260_irq_base + 96); i++) | ||
102 | irq_desc[i].handler = >64260_pic; | ||
103 | |||
104 | if (ppc_md.progress) | ||
105 | ppc_md.progress("gt64260_init_irq: exit", 0x0); | ||
106 | } | ||
107 | |||
108 | /* | ||
109 | * gt64260_get_irq() | ||
110 | * | ||
111 | * This function returns the lowest interrupt number of all interrupts that | ||
112 | * are currently asserted. | ||
113 | * | ||
114 | * Input Variable(s): | ||
115 | * struct pt_regs* not used | ||
116 | * | ||
117 | * Output Variable(s): | ||
118 | * None. | ||
119 | * | ||
120 | * Returns: | ||
121 | * int <interrupt number> or -2 (bogus interrupt) | ||
122 | */ | ||
123 | int | ||
124 | gt64260_get_irq(struct pt_regs *regs) | ||
125 | { | ||
126 | int irq; | ||
127 | int irq_gpp; | ||
128 | |||
129 | irq = mv64x60_read(&bh, GT64260_IC_MAIN_CAUSE_LO); | ||
130 | irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]); | ||
131 | |||
132 | if (irq == -1) { | ||
133 | irq = mv64x60_read(&bh, GT64260_IC_MAIN_CAUSE_HI); | ||
134 | irq = __ilog2((irq & 0x0f000db7) & ppc_cached_irq_mask[1]); | ||
135 | |||
136 | if (irq == -1) | ||
137 | irq = -2; /* bogus interrupt, should never happen */ | ||
138 | else { | ||
139 | if (irq >= 24) { | ||
140 | irq_gpp = mv64x60_read(&bh, | ||
141 | MV64x60_GPP_INTR_CAUSE); | ||
142 | irq_gpp = __ilog2(irq_gpp & | ||
143 | ppc_cached_irq_mask[2]); | ||
144 | |||
145 | if (irq_gpp == -1) | ||
146 | irq = -2; | ||
147 | else { | ||
148 | irq = irq_gpp + 64; | ||
149 | mv64x60_write(&bh, | ||
150 | MV64x60_GPP_INTR_CAUSE, | ||
151 | ~(1 << (irq - 64))); | ||
152 | } | ||
153 | } else | ||
154 | irq += 32; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | (void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE); | ||
159 | |||
160 | if (irq < 0) | ||
161 | return (irq); | ||
162 | else | ||
163 | return (gt64260_irq_base + irq); | ||
164 | } | ||
165 | |||
166 | /* gt64260_unmask_irq() | ||
167 | * | ||
168 | * This function enables an interrupt. | ||
169 | * | ||
170 | * Input Variable(s): | ||
171 | * unsigned int interrupt number (IRQ0...IRQ95). | ||
172 | * | ||
173 | * Output Variable(s): | ||
174 | * None. | ||
175 | * | ||
176 | * Returns: | ||
177 | * void | ||
178 | */ | ||
179 | static void | ||
180 | gt64260_unmask_irq(unsigned int irq) | ||
181 | { | ||
182 | irq -= gt64260_irq_base; | ||
183 | |||
184 | if (irq > 31) | ||
185 | if (irq > 63) /* unmask GPP irq */ | ||
186 | mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, | ||
187 | ppc_cached_irq_mask[2] |= (1 << (irq - 64))); | ||
188 | else /* mask high interrupt register */ | ||
189 | mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI, | ||
190 | ppc_cached_irq_mask[1] |= (1 << (irq - 32))); | ||
191 | else /* mask low interrupt register */ | ||
192 | mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO, | ||
193 | ppc_cached_irq_mask[0] |= (1 << irq)); | ||
194 | |||
195 | (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); | ||
196 | return; | ||
197 | } | ||
198 | |||
199 | /* gt64260_mask_irq() | ||
200 | * | ||
201 | * This function disables the requested interrupt. | ||
202 | * | ||
203 | * Input Variable(s): | ||
204 | * unsigned int interrupt number (IRQ0...IRQ95). | ||
205 | * | ||
206 | * Output Variable(s): | ||
207 | * None. | ||
208 | * | ||
209 | * Returns: | ||
210 | * void | ||
211 | */ | ||
212 | static void | ||
213 | gt64260_mask_irq(unsigned int irq) | ||
214 | { | ||
215 | irq -= gt64260_irq_base; | ||
216 | |||
217 | if (irq > 31) | ||
218 | if (irq > 63) /* mask GPP irq */ | ||
219 | mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, | ||
220 | ppc_cached_irq_mask[2] &= ~(1 << (irq - 64))); | ||
221 | else /* mask high interrupt register */ | ||
222 | mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI, | ||
223 | ppc_cached_irq_mask[1] &= ~(1 << (irq - 32))); | ||
224 | else /* mask low interrupt register */ | ||
225 | mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO, | ||
226 | ppc_cached_irq_mask[0] &= ~(1 << irq)); | ||
227 | |||
228 | (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); | ||
229 | return; | ||
230 | } | ||
231 | |||
232 | static irqreturn_t | ||
233 | gt64260_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
234 | { | ||
235 | printk(KERN_ERR "gt64260_cpu_error_int_handler: %s 0x%08x\n", | ||
236 | "Error on CPU interface - Cause regiser", | ||
237 | mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE)); | ||
238 | printk(KERN_ERR "\tCPU error register dump:\n"); | ||
239 | printk(KERN_ERR "\tAddress low 0x%08x\n", | ||
240 | mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO)); | ||
241 | printk(KERN_ERR "\tAddress high 0x%08x\n", | ||
242 | mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI)); | ||
243 | printk(KERN_ERR "\tData low 0x%08x\n", | ||
244 | mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO)); | ||
245 | printk(KERN_ERR "\tData high 0x%08x\n", | ||
246 | mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI)); | ||
247 | printk(KERN_ERR "\tParity 0x%08x\n", | ||
248 | mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY)); | ||
249 | mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0); | ||
250 | return IRQ_HANDLED; | ||
251 | } | ||
252 | |||
253 | static irqreturn_t | ||
254 | gt64260_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
255 | { | ||
256 | u32 val; | ||
257 | unsigned int pci_bus = (unsigned int)dev_id; | ||
258 | |||
259 | if (pci_bus == 0) { /* Error on PCI 0 */ | ||
260 | val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE); | ||
261 | printk(KERN_ERR "%s: Error in PCI %d Interface\n", | ||
262 | "gt64260_pci_error_int_handler", pci_bus); | ||
263 | printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); | ||
264 | printk(KERN_ERR "\tCause register 0x%08x\n", val); | ||
265 | printk(KERN_ERR "\tAddress Low 0x%08x\n", | ||
266 | mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO)); | ||
267 | printk(KERN_ERR "\tAddress High 0x%08x\n", | ||
268 | mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI)); | ||
269 | printk(KERN_ERR "\tAttribute 0x%08x\n", | ||
270 | mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO)); | ||
271 | printk(KERN_ERR "\tCommand 0x%08x\n", | ||
272 | mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD)); | ||
273 | mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val); | ||
274 | } | ||
275 | if (pci_bus == 1) { /* Error on PCI 1 */ | ||
276 | val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE); | ||
277 | printk(KERN_ERR "%s: Error in PCI %d Interface\n", | ||
278 | "gt64260_pci_error_int_handler", pci_bus); | ||
279 | printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); | ||
280 | printk(KERN_ERR "\tCause register 0x%08x\n", val); | ||
281 | printk(KERN_ERR "\tAddress Low 0x%08x\n", | ||
282 | mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO)); | ||
283 | printk(KERN_ERR "\tAddress High 0x%08x\n", | ||
284 | mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI)); | ||
285 | printk(KERN_ERR "\tAttribute 0x%08x\n", | ||
286 | mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO)); | ||
287 | printk(KERN_ERR "\tCommand 0x%08x\n", | ||
288 | mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD)); | ||
289 | mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val); | ||
290 | } | ||
291 | return IRQ_HANDLED; | ||
292 | } | ||
293 | |||
294 | static int __init | ||
295 | gt64260_register_hdlrs(void) | ||
296 | { | ||
297 | int rc; | ||
298 | |||
299 | /* Register CPU interface error interrupt handler */ | ||
300 | if ((rc = request_irq(MV64x60_IRQ_CPU_ERR, | ||
301 | gt64260_cpu_error_int_handler, SA_INTERRUPT, CPU_INTR_STR, 0))) | ||
302 | printk(KERN_WARNING "Can't register cpu error handler: %d", rc); | ||
303 | |||
304 | mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0); | ||
305 | mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000fe); | ||
306 | |||
307 | /* Register PCI 0 error interrupt handler */ | ||
308 | if ((rc = request_irq(MV64360_IRQ_PCI0, gt64260_pci_error_int_handler, | ||
309 | SA_INTERRUPT, PCI0_INTR_STR, (void *)0))) | ||
310 | printk(KERN_WARNING "Can't register pci 0 error handler: %d", | ||
311 | rc); | ||
312 | |||
313 | mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0); | ||
314 | mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0x003c0c24); | ||
315 | |||
316 | /* Register PCI 1 error interrupt handler */ | ||
317 | if ((rc = request_irq(MV64360_IRQ_PCI1, gt64260_pci_error_int_handler, | ||
318 | SA_INTERRUPT, PCI1_INTR_STR, (void *)1))) | ||
319 | printk(KERN_WARNING "Can't register pci 1 error handler: %d", | ||
320 | rc); | ||
321 | |||
322 | mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0); | ||
323 | mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0x003c0c24); | ||
324 | |||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | arch_initcall(gt64260_register_hdlrs); | ||
diff --git a/arch/ppc/syslib/harrier.c b/arch/ppc/syslib/harrier.c new file mode 100644 index 000000000000..a6b3f8645793 --- /dev/null +++ b/arch/ppc/syslib/harrier.c | |||
@@ -0,0 +1,302 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/harrier.c | ||
3 | * | ||
4 | * Motorola MCG Harrier northbridge/memory controller support | ||
5 | * | ||
6 | * Author: Dale Farnsworth | ||
7 | * dale.farnsworth@mvista.com | ||
8 | * | ||
9 | * 2001 (c) MontaVista, Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/harrier_defs.h> | ||
18 | |||
19 | #include <asm/byteorder.h> | ||
20 | #include <asm/io.h> | ||
21 | #include <asm/irq.h> | ||
22 | #include <asm/pci.h> | ||
23 | #include <asm/pci-bridge.h> | ||
24 | #include <asm/open_pic.h> | ||
25 | #include <asm/harrier.h> | ||
26 | |||
27 | /* define defaults for inbound windows */ | ||
28 | #define HARRIER_ITAT_DEFAULT (HARRIER_ITAT_ENA | \ | ||
29 | HARRIER_ITAT_MEM | \ | ||
30 | HARRIER_ITAT_WPE | \ | ||
31 | HARRIER_ITAT_GBL) | ||
32 | |||
33 | #define HARRIER_MPAT_DEFAULT (HARRIER_ITAT_ENA | \ | ||
34 | HARRIER_ITAT_MEM | \ | ||
35 | HARRIER_ITAT_WPE | \ | ||
36 | HARRIER_ITAT_GBL) | ||
37 | |||
38 | /* | ||
39 | * Initialize the inbound window size on a non-monarch harrier. | ||
40 | */ | ||
41 | void __init harrier_setup_nonmonarch(uint ppc_reg_base, uint in0_size) | ||
42 | { | ||
43 | u16 temps; | ||
44 | u32 temp; | ||
45 | |||
46 | if (in0_size > HARRIER_ITSZ_2GB) { | ||
47 | printk | ||
48 | ("harrier_setup_nonmonarch: Invalid window size code %d\n", | ||
49 | in0_size); | ||
50 | return; | ||
51 | } | ||
52 | |||
53 | /* Clear the PCI memory enable bit. If we don't, then when the | ||
54 | * inbound windows are enabled below, the corresponding BARs will be | ||
55 | * "live" and start answering to PCI memory reads from their default | ||
56 | * addresses (0x0), which overlap with system RAM. | ||
57 | */ | ||
58 | temps = in_le16((u16 *) (ppc_reg_base + | ||
59 | HARRIER_XCSR_CONFIG(PCI_COMMAND))); | ||
60 | temps &= ~(PCI_COMMAND_MEMORY); | ||
61 | out_le16((u16 *) (ppc_reg_base + HARRIER_XCSR_CONFIG(PCI_COMMAND)), | ||
62 | temps); | ||
63 | |||
64 | /* Setup a non-prefetchable inbound window */ | ||
65 | out_le32((u32 *) (ppc_reg_base + | ||
66 | HARRIER_XCSR_CONFIG(HARRIER_ITSZ0_OFF)), in0_size); | ||
67 | |||
68 | temp = in_le32((u32 *) (ppc_reg_base + | ||
69 | HARRIER_XCSR_CONFIG(HARRIER_ITAT0_OFF))); | ||
70 | temp &= ~HARRIER_ITAT_PRE; | ||
71 | temp |= HARRIER_ITAT_DEFAULT; | ||
72 | out_le32((u32 *) (ppc_reg_base + | ||
73 | HARRIER_XCSR_CONFIG(HARRIER_ITAT0_OFF)), temp); | ||
74 | |||
75 | /* Enable the message passing block */ | ||
76 | temp = in_le32((u32 *) (ppc_reg_base + | ||
77 | HARRIER_XCSR_CONFIG(HARRIER_MPAT_OFF))); | ||
78 | temp |= HARRIER_MPAT_DEFAULT; | ||
79 | out_le32((u32 *) (ppc_reg_base + | ||
80 | HARRIER_XCSR_CONFIG(HARRIER_MPAT_OFF)), temp); | ||
81 | } | ||
82 | |||
83 | void __init harrier_release_eready(uint ppc_reg_base) | ||
84 | { | ||
85 | ulong temp; | ||
86 | |||
87 | /* | ||
88 | * Set EREADY to allow the line to be pulled up after everyone is | ||
89 | * ready. | ||
90 | */ | ||
91 | temp = in_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF)); | ||
92 | temp |= HARRIER_EREADY; | ||
93 | out_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF), temp); | ||
94 | } | ||
95 | |||
96 | void __init harrier_wait_eready(uint ppc_reg_base) | ||
97 | { | ||
98 | ulong temp; | ||
99 | |||
100 | /* | ||
101 | * Poll the ERDYS line until it goes high to indicate that all | ||
102 | * non-monarch PrPMCs are ready for bus enumeration (or that there are | ||
103 | * no PrPMCs present). | ||
104 | */ | ||
105 | |||
106 | /* FIXME: Add a timeout of some kind to prevent endless waits. */ | ||
107 | do { | ||
108 | |||
109 | temp = in_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF)); | ||
110 | |||
111 | } while (!(temp & HARRIER_ERDYS)); | ||
112 | } | ||
113 | |||
114 | /* | ||
115 | * Initialize the Motorola MCG Harrier host bridge. | ||
116 | * | ||
117 | * This means setting up the PPC bus to PCI memory and I/O space mappings, | ||
118 | * setting the PCI memory space address of the MPIC (mapped straight | ||
119 | * through), and ioremap'ing the mpic registers. | ||
120 | * 'OpenPIC_Addr' will be set correctly by this routine. | ||
121 | * This routine will not change the PCI_CONFIG_ADDR or PCI_CONFIG_DATA | ||
122 | * addresses and assumes that the mapping of PCI memory space back to system | ||
123 | * memory is set up correctly by PPCBug. | ||
124 | */ | ||
125 | int __init | ||
126 | harrier_init(struct pci_controller *hose, | ||
127 | uint ppc_reg_base, | ||
128 | ulong processor_pci_mem_start, | ||
129 | ulong processor_pci_mem_end, | ||
130 | ulong processor_pci_io_start, | ||
131 | ulong processor_pci_io_end, ulong processor_mpic_base) | ||
132 | { | ||
133 | uint addr, offset; | ||
134 | |||
135 | /* | ||
136 | * Some sanity checks... | ||
137 | */ | ||
138 | if (((processor_pci_mem_start & 0xffff0000) != processor_pci_mem_start) | ||
139 | || ((processor_pci_io_start & 0xffff0000) != | ||
140 | processor_pci_io_start)) { | ||
141 | printk("harrier_init: %s\n", | ||
142 | "PPC to PCI mappings must start on 64 KB boundaries"); | ||
143 | return -1; | ||
144 | } | ||
145 | |||
146 | if (((processor_pci_mem_end & 0x0000ffff) != 0x0000ffff) || | ||
147 | ((processor_pci_io_end & 0x0000ffff) != 0x0000ffff)) { | ||
148 | printk("harrier_init: PPC to PCI mappings %s\n", | ||
149 | "must end just before a 64 KB boundaries"); | ||
150 | return -1; | ||
151 | } | ||
152 | |||
153 | if (((processor_pci_mem_end - processor_pci_mem_start) != | ||
154 | (hose->mem_space.end - hose->mem_space.start)) || | ||
155 | ((processor_pci_io_end - processor_pci_io_start) != | ||
156 | (hose->io_space.end - hose->io_space.start))) { | ||
157 | printk("harrier_init: %s\n", | ||
158 | "PPC and PCI memory or I/O space sizes don't match"); | ||
159 | return -1; | ||
160 | } | ||
161 | |||
162 | if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) { | ||
163 | printk("harrier_init: %s\n", | ||
164 | "MPIC address must start on 256 KB boundary"); | ||
165 | return -1; | ||
166 | } | ||
167 | |||
168 | if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) { | ||
169 | printk("harrier_init: %s\n", | ||
170 | "pci_dram_offset must be multiple of 64 KB"); | ||
171 | return -1; | ||
172 | } | ||
173 | |||
174 | /* | ||
175 | * Program the OTAD/OTOF registers to set up the PCI Mem & I/O | ||
176 | * space mappings. These are the mappings going from the processor to | ||
177 | * the PCI bus. | ||
178 | * | ||
179 | * Note: Don't need to 'AND' start/end addresses with 0xffff0000 | ||
180 | * because sanity check above ensures that they are properly | ||
181 | * aligned. | ||
182 | */ | ||
183 | |||
184 | /* Set up PPC->PCI Mem mapping */ | ||
185 | addr = processor_pci_mem_start | (processor_pci_mem_end >> 16); | ||
186 | #ifdef CONFIG_HARRIER_STORE_GATHERING | ||
187 | offset = (hose->mem_space.start - processor_pci_mem_start) | 0x9a; | ||
188 | #else | ||
189 | offset = (hose->mem_space.start - processor_pci_mem_start) | 0x92; | ||
190 | #endif | ||
191 | out_be32((uint *) (ppc_reg_base + HARRIER_OTAD0_OFF), addr); | ||
192 | out_be32((uint *) (ppc_reg_base + HARRIER_OTOF0_OFF), offset); | ||
193 | |||
194 | /* Set up PPC->PCI I/O mapping -- Contiguous I/O space */ | ||
195 | addr = processor_pci_io_start | (processor_pci_io_end >> 16); | ||
196 | offset = (hose->io_space.start - processor_pci_io_start) | 0x80; | ||
197 | out_be32((uint *) (ppc_reg_base + HARRIER_OTAD1_OFF), addr); | ||
198 | out_be32((uint *) (ppc_reg_base + HARRIER_OTOF1_OFF), offset); | ||
199 | |||
200 | /* Enable MPIC */ | ||
201 | OpenPIC_Addr = (void *)processor_mpic_base; | ||
202 | addr = (processor_mpic_base >> 16) | 1; | ||
203 | out_be16((ushort *) (ppc_reg_base + HARRIER_MBAR_OFF), addr); | ||
204 | out_8((u_char *) (ppc_reg_base + HARRIER_MPIC_CSR_OFF), | ||
205 | HARRIER_MPIC_OPI_ENABLE); | ||
206 | |||
207 | return 0; | ||
208 | } | ||
209 | |||
210 | /* | ||
211 | * Find the amount of RAM present. | ||
212 | * This assumes that PPCBug has initialized the memory controller (SMC) | ||
213 | * on the Harrier correctly (i.e., it does no sanity checking). | ||
214 | * It also assumes that the memory base registers are set to configure the | ||
215 | * memory as contigous starting with "RAM A BASE", "RAM B BASE", etc. | ||
216 | * however, RAM base registers can be skipped (e.g. A, B, C are set, | ||
217 | * D is skipped but E is set is okay). | ||
218 | */ | ||
219 | #define MB (1024*1024UL) | ||
220 | |||
221 | static uint harrier_size_table[] __initdata = { | ||
222 | 0 * MB, /* 0 ==> 0 MB */ | ||
223 | 32 * MB, /* 1 ==> 32 MB */ | ||
224 | 64 * MB, /* 2 ==> 64 MB */ | ||
225 | 64 * MB, /* 3 ==> 64 MB */ | ||
226 | 128 * MB, /* 4 ==> 128 MB */ | ||
227 | 128 * MB, /* 5 ==> 128 MB */ | ||
228 | 128 * MB, /* 6 ==> 128 MB */ | ||
229 | 256 * MB, /* 7 ==> 256 MB */ | ||
230 | 256 * MB, /* 8 ==> 256 MB */ | ||
231 | 256 * MB, /* 9 ==> 256 MB */ | ||
232 | 512 * MB, /* a ==> 512 MB */ | ||
233 | 512 * MB, /* b ==> 512 MB */ | ||
234 | 512 * MB, /* c ==> 512 MB */ | ||
235 | 1024 * MB, /* d ==> 1024 MB */ | ||
236 | 1024 * MB, /* e ==> 1024 MB */ | ||
237 | 2048 * MB, /* f ==> 2048 MB */ | ||
238 | }; | ||
239 | |||
240 | /* | ||
241 | * *** WARNING: You MUST have a BAT set up to map in the XCSR regs *** | ||
242 | * | ||
243 | * Read the memory controller's registers to determine the amount of system | ||
244 | * memory. Assumes that the memory controller registers are already mapped | ||
245 | * into virtual memory--too early to use ioremap(). | ||
246 | */ | ||
247 | unsigned long __init harrier_get_mem_size(uint xcsr_base) | ||
248 | { | ||
249 | ulong last_addr; | ||
250 | int i; | ||
251 | uint vend_dev_id; | ||
252 | uint *size_table; | ||
253 | uint val; | ||
254 | uint *csrp; | ||
255 | uint size; | ||
256 | int size_table_entries; | ||
257 | |||
258 | vend_dev_id = in_be32((uint *) xcsr_base + PCI_VENDOR_ID); | ||
259 | |||
260 | if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) { | ||
261 | printk("harrier_get_mem_size: %s (0x%x)\n", | ||
262 | "Not a Motorola Memory Controller", vend_dev_id); | ||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | vend_dev_id &= 0x0000ffff; | ||
267 | |||
268 | if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HARRIER) { | ||
269 | size_table = harrier_size_table; | ||
270 | size_table_entries = sizeof(harrier_size_table) / | ||
271 | sizeof(harrier_size_table[0]); | ||
272 | } else { | ||
273 | printk("harrier_get_mem_size: %s (0x%x)\n", | ||
274 | "Not a Harrier", vend_dev_id); | ||
275 | return 0; | ||
276 | } | ||
277 | |||
278 | last_addr = 0; | ||
279 | |||
280 | csrp = (uint *) (xcsr_base + HARRIER_SDBA_OFF); | ||
281 | for (i = 0; i < 8; i++) { | ||
282 | val = in_be32(csrp++); | ||
283 | |||
284 | if (val & 0x100) { /* If enabled */ | ||
285 | size = val >> HARRIER_SDB_SIZE_SHIFT; | ||
286 | size &= HARRIER_SDB_SIZE_MASK; | ||
287 | if (size >= size_table_entries) { | ||
288 | break; /* Register not set correctly */ | ||
289 | } | ||
290 | size = size_table[size]; | ||
291 | |||
292 | val &= ~(size - 1); | ||
293 | val += size; | ||
294 | |||
295 | if (val > last_addr) { | ||
296 | last_addr = val; | ||
297 | } | ||
298 | } | ||
299 | } | ||
300 | |||
301 | return last_addr; | ||
302 | } | ||
diff --git a/arch/ppc/syslib/hawk_common.c b/arch/ppc/syslib/hawk_common.c new file mode 100644 index 000000000000..a9911dc3a82f --- /dev/null +++ b/arch/ppc/syslib/hawk_common.c | |||
@@ -0,0 +1,319 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/hawk_common.c | ||
3 | * | ||
4 | * Common Motorola PowerPlus Platform--really Falcon/Raven or HAWK. | ||
5 | * | ||
6 | * Author: Mark A. Greer | ||
7 | * mgreer@mvista.com | ||
8 | * | ||
9 | * 2001 (c) MontaVista, Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/pci.h> | ||
17 | |||
18 | #include <asm/byteorder.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/irq.h> | ||
21 | #include <asm/pci.h> | ||
22 | #include <asm/pci-bridge.h> | ||
23 | #include <asm/open_pic.h> | ||
24 | #include <asm/hawk.h> | ||
25 | |||
26 | /* | ||
27 | * The Falcon/Raven and HAWK has 4 sets of registers: | ||
28 | * 1) PPC Registers which define the mappings from PPC bus to PCI bus, | ||
29 | * etc. | ||
30 | * 2) PCI Registers which define the mappings from PCI bus to PPC bus and the | ||
31 | * MPIC base address. | ||
32 | * 3) MPIC registers. | ||
33 | * 4) System Memory Controller (SMC) registers. | ||
34 | */ | ||
35 | |||
36 | /* | ||
37 | * Initialize the Motorola MCG Raven or HAWK host bridge. | ||
38 | * | ||
39 | * This means setting up the PPC bus to PCI memory and I/O space mappings, | ||
40 | * setting the PCI memory space address of the MPIC (mapped straight | ||
41 | * through), and ioremap'ing the mpic registers. | ||
42 | * This routine will set the PCI_CONFIG_ADDR or PCI_CONFIG_DATA | ||
43 | * addresses based on the PCI I/O address that is passed in. | ||
44 | * 'OpenPIC_Addr' will be set correctly by this routine. | ||
45 | */ | ||
46 | int __init | ||
47 | hawk_init(struct pci_controller *hose, | ||
48 | uint ppc_reg_base, | ||
49 | ulong processor_pci_mem_start, | ||
50 | ulong processor_pci_mem_end, | ||
51 | ulong processor_pci_io_start, | ||
52 | ulong processor_pci_io_end, | ||
53 | ulong processor_mpic_base) | ||
54 | { | ||
55 | uint addr, offset; | ||
56 | |||
57 | /* | ||
58 | * Some sanity checks... | ||
59 | */ | ||
60 | if (((processor_pci_mem_start&0xffff0000) != processor_pci_mem_start) || | ||
61 | ((processor_pci_io_start &0xffff0000) != processor_pci_io_start)) { | ||
62 | printk("hawk_init: %s\n", | ||
63 | "PPC to PCI mappings must start on 64 KB boundaries"); | ||
64 | return -1; | ||
65 | } | ||
66 | |||
67 | if (((processor_pci_mem_end &0x0000ffff) != 0x0000ffff) || | ||
68 | ((processor_pci_io_end &0x0000ffff) != 0x0000ffff)) { | ||
69 | printk("hawk_init: PPC to PCI mappings %s\n", | ||
70 | "must end just before a 64 KB boundaries"); | ||
71 | return -1; | ||
72 | } | ||
73 | |||
74 | if (((processor_pci_mem_end - processor_pci_mem_start) != | ||
75 | (hose->mem_space.end - hose->mem_space.start)) || | ||
76 | ((processor_pci_io_end - processor_pci_io_start) != | ||
77 | (hose->io_space.end - hose->io_space.start))) { | ||
78 | printk("hawk_init: %s\n", | ||
79 | "PPC and PCI memory or I/O space sizes don't match"); | ||
80 | return -1; | ||
81 | } | ||
82 | |||
83 | if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) { | ||
84 | printk("hawk_init: %s\n", | ||
85 | "MPIC address must start on 256 MB boundary"); | ||
86 | return -1; | ||
87 | } | ||
88 | |||
89 | if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) { | ||
90 | printk("hawk_init: %s\n", | ||
91 | "pci_dram_offset must be multiple of 64 KB"); | ||
92 | return -1; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Disable previous PPC->PCI mappings. | ||
97 | */ | ||
98 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF0_OFF), 0x00000000); | ||
99 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF1_OFF), 0x00000000); | ||
100 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF2_OFF), 0x00000000); | ||
101 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF3_OFF), 0x00000000); | ||
102 | |||
103 | /* | ||
104 | * Program the XSADD/XSOFF registers to set up the PCI Mem & I/O | ||
105 | * space mappings. These are the mappings going from the processor to | ||
106 | * the PCI bus. | ||
107 | * | ||
108 | * Note: Don't need to 'AND' start/end addresses with 0xffff0000 | ||
109 | * because sanity check above ensures that they are properly | ||
110 | * aligned. | ||
111 | */ | ||
112 | |||
113 | /* Set up PPC->PCI Mem mapping */ | ||
114 | addr = processor_pci_mem_start | (processor_pci_mem_end >> 16); | ||
115 | offset = (hose->mem_space.start - processor_pci_mem_start) | 0xd2; | ||
116 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD0_OFF), addr); | ||
117 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF0_OFF), offset); | ||
118 | |||
119 | /* Set up PPC->MPIC mapping on the bridge */ | ||
120 | addr = processor_mpic_base | | ||
121 | (((processor_mpic_base + HAWK_MPIC_SIZE) >> 16) - 1); | ||
122 | /* No write posting for this PCI Mem space */ | ||
123 | offset = (hose->mem_space.start - processor_pci_mem_start) | 0xc2; | ||
124 | |||
125 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD1_OFF), addr); | ||
126 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF1_OFF), offset); | ||
127 | |||
128 | /* Set up PPC->PCI I/O mapping -- Contiguous I/O space */ | ||
129 | addr = processor_pci_io_start | (processor_pci_io_end >> 16); | ||
130 | offset = (hose->io_space.start - processor_pci_io_start) | 0xc0; | ||
131 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD3_OFF), addr); | ||
132 | out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF3_OFF), offset); | ||
133 | |||
134 | hose->io_base_virt = (void *)ioremap(processor_pci_io_start, | ||
135 | (processor_pci_io_end - processor_pci_io_start + 1)); | ||
136 | |||
137 | /* | ||
138 | * Set up the indirect method of accessing PCI config space. | ||
139 | * The PCI config addr/data pair based on start addr of PCI I/O space. | ||
140 | */ | ||
141 | setup_indirect_pci(hose, | ||
142 | processor_pci_io_start + HAWK_PCI_CONFIG_ADDR_OFF, | ||
143 | processor_pci_io_start + HAWK_PCI_CONFIG_DATA_OFF); | ||
144 | |||
145 | /* | ||
146 | * Disable previous PCI->PPC mappings. | ||
147 | */ | ||
148 | |||
149 | /* XXXX Put in mappings from PCI bus to processor bus XXXX */ | ||
150 | |||
151 | /* | ||
152 | * Disable MPIC response to PCI I/O space (BAR 0). | ||
153 | * Make MPIC respond to PCI Mem space at specified address. | ||
154 | * (BAR 1). | ||
155 | */ | ||
156 | early_write_config_dword(hose, | ||
157 | 0, | ||
158 | PCI_DEVFN(0,0), | ||
159 | PCI_BASE_ADDRESS_0, | ||
160 | 0x00000000 | 0x1); | ||
161 | |||
162 | early_write_config_dword(hose, | ||
163 | 0, | ||
164 | PCI_DEVFN(0,0), | ||
165 | PCI_BASE_ADDRESS_1, | ||
166 | (processor_mpic_base - | ||
167 | processor_pci_mem_start + | ||
168 | hose->mem_space.start) | 0x0); | ||
169 | |||
170 | /* Map MPIC into vitual memory */ | ||
171 | OpenPIC_Addr = ioremap(processor_mpic_base, HAWK_MPIC_SIZE); | ||
172 | |||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * Find the amount of RAM present. | ||
178 | * This assumes that PPCBug has initialized the memory controller (SMC) | ||
179 | * on the Falcon/HAWK correctly (i.e., it does no sanity checking). | ||
180 | * It also assumes that the memory base registers are set to configure the | ||
181 | * memory as contigous starting with "RAM A BASE", "RAM B BASE", etc. | ||
182 | * however, RAM base registers can be skipped (e.g. A, B, C are set, | ||
183 | * D is skipped but E is set is okay). | ||
184 | */ | ||
185 | #define MB (1024*1024) | ||
186 | |||
187 | static uint reg_offset_table[] __initdata = { | ||
188 | HAWK_SMC_RAM_A_SIZE_REG_OFF, | ||
189 | HAWK_SMC_RAM_B_SIZE_REG_OFF, | ||
190 | HAWK_SMC_RAM_C_SIZE_REG_OFF, | ||
191 | HAWK_SMC_RAM_D_SIZE_REG_OFF, | ||
192 | HAWK_SMC_RAM_E_SIZE_REG_OFF, | ||
193 | HAWK_SMC_RAM_F_SIZE_REG_OFF, | ||
194 | HAWK_SMC_RAM_G_SIZE_REG_OFF, | ||
195 | HAWK_SMC_RAM_H_SIZE_REG_OFF | ||
196 | }; | ||
197 | |||
198 | static uint falcon_size_table[] __initdata = { | ||
199 | 0 * MB, /* 0 ==> 0 MB */ | ||
200 | 16 * MB, /* 1 ==> 16 MB */ | ||
201 | 32 * MB, /* 2 ==> 32 MB */ | ||
202 | 64 * MB, /* 3 ==> 64 MB */ | ||
203 | 128 * MB, /* 4 ==> 128 MB */ | ||
204 | 256 * MB, /* 5 ==> 256 MB */ | ||
205 | 1024 * MB, /* 6 ==> 1024 MB (1 GB) */ | ||
206 | }; | ||
207 | |||
208 | static uint hawk_size_table[] __initdata = { | ||
209 | 0 * MB, /* 0 ==> 0 MB */ | ||
210 | 32 * MB, /* 1 ==> 32 MB */ | ||
211 | 64 * MB, /* 2 ==> 64 MB */ | ||
212 | 64 * MB, /* 3 ==> 64 MB */ | ||
213 | 128 * MB, /* 4 ==> 128 MB */ | ||
214 | 128 * MB, /* 5 ==> 128 MB */ | ||
215 | 128 * MB, /* 6 ==> 128 MB */ | ||
216 | 256 * MB, /* 7 ==> 256 MB */ | ||
217 | 256 * MB, /* 8 ==> 256 MB */ | ||
218 | 512 * MB, /* 9 ==> 512 MB */ | ||
219 | }; | ||
220 | |||
221 | /* | ||
222 | * *** WARNING: You MUST have a BAT set up to map in the SMC regs *** | ||
223 | * | ||
224 | * Read the memory controller's registers to determine the amount of system | ||
225 | * memory. Assumes that the memory controller registers are already mapped | ||
226 | * into virtual memory--too early to use ioremap(). | ||
227 | */ | ||
228 | unsigned long __init | ||
229 | hawk_get_mem_size(uint smc_base) | ||
230 | { | ||
231 | unsigned long total; | ||
232 | int i, size_table_entries, reg_limit; | ||
233 | uint vend_dev_id; | ||
234 | uint *size_table; | ||
235 | u_char val; | ||
236 | |||
237 | |||
238 | vend_dev_id = in_be32((uint *)smc_base + PCI_VENDOR_ID); | ||
239 | |||
240 | if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) { | ||
241 | printk("hawk_get_mem_size: %s (0x%x)\n", | ||
242 | "Not a Motorola Memory Controller", vend_dev_id); | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | vend_dev_id &= 0x0000ffff; | ||
247 | |||
248 | if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_FALCON) { | ||
249 | size_table = falcon_size_table; | ||
250 | size_table_entries = sizeof(falcon_size_table) / | ||
251 | sizeof(falcon_size_table[0]); | ||
252 | |||
253 | reg_limit = FALCON_SMC_REG_COUNT; | ||
254 | } | ||
255 | else if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HAWK) { | ||
256 | size_table = hawk_size_table; | ||
257 | size_table_entries = sizeof(hawk_size_table) / | ||
258 | sizeof(hawk_size_table[0]); | ||
259 | reg_limit = HAWK_SMC_REG_COUNT; | ||
260 | } | ||
261 | else { | ||
262 | printk("hawk_get_mem_size: %s (0x%x)\n", | ||
263 | "Not a Falcon or HAWK", vend_dev_id); | ||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | total = 0; | ||
268 | |||
269 | /* Check every reg because PPCBug may skip some */ | ||
270 | for (i=0; i<reg_limit; i++) { | ||
271 | val = in_8((u_char *)(smc_base + reg_offset_table[i])); | ||
272 | |||
273 | if (val & 0x80) { /* If enabled */ | ||
274 | val &= 0x0f; | ||
275 | |||
276 | /* Don't go past end of size_table */ | ||
277 | if (val < size_table_entries) { | ||
278 | total += size_table[val]; | ||
279 | } | ||
280 | else { /* Register not set correctly */ | ||
281 | break; | ||
282 | } | ||
283 | } | ||
284 | } | ||
285 | |||
286 | return total; | ||
287 | } | ||
288 | |||
289 | int __init | ||
290 | hawk_mpic_init(unsigned int pci_mem_offset) | ||
291 | { | ||
292 | unsigned short devid; | ||
293 | unsigned int pci_membase; | ||
294 | |||
295 | /* Check the first PCI device to see if it is a Raven or Hawk. */ | ||
296 | early_read_config_word(0, 0, 0, PCI_DEVICE_ID, &devid); | ||
297 | |||
298 | switch (devid) { | ||
299 | case PCI_DEVICE_ID_MOTOROLA_RAVEN: | ||
300 | case PCI_DEVICE_ID_MOTOROLA_HAWK: | ||
301 | break; | ||
302 | default: | ||
303 | OpenPIC_Addr = NULL; | ||
304 | return 1; | ||
305 | } | ||
306 | |||
307 | /* Read the memory base register. */ | ||
308 | early_read_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase); | ||
309 | |||
310 | if (pci_membase == 0) { | ||
311 | OpenPIC_Addr = NULL; | ||
312 | return 1; | ||
313 | } | ||
314 | |||
315 | /* Map the MPIC registers to virtual memory. */ | ||
316 | OpenPIC_Addr = ioremap(pci_membase + pci_mem_offset, 0x22000); | ||
317 | |||
318 | return 0; | ||
319 | } | ||
diff --git a/arch/ppc/syslib/i8259.c b/arch/ppc/syslib/i8259.c new file mode 100644 index 000000000000..b9391e650141 --- /dev/null +++ b/arch/ppc/syslib/i8259.c | |||
@@ -0,0 +1,211 @@ | |||
1 | #include <linux/init.h> | ||
2 | #include <linux/ioport.h> | ||
3 | #include <linux/interrupt.h> | ||
4 | #include <asm/io.h> | ||
5 | #include <asm/i8259.h> | ||
6 | |||
7 | static volatile unsigned char *pci_intack; /* RO, gives us the irq vector */ | ||
8 | |||
9 | unsigned char cached_8259[2] = { 0xff, 0xff }; | ||
10 | #define cached_A1 (cached_8259[0]) | ||
11 | #define cached_21 (cached_8259[1]) | ||
12 | |||
13 | static DEFINE_SPINLOCK(i8259_lock); | ||
14 | |||
15 | int i8259_pic_irq_offset; | ||
16 | |||
17 | /* | ||
18 | * Acknowledge the IRQ using either the PCI host bridge's interrupt | ||
19 | * acknowledge feature or poll. How i8259_init() is called determines | ||
20 | * which is called. It should be noted that polling is broken on some | ||
21 | * IBM and Motorola PReP boxes so we must use the int-ack feature on them. | ||
22 | */ | ||
23 | int | ||
24 | i8259_irq(struct pt_regs *regs) | ||
25 | { | ||
26 | int irq; | ||
27 | |||
28 | spin_lock(&i8259_lock); | ||
29 | |||
30 | /* Either int-ack or poll for the IRQ */ | ||
31 | if (pci_intack) | ||
32 | irq = *pci_intack; | ||
33 | else { | ||
34 | /* Perform an interrupt acknowledge cycle on controller 1. */ | ||
35 | outb(0x0C, 0x20); /* prepare for poll */ | ||
36 | irq = inb(0x20) & 7; | ||
37 | if (irq == 2 ) { | ||
38 | /* | ||
39 | * Interrupt is cascaded so perform interrupt | ||
40 | * acknowledge on controller 2. | ||
41 | */ | ||
42 | outb(0x0C, 0xA0); /* prepare for poll */ | ||
43 | irq = (inb(0xA0) & 7) + 8; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | if (irq == 7) { | ||
48 | /* | ||
49 | * This may be a spurious interrupt. | ||
50 | * | ||
51 | * Read the interrupt status register (ISR). If the most | ||
52 | * significant bit is not set then there is no valid | ||
53 | * interrupt. | ||
54 | */ | ||
55 | if (!pci_intack) | ||
56 | outb(0x0B, 0x20); /* ISR register */ | ||
57 | if(~inb(0x20) & 0x80) | ||
58 | irq = -1; | ||
59 | } | ||
60 | |||
61 | spin_unlock(&i8259_lock); | ||
62 | return irq; | ||
63 | } | ||
64 | |||
65 | static void i8259_mask_and_ack_irq(unsigned int irq_nr) | ||
66 | { | ||
67 | unsigned long flags; | ||
68 | |||
69 | spin_lock_irqsave(&i8259_lock, flags); | ||
70 | if ( irq_nr >= i8259_pic_irq_offset ) | ||
71 | irq_nr -= i8259_pic_irq_offset; | ||
72 | |||
73 | if (irq_nr > 7) { | ||
74 | cached_A1 |= 1 << (irq_nr-8); | ||
75 | inb(0xA1); /* DUMMY */ | ||
76 | outb(cached_A1,0xA1); | ||
77 | outb(0x20,0xA0); /* Non-specific EOI */ | ||
78 | outb(0x20,0x20); /* Non-specific EOI to cascade */ | ||
79 | } else { | ||
80 | cached_21 |= 1 << irq_nr; | ||
81 | inb(0x21); /* DUMMY */ | ||
82 | outb(cached_21,0x21); | ||
83 | outb(0x20,0x20); /* Non-specific EOI */ | ||
84 | } | ||
85 | spin_unlock_irqrestore(&i8259_lock, flags); | ||
86 | } | ||
87 | |||
88 | static void i8259_set_irq_mask(int irq_nr) | ||
89 | { | ||
90 | outb(cached_A1,0xA1); | ||
91 | outb(cached_21,0x21); | ||
92 | } | ||
93 | |||
94 | static void i8259_mask_irq(unsigned int irq_nr) | ||
95 | { | ||
96 | unsigned long flags; | ||
97 | |||
98 | spin_lock_irqsave(&i8259_lock, flags); | ||
99 | if ( irq_nr >= i8259_pic_irq_offset ) | ||
100 | irq_nr -= i8259_pic_irq_offset; | ||
101 | if ( irq_nr < 8 ) | ||
102 | cached_21 |= 1 << irq_nr; | ||
103 | else | ||
104 | cached_A1 |= 1 << (irq_nr-8); | ||
105 | i8259_set_irq_mask(irq_nr); | ||
106 | spin_unlock_irqrestore(&i8259_lock, flags); | ||
107 | } | ||
108 | |||
109 | static void i8259_unmask_irq(unsigned int irq_nr) | ||
110 | { | ||
111 | unsigned long flags; | ||
112 | |||
113 | spin_lock_irqsave(&i8259_lock, flags); | ||
114 | if ( irq_nr >= i8259_pic_irq_offset ) | ||
115 | irq_nr -= i8259_pic_irq_offset; | ||
116 | if ( irq_nr < 8 ) | ||
117 | cached_21 &= ~(1 << irq_nr); | ||
118 | else | ||
119 | cached_A1 &= ~(1 << (irq_nr-8)); | ||
120 | i8259_set_irq_mask(irq_nr); | ||
121 | spin_unlock_irqrestore(&i8259_lock, flags); | ||
122 | } | ||
123 | |||
124 | static void i8259_end_irq(unsigned int irq) | ||
125 | { | ||
126 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) | ||
127 | && irq_desc[irq].action) | ||
128 | i8259_unmask_irq(irq); | ||
129 | } | ||
130 | |||
131 | struct hw_interrupt_type i8259_pic = { | ||
132 | " i8259 ", | ||
133 | NULL, | ||
134 | NULL, | ||
135 | i8259_unmask_irq, | ||
136 | i8259_mask_irq, | ||
137 | i8259_mask_and_ack_irq, | ||
138 | i8259_end_irq, | ||
139 | NULL | ||
140 | }; | ||
141 | |||
142 | static struct resource pic1_iores = { | ||
143 | .name = "8259 (master)", | ||
144 | .start = 0x20, | ||
145 | .end = 0x21, | ||
146 | .flags = IORESOURCE_BUSY, | ||
147 | }; | ||
148 | |||
149 | static struct resource pic2_iores = { | ||
150 | .name = "8259 (slave)", | ||
151 | .start = 0xa0, | ||
152 | .end = 0xa1, | ||
153 | .flags = IORESOURCE_BUSY, | ||
154 | }; | ||
155 | |||
156 | static struct resource pic_edgectrl_iores = { | ||
157 | .name = "8259 edge control", | ||
158 | .start = 0x4d0, | ||
159 | .end = 0x4d1, | ||
160 | .flags = IORESOURCE_BUSY, | ||
161 | }; | ||
162 | |||
163 | static struct irqaction i8259_irqaction = { | ||
164 | .handler = no_action, | ||
165 | .flags = SA_INTERRUPT, | ||
166 | .mask = CPU_MASK_NONE, | ||
167 | .name = "82c59 secondary cascade", | ||
168 | }; | ||
169 | |||
170 | /* | ||
171 | * i8259_init() | ||
172 | * intack_addr - PCI interrupt acknowledge (real) address which will return | ||
173 | * the active irq from the 8259 | ||
174 | */ | ||
175 | void __init | ||
176 | i8259_init(long intack_addr) | ||
177 | { | ||
178 | unsigned long flags; | ||
179 | |||
180 | spin_lock_irqsave(&i8259_lock, flags); | ||
181 | /* init master interrupt controller */ | ||
182 | outb(0x11, 0x20); /* Start init sequence */ | ||
183 | outb(0x00, 0x21); /* Vector base */ | ||
184 | outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */ | ||
185 | outb(0x01, 0x21); /* Select 8086 mode */ | ||
186 | |||
187 | /* init slave interrupt controller */ | ||
188 | outb(0x11, 0xA0); /* Start init sequence */ | ||
189 | outb(0x08, 0xA1); /* Vector base */ | ||
190 | outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */ | ||
191 | outb(0x01, 0xA1); /* Select 8086 mode */ | ||
192 | |||
193 | /* always read ISR */ | ||
194 | outb(0x0B, 0x20); | ||
195 | outb(0x0B, 0xA0); | ||
196 | |||
197 | /* Mask all interrupts */ | ||
198 | outb(cached_A1, 0xA1); | ||
199 | outb(cached_21, 0x21); | ||
200 | |||
201 | spin_unlock_irqrestore(&i8259_lock, flags); | ||
202 | |||
203 | /* reserve our resources */ | ||
204 | setup_irq( i8259_pic_irq_offset + 2, &i8259_irqaction); | ||
205 | request_resource(&ioport_resource, &pic1_iores); | ||
206 | request_resource(&ioport_resource, &pic2_iores); | ||
207 | request_resource(&ioport_resource, &pic_edgectrl_iores); | ||
208 | |||
209 | if (intack_addr != 0) | ||
210 | pci_intack = ioremap(intack_addr, 1); | ||
211 | } | ||
diff --git a/arch/ppc/syslib/ibm440gp_common.c b/arch/ppc/syslib/ibm440gp_common.c new file mode 100644 index 000000000000..0d6be2d6dd67 --- /dev/null +++ b/arch/ppc/syslib/ibm440gp_common.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ibm440gp_common.c | ||
3 | * | ||
4 | * PPC440GP system library | ||
5 | * | ||
6 | * Matt Porter <mporter@mvista.com> | ||
7 | * Copyright 2002-2003 MontaVista Software Inc. | ||
8 | * | ||
9 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||
10 | * Copyright (c) 2003 Zultys Technologies | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | * | ||
17 | */ | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <asm/reg.h> | ||
21 | #include <asm/ibm44x.h> | ||
22 | #include <asm/mmu.h> | ||
23 | |||
24 | /* | ||
25 | * Calculate 440GP clocks | ||
26 | */ | ||
27 | void __init ibm440gp_get_clocks(struct ibm44x_clocks* p, | ||
28 | unsigned int sys_clk, | ||
29 | unsigned int ser_clk) | ||
30 | { | ||
31 | u32 cpc0_sys0 = mfdcr(DCRN_CPC0_SYS0); | ||
32 | u32 cpc0_cr0 = mfdcr(DCRN_CPC0_CR0); | ||
33 | u32 opdv = ((cpc0_sys0 >> 10) & 0x3) + 1; | ||
34 | u32 epdv = ((cpc0_sys0 >> 8) & 0x3) + 1; | ||
35 | |||
36 | if (cpc0_sys0 & 0x2){ | ||
37 | /* Bypass system PLL */ | ||
38 | p->cpu = p->plb = sys_clk; | ||
39 | } | ||
40 | else { | ||
41 | u32 fbdv, fwdva, fwdvb, m, vco; | ||
42 | |||
43 | fbdv = (cpc0_sys0 >> 18) & 0x0f; | ||
44 | if (!fbdv) | ||
45 | fbdv = 16; | ||
46 | |||
47 | fwdva = 8 - ((cpc0_sys0 >> 15) & 0x7); | ||
48 | fwdvb = 8 - ((cpc0_sys0 >> 12) & 0x7); | ||
49 | |||
50 | /* Feedback path */ | ||
51 | if (cpc0_sys0 & 0x00000080){ | ||
52 | /* PerClk */ | ||
53 | m = fwdvb * opdv * epdv; | ||
54 | } | ||
55 | else { | ||
56 | /* CPU clock */ | ||
57 | m = fbdv * fwdva; | ||
58 | } | ||
59 | vco = sys_clk * m; | ||
60 | p->cpu = vco / fwdva; | ||
61 | p->plb = vco / fwdvb; | ||
62 | } | ||
63 | |||
64 | p->opb = p->plb / opdv; | ||
65 | p->ebc = p->opb / epdv; | ||
66 | |||
67 | if (cpc0_cr0 & 0x00400000){ | ||
68 | /* External UART clock */ | ||
69 | p->uart0 = p->uart1 = ser_clk; | ||
70 | } | ||
71 | else { | ||
72 | /* Internal UART clock */ | ||
73 | u32 uart_div = ((cpc0_cr0 >> 16) & 0x1f) + 1; | ||
74 | p->uart0 = p->uart1 = p->plb / uart_div; | ||
75 | } | ||
76 | } | ||
diff --git a/arch/ppc/syslib/ibm440gp_common.h b/arch/ppc/syslib/ibm440gp_common.h new file mode 100644 index 000000000000..a054d83cb1ac --- /dev/null +++ b/arch/ppc/syslib/ibm440gp_common.h | |||
@@ -0,0 +1,35 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/ibm440gp_common.h | ||
3 | * | ||
4 | * PPC440GP system library | ||
5 | * | ||
6 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||
7 | * Copyright (c) 2003 Zultys Technologies | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | */ | ||
15 | #ifdef __KERNEL__ | ||
16 | #ifndef __PPC_SYSLIB_IBM440GP_COMMON_H | ||
17 | #define __PPC_SYSLIB_IBM440GP_COMMON_H | ||
18 | |||
19 | #ifndef __ASSEMBLY__ | ||
20 | |||
21 | #include <linux/config.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <syslib/ibm44x_common.h> | ||
24 | |||
25 | /* | ||
26 | * Please, refer to the Figure 13.1 in 440GP user manual | ||
27 | * | ||
28 | * if internal UART clock is used, ser_clk is ignored | ||
29 | */ | ||
30 | void ibm440gp_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk, | ||
31 | unsigned int ser_clk) __init; | ||
32 | |||
33 | #endif /* __ASSEMBLY__ */ | ||
34 | #endif /* __PPC_SYSLIB_IBM440GP_COMMON_H */ | ||
35 | #endif /* __KERNEL__ */ | ||
diff --git a/arch/ppc/syslib/ibm440gx_common.c b/arch/ppc/syslib/ibm440gx_common.c new file mode 100644 index 000000000000..4ad85e0e0234 --- /dev/null +++ b/arch/ppc/syslib/ibm440gx_common.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/ibm440gx_common.c | ||
3 | * | ||
4 | * PPC440GX system library | ||
5 | * | ||
6 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||
7 | * Copyright (c) 2003, 2004 Zultys Technologies | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | */ | ||
15 | #include <linux/config.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <asm/ibm44x.h> | ||
19 | #include <asm/mmu.h> | ||
20 | #include <asm/processor.h> | ||
21 | #include <syslib/ibm440gx_common.h> | ||
22 | |||
23 | /* | ||
24 | * Calculate 440GX clocks | ||
25 | */ | ||
26 | static inline u32 __fix_zero(u32 v, u32 def){ | ||
27 | return v ? v : def; | ||
28 | } | ||
29 | |||
30 | void __init ibm440gx_get_clocks(struct ibm44x_clocks* p, unsigned int sys_clk, | ||
31 | unsigned int ser_clk) | ||
32 | { | ||
33 | u32 pllc = CPR_READ(DCRN_CPR_PLLC); | ||
34 | u32 plld = CPR_READ(DCRN_CPR_PLLD); | ||
35 | u32 uart0 = SDR_READ(DCRN_SDR_UART0); | ||
36 | u32 uart1 = SDR_READ(DCRN_SDR_UART1); | ||
37 | |||
38 | /* Dividers */ | ||
39 | u32 fbdv = __fix_zero((plld >> 24) & 0x1f, 32); | ||
40 | u32 fwdva = __fix_zero((plld >> 16) & 0xf, 16); | ||
41 | u32 fwdvb = __fix_zero((plld >> 8) & 7, 8); | ||
42 | u32 lfbdv = __fix_zero(plld & 0x3f, 64); | ||
43 | u32 pradv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMAD) >> 24) & 7, 8); | ||
44 | u32 prbdv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMBD) >> 24) & 7, 8); | ||
45 | u32 opbdv0 = __fix_zero((CPR_READ(DCRN_CPR_OPBD) >> 24) & 3, 4); | ||
46 | u32 perdv0 = __fix_zero((CPR_READ(DCRN_CPR_PERD) >> 24) & 3, 4); | ||
47 | |||
48 | /* Input clocks for primary dividers */ | ||
49 | u32 clk_a, clk_b; | ||
50 | |||
51 | if (pllc & 0x40000000){ | ||
52 | u32 m; | ||
53 | |||
54 | /* Feedback path */ | ||
55 | switch ((pllc >> 24) & 7){ | ||
56 | case 0: | ||
57 | /* PLLOUTx */ | ||
58 | m = ((pllc & 0x20000000) ? fwdvb : fwdva) * lfbdv; | ||
59 | break; | ||
60 | case 1: | ||
61 | /* CPU */ | ||
62 | m = fwdva * pradv0; | ||
63 | break; | ||
64 | case 5: | ||
65 | /* PERClk */ | ||
66 | m = fwdvb * prbdv0 * opbdv0 * perdv0; | ||
67 | break; | ||
68 | default: | ||
69 | printk(KERN_EMERG "invalid PLL feedback source\n"); | ||
70 | goto bypass; | ||
71 | } | ||
72 | m *= fbdv; | ||
73 | p->vco = sys_clk * m; | ||
74 | clk_a = p->vco / fwdva; | ||
75 | clk_b = p->vco / fwdvb; | ||
76 | } | ||
77 | else { | ||
78 | bypass: | ||
79 | /* Bypass system PLL */ | ||
80 | p->vco = 0; | ||
81 | clk_a = clk_b = sys_clk; | ||
82 | } | ||
83 | |||
84 | p->cpu = clk_a / pradv0; | ||
85 | p->plb = clk_b / prbdv0; | ||
86 | p->opb = p->plb / opbdv0; | ||
87 | p->ebc = p->opb / perdv0; | ||
88 | |||
89 | /* UARTs clock */ | ||
90 | if (uart0 & 0x00800000) | ||
91 | p->uart0 = ser_clk; | ||
92 | else | ||
93 | p->uart0 = p->plb / __fix_zero(uart0 & 0xff, 256); | ||
94 | |||
95 | if (uart1 & 0x00800000) | ||
96 | p->uart1 = ser_clk; | ||
97 | else | ||
98 | p->uart1 = p->plb / __fix_zero(uart1 & 0xff, 256); | ||
99 | } | ||
100 | |||
101 | /* Issue L2C diagnostic command */ | ||
102 | static inline u32 l2c_diag(u32 addr) | ||
103 | { | ||
104 | mtdcr(DCRN_L2C0_ADDR, addr); | ||
105 | mtdcr(DCRN_L2C0_CMD, L2C_CMD_DIAG); | ||
106 | while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ; | ||
107 | return mfdcr(DCRN_L2C0_DATA); | ||
108 | } | ||
109 | |||
110 | static irqreturn_t l2c_error_handler(int irq, void* dev, struct pt_regs* regs) | ||
111 | { | ||
112 | u32 sr = mfdcr(DCRN_L2C0_SR); | ||
113 | if (sr & L2C_SR_CPE){ | ||
114 | /* Read cache trapped address */ | ||
115 | u32 addr = l2c_diag(0x42000000); | ||
116 | printk(KERN_EMERG "L2C: Cache Parity Error, addr[16:26] = 0x%08x\n", addr); | ||
117 | } | ||
118 | if (sr & L2C_SR_TPE){ | ||
119 | /* Read tag trapped address */ | ||
120 | u32 addr = l2c_diag(0x82000000) >> 16; | ||
121 | printk(KERN_EMERG "L2C: Tag Parity Error, addr[16:26] = 0x%08x\n", addr); | ||
122 | } | ||
123 | |||
124 | /* Clear parity errors */ | ||
125 | if (sr & (L2C_SR_CPE | L2C_SR_TPE)){ | ||
126 | mtdcr(DCRN_L2C0_ADDR, 0); | ||
127 | mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE); | ||
128 | } else | ||
129 | printk(KERN_EMERG "L2C: LRU error\n"); | ||
130 | |||
131 | return IRQ_HANDLED; | ||
132 | } | ||
133 | |||
134 | /* Enable L2 cache */ | ||
135 | void __init ibm440gx_l2c_enable(void){ | ||
136 | u32 r; | ||
137 | unsigned long flags; | ||
138 | |||
139 | /* Install error handler */ | ||
140 | if (request_irq(87, l2c_error_handler, SA_INTERRUPT, "L2C", 0) < 0){ | ||
141 | printk(KERN_ERR "Cannot install L2C error handler, cache is not enabled\n"); | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | local_irq_save(flags); | ||
146 | asm volatile ("sync" ::: "memory"); | ||
147 | |||
148 | /* Disable SRAM */ | ||
149 | mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) & ~SRAM_DPC_ENABLE); | ||
150 | mtdcr(DCRN_SRAM0_SB0CR, mfdcr(DCRN_SRAM0_SB0CR) & ~SRAM_SBCR_BU_MASK); | ||
151 | mtdcr(DCRN_SRAM0_SB1CR, mfdcr(DCRN_SRAM0_SB1CR) & ~SRAM_SBCR_BU_MASK); | ||
152 | mtdcr(DCRN_SRAM0_SB2CR, mfdcr(DCRN_SRAM0_SB2CR) & ~SRAM_SBCR_BU_MASK); | ||
153 | mtdcr(DCRN_SRAM0_SB3CR, mfdcr(DCRN_SRAM0_SB3CR) & ~SRAM_SBCR_BU_MASK); | ||
154 | |||
155 | /* Enable L2_MODE without ICU/DCU */ | ||
156 | r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_SS_MASK); | ||
157 | r |= L2C_CFG_L2M | L2C_CFG_SS_256; | ||
158 | mtdcr(DCRN_L2C0_CFG, r); | ||
159 | |||
160 | mtdcr(DCRN_L2C0_ADDR, 0); | ||
161 | |||
162 | /* Hardware Clear Command */ | ||
163 | mtdcr(DCRN_L2C0_CMD, L2C_CMD_HCC); | ||
164 | while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ; | ||
165 | |||
166 | /* Clear Cache Parity and Tag Errors */ | ||
167 | mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE); | ||
168 | |||
169 | /* Enable 64G snoop region starting at 0 */ | ||
170 | r = mfdcr(DCRN_L2C0_SNP0) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK); | ||
171 | r |= L2C_SNP_SSR_32G | L2C_SNP_ESR; | ||
172 | mtdcr(DCRN_L2C0_SNP0, r); | ||
173 | |||
174 | r = mfdcr(DCRN_L2C0_SNP1) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK); | ||
175 | r |= 0x80000000 | L2C_SNP_SSR_32G | L2C_SNP_ESR; | ||
176 | mtdcr(DCRN_L2C0_SNP1, r); | ||
177 | |||
178 | asm volatile ("sync" ::: "memory"); | ||
179 | |||
180 | /* Enable ICU/DCU ports */ | ||
181 | r = mfdcr(DCRN_L2C0_CFG); | ||
182 | r &= ~(L2C_CFG_DCW_MASK | L2C_CFG_PMUX_MASK | L2C_CFG_PMIM | L2C_CFG_TPEI | ||
183 | | L2C_CFG_CPEI | L2C_CFG_NAM | L2C_CFG_NBRM); | ||
184 | r |= L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN | ||
185 | | L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM | L2C_CFG_SMCM; | ||
186 | mtdcr(DCRN_L2C0_CFG, r); | ||
187 | |||
188 | asm volatile ("sync; isync" ::: "memory"); | ||
189 | local_irq_restore(flags); | ||
190 | } | ||
191 | |||
192 | /* Disable L2 cache */ | ||
193 | void __init ibm440gx_l2c_disable(void){ | ||
194 | u32 r; | ||
195 | unsigned long flags; | ||
196 | |||
197 | local_irq_save(flags); | ||
198 | asm volatile ("sync" ::: "memory"); | ||
199 | |||
200 | /* Disable L2C mode */ | ||
201 | r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_L2M | L2C_CFG_ICU | L2C_CFG_DCU); | ||
202 | mtdcr(DCRN_L2C0_CFG, r); | ||
203 | |||
204 | /* Enable SRAM */ | ||
205 | mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) | SRAM_DPC_ENABLE); | ||
206 | mtdcr(DCRN_SRAM0_SB0CR, | ||
207 | SRAM_SBCR_BAS0 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); | ||
208 | mtdcr(DCRN_SRAM0_SB1CR, | ||
209 | SRAM_SBCR_BAS1 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); | ||
210 | mtdcr(DCRN_SRAM0_SB2CR, | ||
211 | SRAM_SBCR_BAS2 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); | ||
212 | mtdcr(DCRN_SRAM0_SB3CR, | ||
213 | SRAM_SBCR_BAS3 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW); | ||
214 | |||
215 | asm volatile ("sync; isync" ::: "memory"); | ||
216 | local_irq_restore(flags); | ||
217 | } | ||
218 | |||
219 | void __init ibm440gx_l2c_setup(struct ibm44x_clocks* p) | ||
220 | { | ||
221 | /* Disable L2C on rev.A, rev.B and 800MHz version of rev.C, | ||
222 | enable it on all other revisions | ||
223 | */ | ||
224 | u32 pvr = mfspr(SPRN_PVR); | ||
225 | if (pvr == PVR_440GX_RA || pvr == PVR_440GX_RB || | ||
226 | (pvr == PVR_440GX_RC && p->cpu > 667000000)) | ||
227 | ibm440gx_l2c_disable(); | ||
228 | else | ||
229 | ibm440gx_l2c_enable(); | ||
230 | } | ||
231 | |||
232 | int __init ibm440gx_get_eth_grp(void) | ||
233 | { | ||
234 | return (SDR_READ(DCRN_SDR_PFC1) & DCRN_SDR_PFC1_EPS) >> DCRN_SDR_PFC1_EPS_SHIFT; | ||
235 | } | ||
236 | |||
237 | void __init ibm440gx_set_eth_grp(int group) | ||
238 | { | ||
239 | SDR_WRITE(DCRN_SDR_PFC1, (SDR_READ(DCRN_SDR_PFC1) & ~DCRN_SDR_PFC1_EPS) | (group << DCRN_SDR_PFC1_EPS_SHIFT)); | ||
240 | } | ||
241 | |||
242 | void __init ibm440gx_tah_enable(void) | ||
243 | { | ||
244 | /* Enable TAH0 and TAH1 */ | ||
245 | SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) & | ||
246 | ~DCRN_SDR_MFR_TAH0); | ||
247 | SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) & | ||
248 | ~DCRN_SDR_MFR_TAH1); | ||
249 | } | ||
250 | |||
251 | int ibm440gx_show_cpuinfo(struct seq_file *m){ | ||
252 | |||
253 | u32 l2c_cfg = mfdcr(DCRN_L2C0_CFG); | ||
254 | const char* s; | ||
255 | if (l2c_cfg & L2C_CFG_L2M){ | ||
256 | switch (l2c_cfg & (L2C_CFG_ICU | L2C_CFG_DCU)){ | ||
257 | case L2C_CFG_ICU: s = "I-Cache only"; break; | ||
258 | case L2C_CFG_DCU: s = "D-Cache only"; break; | ||
259 | default: s = "I-Cache/D-Cache"; break; | ||
260 | } | ||
261 | } | ||
262 | else | ||
263 | s = "disabled"; | ||
264 | |||
265 | seq_printf(m, "L2-Cache\t: %s (0x%08x 0x%08x)\n", s, | ||
266 | l2c_cfg, mfdcr(DCRN_L2C0_SR)); | ||
267 | |||
268 | return 0; | ||
269 | } | ||
270 | |||
diff --git a/arch/ppc/syslib/ibm440gx_common.h b/arch/ppc/syslib/ibm440gx_common.h new file mode 100644 index 000000000000..e73aa0411d35 --- /dev/null +++ b/arch/ppc/syslib/ibm440gx_common.h | |||
@@ -0,0 +1,57 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/ibm440gx_common.h | ||
3 | * | ||
4 | * PPC440GX system library | ||
5 | * | ||
6 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||
7 | * Copyright (c) 2003, 2004 Zultys Technologies | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | */ | ||
15 | #ifdef __KERNEL__ | ||
16 | #ifndef __PPC_SYSLIB_IBM440GX_COMMON_H | ||
17 | #define __PPC_SYSLIB_IBM440GX_COMMON_H | ||
18 | |||
19 | #ifndef __ASSEMBLY__ | ||
20 | |||
21 | #include <linux/config.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/seq_file.h> | ||
24 | #include <syslib/ibm44x_common.h> | ||
25 | |||
26 | /* | ||
27 | * Please, refer to the Figure 14.1 in 440GX user manual | ||
28 | * | ||
29 | * if internal UART clock is used, ser_clk is ignored | ||
30 | */ | ||
31 | void ibm440gx_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk, | ||
32 | unsigned int ser_clk) __init; | ||
33 | |||
34 | /* Enable L2 cache */ | ||
35 | void ibm440gx_l2c_enable(void) __init; | ||
36 | |||
37 | /* Disable L2 cache */ | ||
38 | void ibm440gx_l2c_disable(void) __init; | ||
39 | |||
40 | /* Enable/disable L2 cache for a particular chip revision */ | ||
41 | void ibm440gx_l2c_setup(struct ibm44x_clocks*) __init; | ||
42 | |||
43 | /* Get Ethernet Group */ | ||
44 | int ibm440gx_get_eth_grp(void) __init; | ||
45 | |||
46 | /* Set Ethernet Group */ | ||
47 | void ibm440gx_set_eth_grp(int group) __init; | ||
48 | |||
49 | /* Enable TAH devices */ | ||
50 | void ibm440gx_tah_enable(void) __init; | ||
51 | |||
52 | /* Add L2C info to /proc/cpuinfo */ | ||
53 | int ibm440gx_show_cpuinfo(struct seq_file*); | ||
54 | |||
55 | #endif /* __ASSEMBLY__ */ | ||
56 | #endif /* __PPC_SYSLIB_IBM440GX_COMMON_H */ | ||
57 | #endif /* __KERNEL__ */ | ||
diff --git a/arch/ppc/syslib/ibm440sp_common.c b/arch/ppc/syslib/ibm440sp_common.c new file mode 100644 index 000000000000..417d4cff77a0 --- /dev/null +++ b/arch/ppc/syslib/ibm440sp_common.c | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ibm440sp_common.c | ||
3 | * | ||
4 | * PPC440SP system library | ||
5 | * | ||
6 | * Matt Porter <mporter@kernel.crashing.org> | ||
7 | * Copyright 2002-2005 MontaVista Software Inc. | ||
8 | * | ||
9 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||
10 | * Copyright (c) 2003, 2004 Zultys Technologies | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | * | ||
17 | */ | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/types.h> | ||
20 | #include <linux/serial.h> | ||
21 | |||
22 | #include <asm/param.h> | ||
23 | #include <asm/ibm44x.h> | ||
24 | #include <asm/mmu.h> | ||
25 | #include <asm/machdep.h> | ||
26 | #include <asm/time.h> | ||
27 | #include <asm/ppc4xx_pic.h> | ||
28 | |||
29 | /* | ||
30 | * Read the 440SP memory controller to get size of system memory. | ||
31 | */ | ||
32 | unsigned long __init ibm440sp_find_end_of_memory(void) | ||
33 | { | ||
34 | u32 i; | ||
35 | u32 mem_size = 0; | ||
36 | |||
37 | /* Read two bank sizes and sum */ | ||
38 | for (i=0; i<2; i++) | ||
39 | switch (mfdcr(DCRN_MQ0_BS0BAS + i) & MQ0_CONFIG_SIZE_MASK) { | ||
40 | case MQ0_CONFIG_SIZE_8M: | ||
41 | mem_size += PPC44x_MEM_SIZE_8M; | ||
42 | break; | ||
43 | case MQ0_CONFIG_SIZE_16M: | ||
44 | mem_size += PPC44x_MEM_SIZE_16M; | ||
45 | break; | ||
46 | case MQ0_CONFIG_SIZE_32M: | ||
47 | mem_size += PPC44x_MEM_SIZE_32M; | ||
48 | break; | ||
49 | case MQ0_CONFIG_SIZE_64M: | ||
50 | mem_size += PPC44x_MEM_SIZE_64M; | ||
51 | break; | ||
52 | case MQ0_CONFIG_SIZE_128M: | ||
53 | mem_size += PPC44x_MEM_SIZE_128M; | ||
54 | break; | ||
55 | case MQ0_CONFIG_SIZE_256M: | ||
56 | mem_size += PPC44x_MEM_SIZE_256M; | ||
57 | break; | ||
58 | case MQ0_CONFIG_SIZE_512M: | ||
59 | mem_size += PPC44x_MEM_SIZE_512M; | ||
60 | break; | ||
61 | case MQ0_CONFIG_SIZE_1G: | ||
62 | mem_size += PPC44x_MEM_SIZE_1G; | ||
63 | break; | ||
64 | case MQ0_CONFIG_SIZE_2G: | ||
65 | mem_size += PPC44x_MEM_SIZE_2G; | ||
66 | break; | ||
67 | default: | ||
68 | break; | ||
69 | } | ||
70 | return mem_size; | ||
71 | } | ||
diff --git a/arch/ppc/syslib/ibm440sp_common.h b/arch/ppc/syslib/ibm440sp_common.h new file mode 100644 index 000000000000..a21a9906dcc9 --- /dev/null +++ b/arch/ppc/syslib/ibm440sp_common.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ibm440sp_common.h | ||
3 | * | ||
4 | * PPC440SP system library | ||
5 | * | ||
6 | * Matt Porter <mporter@kernel.crashing.org> | ||
7 | * Copyright 2004-2005 MontaVista Software, Inc. | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | */ | ||
15 | #ifdef __KERNEL__ | ||
16 | #ifndef __PPC_SYSLIB_IBM440SP_COMMON_H | ||
17 | #define __PPC_SYSLIB_IBM440SP_COMMON_H | ||
18 | |||
19 | #ifndef __ASSEMBLY__ | ||
20 | |||
21 | extern unsigned long __init ibm440sp_find_end_of_memory(void); | ||
22 | |||
23 | #endif /* __ASSEMBLY__ */ | ||
24 | #endif /* __PPC_SYSLIB_IBM440SP_COMMON_H */ | ||
25 | #endif /* __KERNEL__ */ | ||
diff --git a/arch/ppc/syslib/ibm44x_common.c b/arch/ppc/syslib/ibm44x_common.c new file mode 100644 index 000000000000..7612e0623f99 --- /dev/null +++ b/arch/ppc/syslib/ibm44x_common.c | |||
@@ -0,0 +1,193 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ibm44x_common.c | ||
3 | * | ||
4 | * PPC44x system library | ||
5 | * | ||
6 | * Matt Porter <mporter@kernel.crashing.org> | ||
7 | * Copyright 2002-2005 MontaVista Software Inc. | ||
8 | * | ||
9 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||
10 | * Copyright (c) 2003, 2004 Zultys Technologies | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License as published by the | ||
14 | * Free Software Foundation; either version 2 of the License, or (at your | ||
15 | * option) any later version. | ||
16 | * | ||
17 | */ | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/time.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/serial.h> | ||
22 | #include <linux/module.h> | ||
23 | |||
24 | #include <asm/ibm44x.h> | ||
25 | #include <asm/mmu.h> | ||
26 | #include <asm/machdep.h> | ||
27 | #include <asm/time.h> | ||
28 | #include <asm/ppc4xx_pic.h> | ||
29 | #include <asm/param.h> | ||
30 | |||
31 | #include <syslib/gen550.h> | ||
32 | |||
33 | phys_addr_t fixup_bigphys_addr(phys_addr_t addr, phys_addr_t size) | ||
34 | { | ||
35 | phys_addr_t page_4gb = 0; | ||
36 | |||
37 | /* | ||
38 | * Trap the least significant 32-bit portions of an | ||
39 | * address in the 440's 36-bit address space. Fix | ||
40 | * them up with the appropriate ERPN | ||
41 | */ | ||
42 | if ((addr >= PPC44x_IO_LO) && (addr <= PPC44x_IO_HI)) | ||
43 | page_4gb = PPC44x_IO_PAGE; | ||
44 | else if ((addr >= PPC44x_PCI0CFG_LO) && (addr <= PPC44x_PCI0CFG_HI)) | ||
45 | page_4gb = PPC44x_PCICFG_PAGE; | ||
46 | #ifdef CONFIG_440SP | ||
47 | else if ((addr >= PPC44x_PCI1CFG_LO) && (addr <= PPC44x_PCI1CFG_HI)) | ||
48 | page_4gb = PPC44x_PCICFG_PAGE; | ||
49 | else if ((addr >= PPC44x_PCI2CFG_LO) && (addr <= PPC44x_PCI2CFG_HI)) | ||
50 | page_4gb = PPC44x_PCICFG_PAGE; | ||
51 | #endif | ||
52 | else if ((addr >= PPC44x_PCIMEM_LO) && (addr <= PPC44x_PCIMEM_HI)) | ||
53 | page_4gb = PPC44x_PCIMEM_PAGE; | ||
54 | |||
55 | return (page_4gb | addr); | ||
56 | }; | ||
57 | EXPORT_SYMBOL(fixup_bigphys_addr); | ||
58 | |||
59 | void __init ibm44x_calibrate_decr(unsigned int freq) | ||
60 | { | ||
61 | tb_ticks_per_jiffy = freq / HZ; | ||
62 | tb_to_us = mulhwu_scale_factor(freq, 1000000); | ||
63 | |||
64 | /* Set the time base to zero */ | ||
65 | mtspr(SPRN_TBWL, 0); | ||
66 | mtspr(SPRN_TBWU, 0); | ||
67 | |||
68 | /* Clear any pending timer interrupts */ | ||
69 | mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); | ||
70 | |||
71 | /* Enable decrementer interrupt */ | ||
72 | mtspr(SPRN_TCR, TCR_DIE); | ||
73 | } | ||
74 | |||
75 | extern void abort(void); | ||
76 | |||
77 | static void ibm44x_restart(char *cmd) | ||
78 | { | ||
79 | local_irq_disable(); | ||
80 | abort(); | ||
81 | } | ||
82 | |||
83 | static void ibm44x_power_off(void) | ||
84 | { | ||
85 | local_irq_disable(); | ||
86 | for(;;); | ||
87 | } | ||
88 | |||
89 | static void ibm44x_halt(void) | ||
90 | { | ||
91 | local_irq_disable(); | ||
92 | for(;;); | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Read the 44x memory controller to get size of system memory. | ||
97 | */ | ||
98 | static unsigned long __init ibm44x_find_end_of_memory(void) | ||
99 | { | ||
100 | u32 i, bank_config; | ||
101 | u32 mem_size = 0; | ||
102 | |||
103 | for (i=0; i<4; i++) | ||
104 | { | ||
105 | switch (i) | ||
106 | { | ||
107 | case 0: | ||
108 | mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B0CR); | ||
109 | break; | ||
110 | case 1: | ||
111 | mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B1CR); | ||
112 | break; | ||
113 | case 2: | ||
114 | mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B2CR); | ||
115 | break; | ||
116 | case 3: | ||
117 | mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B3CR); | ||
118 | break; | ||
119 | } | ||
120 | |||
121 | bank_config = mfdcr(DCRN_SDRAM0_CFGDATA); | ||
122 | |||
123 | if (!(bank_config & SDRAM_CONFIG_BANK_ENABLE)) | ||
124 | continue; | ||
125 | switch (SDRAM_CONFIG_BANK_SIZE(bank_config)) | ||
126 | { | ||
127 | case SDRAM_CONFIG_SIZE_8M: | ||
128 | mem_size += PPC44x_MEM_SIZE_8M; | ||
129 | break; | ||
130 | case SDRAM_CONFIG_SIZE_16M: | ||
131 | mem_size += PPC44x_MEM_SIZE_16M; | ||
132 | break; | ||
133 | case SDRAM_CONFIG_SIZE_32M: | ||
134 | mem_size += PPC44x_MEM_SIZE_32M; | ||
135 | break; | ||
136 | case SDRAM_CONFIG_SIZE_64M: | ||
137 | mem_size += PPC44x_MEM_SIZE_64M; | ||
138 | break; | ||
139 | case SDRAM_CONFIG_SIZE_128M: | ||
140 | mem_size += PPC44x_MEM_SIZE_128M; | ||
141 | break; | ||
142 | case SDRAM_CONFIG_SIZE_256M: | ||
143 | mem_size += PPC44x_MEM_SIZE_256M; | ||
144 | break; | ||
145 | case SDRAM_CONFIG_SIZE_512M: | ||
146 | mem_size += PPC44x_MEM_SIZE_512M; | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | return mem_size; | ||
151 | } | ||
152 | |||
153 | void __init ibm44x_platform_init(void) | ||
154 | { | ||
155 | ppc_md.init_IRQ = ppc4xx_pic_init; | ||
156 | ppc_md.find_end_of_memory = ibm44x_find_end_of_memory; | ||
157 | ppc_md.restart = ibm44x_restart; | ||
158 | ppc_md.power_off = ibm44x_power_off; | ||
159 | ppc_md.halt = ibm44x_halt; | ||
160 | |||
161 | #ifdef CONFIG_SERIAL_TEXT_DEBUG | ||
162 | ppc_md.progress = gen550_progress; | ||
163 | #endif /* CONFIG_SERIAL_TEXT_DEBUG */ | ||
164 | #ifdef CONFIG_KGDB | ||
165 | ppc_md.kgdb_map_scc = gen550_kgdb_map_scc; | ||
166 | #endif | ||
167 | |||
168 | /* | ||
169 | * The Abatron BDI JTAG debugger does not tolerate others | ||
170 | * mucking with the debug registers. | ||
171 | */ | ||
172 | #if !defined(CONFIG_BDI_SWITCH) | ||
173 | /* Enable internal debug mode */ | ||
174 | mtspr(SPRN_DBCR0, (DBCR0_IDM)); | ||
175 | |||
176 | /* Clear any residual debug events */ | ||
177 | mtspr(SPRN_DBSR, 0xffffffff); | ||
178 | #endif | ||
179 | } | ||
180 | |||
181 | /* Called from MachineCheckException */ | ||
182 | void platform_machine_check(struct pt_regs *regs) | ||
183 | { | ||
184 | printk("PLB0: BEAR=0x%08x%08x ACR= 0x%08x BESR= 0x%08x\n", | ||
185 | mfdcr(DCRN_PLB0_BEARH), mfdcr(DCRN_PLB0_BEARL), | ||
186 | mfdcr(DCRN_PLB0_ACR), mfdcr(DCRN_PLB0_BESR)); | ||
187 | printk("POB0: BEAR=0x%08x%08x BESR0=0x%08x BESR1=0x%08x\n", | ||
188 | mfdcr(DCRN_POB0_BEARH), mfdcr(DCRN_POB0_BEARL), | ||
189 | mfdcr(DCRN_POB0_BESR0), mfdcr(DCRN_POB0_BESR1)); | ||
190 | printk("OPB0: BEAR=0x%08x%08x BSTAT=0x%08x\n", | ||
191 | mfdcr(DCRN_OPB0_BEARH), mfdcr(DCRN_OPB0_BEARL), | ||
192 | mfdcr(DCRN_OPB0_BSTAT)); | ||
193 | } | ||
diff --git a/arch/ppc/syslib/ibm44x_common.h b/arch/ppc/syslib/ibm44x_common.h new file mode 100644 index 000000000000..b14eb603ce01 --- /dev/null +++ b/arch/ppc/syslib/ibm44x_common.h | |||
@@ -0,0 +1,42 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/ibm44x_common.h | ||
3 | * | ||
4 | * PPC44x system library | ||
5 | * | ||
6 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||
7 | * Copyright (c) 2003, 2004 Zultys Technologies | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify it | ||
10 | * under the terms of the GNU General Public License as published by the | ||
11 | * Free Software Foundation; either version 2 of the License, or (at your | ||
12 | * option) any later version. | ||
13 | * | ||
14 | */ | ||
15 | #ifdef __KERNEL__ | ||
16 | #ifndef __PPC_SYSLIB_IBM44x_COMMON_H | ||
17 | #define __PPC_SYSLIB_IBM44x_COMMON_H | ||
18 | |||
19 | #ifndef __ASSEMBLY__ | ||
20 | |||
21 | /* | ||
22 | * All clocks are in Hz | ||
23 | */ | ||
24 | struct ibm44x_clocks { | ||
25 | unsigned int vco; /* VCO, 0 if system PLL is bypassed */ | ||
26 | unsigned int cpu; /* CPUCoreClk */ | ||
27 | unsigned int plb; /* PLBClk */ | ||
28 | unsigned int opb; /* OPBClk */ | ||
29 | unsigned int ebc; /* PerClk */ | ||
30 | unsigned int uart0; | ||
31 | unsigned int uart1; | ||
32 | }; | ||
33 | |||
34 | /* common 44x platform init */ | ||
35 | void ibm44x_platform_init(void) __init; | ||
36 | |||
37 | /* initialize decrementer and tick-related variables */ | ||
38 | void ibm44x_calibrate_decr(unsigned int freq) __init; | ||
39 | |||
40 | #endif /* __ASSEMBLY__ */ | ||
41 | #endif /* __PPC_SYSLIB_IBM44x_COMMON_H */ | ||
42 | #endif /* __KERNEL__ */ | ||
diff --git a/arch/ppc/syslib/ibm_ocp.c b/arch/ppc/syslib/ibm_ocp.c new file mode 100644 index 000000000000..3f6e55c79181 --- /dev/null +++ b/arch/ppc/syslib/ibm_ocp.c | |||
@@ -0,0 +1,9 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <asm/ocp.h> | ||
3 | |||
4 | struct ocp_sys_info_data ocp_sys_info = { | ||
5 | .opb_bus_freq = 50000000, /* OPB Bus Frequency (Hz) */ | ||
6 | .ebc_bus_freq = 33333333, /* EBC Bus Frequency (Hz) */ | ||
7 | }; | ||
8 | |||
9 | EXPORT_SYMBOL(ocp_sys_info); | ||
diff --git a/arch/ppc/syslib/indirect_pci.c b/arch/ppc/syslib/indirect_pci.c new file mode 100644 index 000000000000..a5a752609e2c --- /dev/null +++ b/arch/ppc/syslib/indirect_pci.c | |||
@@ -0,0 +1,135 @@ | |||
1 | /* | ||
2 | * Support for indirect PCI bridges. | ||
3 | * | ||
4 | * Copyright (C) 1998 Gabriel Paubert. | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or | ||
7 | * modify it under the terms of the GNU General Public License | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the License, or (at your option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/pci.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/string.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/bootmem.h> | ||
18 | |||
19 | #include <asm/io.h> | ||
20 | #include <asm/prom.h> | ||
21 | #include <asm/pci-bridge.h> | ||
22 | #include <asm/machdep.h> | ||
23 | |||
24 | #ifdef CONFIG_PPC_INDIRECT_PCI_BE | ||
25 | #define PCI_CFG_OUT out_be32 | ||
26 | #else | ||
27 | #define PCI_CFG_OUT out_le32 | ||
28 | #endif | ||
29 | |||
30 | static int | ||
31 | indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset, | ||
32 | int len, u32 *val) | ||
33 | { | ||
34 | struct pci_controller *hose = bus->sysdata; | ||
35 | volatile void __iomem *cfg_data; | ||
36 | u8 cfg_type = 0; | ||
37 | |||
38 | if (ppc_md.pci_exclude_device) | ||
39 | if (ppc_md.pci_exclude_device(bus->number, devfn)) | ||
40 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
41 | |||
42 | if (hose->set_cfg_type) | ||
43 | if (bus->number != hose->first_busno) | ||
44 | cfg_type = 1; | ||
45 | |||
46 | PCI_CFG_OUT(hose->cfg_addr, | ||
47 | (0x80000000 | ((bus->number - hose->bus_offset) << 16) | ||
48 | | (devfn << 8) | ((offset & 0xfc) | cfg_type))); | ||
49 | |||
50 | /* | ||
51 | * Note: the caller has already checked that offset is | ||
52 | * suitably aligned and that len is 1, 2 or 4. | ||
53 | */ | ||
54 | cfg_data = hose->cfg_data + (offset & 3); | ||
55 | switch (len) { | ||
56 | case 1: | ||
57 | *val = in_8(cfg_data); | ||
58 | break; | ||
59 | case 2: | ||
60 | *val = in_le16(cfg_data); | ||
61 | break; | ||
62 | default: | ||
63 | *val = in_le32(cfg_data); | ||
64 | break; | ||
65 | } | ||
66 | return PCIBIOS_SUCCESSFUL; | ||
67 | } | ||
68 | |||
69 | static int | ||
70 | indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset, | ||
71 | int len, u32 val) | ||
72 | { | ||
73 | struct pci_controller *hose = bus->sysdata; | ||
74 | volatile void __iomem *cfg_data; | ||
75 | u8 cfg_type = 0; | ||
76 | |||
77 | if (ppc_md.pci_exclude_device) | ||
78 | if (ppc_md.pci_exclude_device(bus->number, devfn)) | ||
79 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
80 | |||
81 | if (hose->set_cfg_type) | ||
82 | if (bus->number != hose->first_busno) | ||
83 | cfg_type = 1; | ||
84 | |||
85 | PCI_CFG_OUT(hose->cfg_addr, | ||
86 | (0x80000000 | ((bus->number - hose->bus_offset) << 16) | ||
87 | | (devfn << 8) | ((offset & 0xfc) | cfg_type))); | ||
88 | |||
89 | /* | ||
90 | * Note: the caller has already checked that offset is | ||
91 | * suitably aligned and that len is 1, 2 or 4. | ||
92 | */ | ||
93 | cfg_data = hose->cfg_data + (offset & 3); | ||
94 | switch (len) { | ||
95 | case 1: | ||
96 | out_8(cfg_data, val); | ||
97 | break; | ||
98 | case 2: | ||
99 | out_le16(cfg_data, val); | ||
100 | break; | ||
101 | default: | ||
102 | out_le32(cfg_data, val); | ||
103 | break; | ||
104 | } | ||
105 | return PCIBIOS_SUCCESSFUL; | ||
106 | } | ||
107 | |||
108 | static struct pci_ops indirect_pci_ops = | ||
109 | { | ||
110 | indirect_read_config, | ||
111 | indirect_write_config | ||
112 | }; | ||
113 | |||
114 | void __init | ||
115 | setup_indirect_pci_nomap(struct pci_controller* hose, void __iomem * cfg_addr, | ||
116 | void __iomem * cfg_data) | ||
117 | { | ||
118 | hose->cfg_addr = cfg_addr; | ||
119 | hose->cfg_data = cfg_data; | ||
120 | hose->ops = &indirect_pci_ops; | ||
121 | } | ||
122 | |||
123 | void __init | ||
124 | setup_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) | ||
125 | { | ||
126 | unsigned long base = cfg_addr & PAGE_MASK; | ||
127 | void __iomem *mbase, *addr, *data; | ||
128 | |||
129 | mbase = ioremap(base, PAGE_SIZE); | ||
130 | addr = mbase + (cfg_addr & ~PAGE_MASK); | ||
131 | if ((cfg_data & PAGE_MASK) != base) | ||
132 | mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE); | ||
133 | data = mbase + (cfg_data & ~PAGE_MASK); | ||
134 | setup_indirect_pci_nomap(hose, addr, data); | ||
135 | } | ||
diff --git a/arch/ppc/syslib/ipic.c b/arch/ppc/syslib/ipic.c new file mode 100644 index 000000000000..acb2cde3171f --- /dev/null +++ b/arch/ppc/syslib/ipic.c | |||
@@ -0,0 +1,646 @@ | |||
1 | /* | ||
2 | * include/asm-ppc/ipic.c | ||
3 | * | ||
4 | * IPIC routines implementations. | ||
5 | * | ||
6 | * Copyright 2005 Freescale Semiconductor, Inc. | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License as published by the | ||
10 | * Free Software Foundation; either version 2 of the License, or (at your | ||
11 | * option) any later version. | ||
12 | */ | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/reboot.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/stddef.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/signal.h> | ||
21 | #include <linux/sysdev.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/ipic.h> | ||
25 | #include <asm/mpc83xx.h> | ||
26 | |||
27 | #include "ipic.h" | ||
28 | |||
29 | static struct ipic p_ipic; | ||
30 | static struct ipic * primary_ipic; | ||
31 | |||
32 | static struct ipic_info ipic_info[] = { | ||
33 | [9] = { | ||
34 | .pend = IPIC_SIPNR_H, | ||
35 | .mask = IPIC_SIMSR_H, | ||
36 | .prio = IPIC_SIPRR_D, | ||
37 | .force = IPIC_SIFCR_H, | ||
38 | .bit = 24, | ||
39 | .prio_mask = 0, | ||
40 | }, | ||
41 | [10] = { | ||
42 | .pend = IPIC_SIPNR_H, | ||
43 | .mask = IPIC_SIMSR_H, | ||
44 | .prio = IPIC_SIPRR_D, | ||
45 | .force = IPIC_SIFCR_H, | ||
46 | .bit = 25, | ||
47 | .prio_mask = 1, | ||
48 | }, | ||
49 | [11] = { | ||
50 | .pend = IPIC_SIPNR_H, | ||
51 | .mask = IPIC_SIMSR_H, | ||
52 | .prio = IPIC_SIPRR_D, | ||
53 | .force = IPIC_SIFCR_H, | ||
54 | .bit = 26, | ||
55 | .prio_mask = 2, | ||
56 | }, | ||
57 | [14] = { | ||
58 | .pend = IPIC_SIPNR_H, | ||
59 | .mask = IPIC_SIMSR_H, | ||
60 | .prio = IPIC_SIPRR_D, | ||
61 | .force = IPIC_SIFCR_H, | ||
62 | .bit = 29, | ||
63 | .prio_mask = 5, | ||
64 | }, | ||
65 | [15] = { | ||
66 | .pend = IPIC_SIPNR_H, | ||
67 | .mask = IPIC_SIMSR_H, | ||
68 | .prio = IPIC_SIPRR_D, | ||
69 | .force = IPIC_SIFCR_H, | ||
70 | .bit = 30, | ||
71 | .prio_mask = 6, | ||
72 | }, | ||
73 | [16] = { | ||
74 | .pend = IPIC_SIPNR_H, | ||
75 | .mask = IPIC_SIMSR_H, | ||
76 | .prio = IPIC_SIPRR_D, | ||
77 | .force = IPIC_SIFCR_H, | ||
78 | .bit = 31, | ||
79 | .prio_mask = 7, | ||
80 | }, | ||
81 | [17] = { | ||
82 | .pend = IPIC_SIPNR_H, | ||
83 | .mask = IPIC_SEMSR, | ||
84 | .prio = IPIC_SMPRR_A, | ||
85 | .force = IPIC_SEFCR, | ||
86 | .bit = 1, | ||
87 | .prio_mask = 5, | ||
88 | }, | ||
89 | [18] = { | ||
90 | .pend = IPIC_SIPNR_H, | ||
91 | .mask = IPIC_SEMSR, | ||
92 | .prio = IPIC_SMPRR_A, | ||
93 | .force = IPIC_SEFCR, | ||
94 | .bit = 2, | ||
95 | .prio_mask = 6, | ||
96 | }, | ||
97 | [19] = { | ||
98 | .pend = IPIC_SIPNR_H, | ||
99 | .mask = IPIC_SEMSR, | ||
100 | .prio = IPIC_SMPRR_A, | ||
101 | .force = IPIC_SEFCR, | ||
102 | .bit = 3, | ||
103 | .prio_mask = 7, | ||
104 | }, | ||
105 | [20] = { | ||
106 | .pend = IPIC_SIPNR_H, | ||
107 | .mask = IPIC_SEMSR, | ||
108 | .prio = IPIC_SMPRR_B, | ||
109 | .force = IPIC_SEFCR, | ||
110 | .bit = 4, | ||
111 | .prio_mask = 4, | ||
112 | }, | ||
113 | [21] = { | ||
114 | .pend = IPIC_SIPNR_H, | ||
115 | .mask = IPIC_SEMSR, | ||
116 | .prio = IPIC_SMPRR_B, | ||
117 | .force = IPIC_SEFCR, | ||
118 | .bit = 5, | ||
119 | .prio_mask = 5, | ||
120 | }, | ||
121 | [22] = { | ||
122 | .pend = IPIC_SIPNR_H, | ||
123 | .mask = IPIC_SEMSR, | ||
124 | .prio = IPIC_SMPRR_B, | ||
125 | .force = IPIC_SEFCR, | ||
126 | .bit = 6, | ||
127 | .prio_mask = 6, | ||
128 | }, | ||
129 | [23] = { | ||
130 | .pend = IPIC_SIPNR_H, | ||
131 | .mask = IPIC_SEMSR, | ||
132 | .prio = IPIC_SMPRR_B, | ||
133 | .force = IPIC_SEFCR, | ||
134 | .bit = 7, | ||
135 | .prio_mask = 7, | ||
136 | }, | ||
137 | [32] = { | ||
138 | .pend = IPIC_SIPNR_H, | ||
139 | .mask = IPIC_SIMSR_H, | ||
140 | .prio = IPIC_SIPRR_A, | ||
141 | .force = IPIC_SIFCR_H, | ||
142 | .bit = 0, | ||
143 | .prio_mask = 0, | ||
144 | }, | ||
145 | [33] = { | ||
146 | .pend = IPIC_SIPNR_H, | ||
147 | .mask = IPIC_SIMSR_H, | ||
148 | .prio = IPIC_SIPRR_A, | ||
149 | .force = IPIC_SIFCR_H, | ||
150 | .bit = 1, | ||
151 | .prio_mask = 1, | ||
152 | }, | ||
153 | [34] = { | ||
154 | .pend = IPIC_SIPNR_H, | ||
155 | .mask = IPIC_SIMSR_H, | ||
156 | .prio = IPIC_SIPRR_A, | ||
157 | .force = IPIC_SIFCR_H, | ||
158 | .bit = 2, | ||
159 | .prio_mask = 2, | ||
160 | }, | ||
161 | [35] = { | ||
162 | .pend = IPIC_SIPNR_H, | ||
163 | .mask = IPIC_SIMSR_H, | ||
164 | .prio = IPIC_SIPRR_A, | ||
165 | .force = IPIC_SIFCR_H, | ||
166 | .bit = 3, | ||
167 | .prio_mask = 3, | ||
168 | }, | ||
169 | [36] = { | ||
170 | .pend = IPIC_SIPNR_H, | ||
171 | .mask = IPIC_SIMSR_H, | ||
172 | .prio = IPIC_SIPRR_A, | ||
173 | .force = IPIC_SIFCR_H, | ||
174 | .bit = 4, | ||
175 | .prio_mask = 4, | ||
176 | }, | ||
177 | [37] = { | ||
178 | .pend = IPIC_SIPNR_H, | ||
179 | .mask = IPIC_SIMSR_H, | ||
180 | .prio = IPIC_SIPRR_A, | ||
181 | .force = IPIC_SIFCR_H, | ||
182 | .bit = 5, | ||
183 | .prio_mask = 5, | ||
184 | }, | ||
185 | [38] = { | ||
186 | .pend = IPIC_SIPNR_H, | ||
187 | .mask = IPIC_SIMSR_H, | ||
188 | .prio = IPIC_SIPRR_A, | ||
189 | .force = IPIC_SIFCR_H, | ||
190 | .bit = 6, | ||
191 | .prio_mask = 6, | ||
192 | }, | ||
193 | [39] = { | ||
194 | .pend = IPIC_SIPNR_H, | ||
195 | .mask = IPIC_SIMSR_H, | ||
196 | .prio = IPIC_SIPRR_A, | ||
197 | .force = IPIC_SIFCR_H, | ||
198 | .bit = 7, | ||
199 | .prio_mask = 7, | ||
200 | }, | ||
201 | [48] = { | ||
202 | .pend = IPIC_SEPNR, | ||
203 | .mask = IPIC_SEMSR, | ||
204 | .prio = IPIC_SMPRR_A, | ||
205 | .force = IPIC_SEFCR, | ||
206 | .bit = 0, | ||
207 | .prio_mask = 4, | ||
208 | }, | ||
209 | [64] = { | ||
210 | .pend = IPIC_SIPNR_H, | ||
211 | .mask = IPIC_SIMSR_L, | ||
212 | .prio = IPIC_SMPRR_A, | ||
213 | .force = IPIC_SIFCR_L, | ||
214 | .bit = 0, | ||
215 | .prio_mask = 0, | ||
216 | }, | ||
217 | [65] = { | ||
218 | .pend = IPIC_SIPNR_H, | ||
219 | .mask = IPIC_SIMSR_L, | ||
220 | .prio = IPIC_SMPRR_A, | ||
221 | .force = IPIC_SIFCR_L, | ||
222 | .bit = 1, | ||
223 | .prio_mask = 1, | ||
224 | }, | ||
225 | [66] = { | ||
226 | .pend = IPIC_SIPNR_H, | ||
227 | .mask = IPIC_SIMSR_L, | ||
228 | .prio = IPIC_SMPRR_A, | ||
229 | .force = IPIC_SIFCR_L, | ||
230 | .bit = 2, | ||
231 | .prio_mask = 2, | ||
232 | }, | ||
233 | [67] = { | ||
234 | .pend = IPIC_SIPNR_H, | ||
235 | .mask = IPIC_SIMSR_L, | ||
236 | .prio = IPIC_SMPRR_A, | ||
237 | .force = IPIC_SIFCR_L, | ||
238 | .bit = 3, | ||
239 | .prio_mask = 3, | ||
240 | }, | ||
241 | [68] = { | ||
242 | .pend = IPIC_SIPNR_H, | ||
243 | .mask = IPIC_SIMSR_L, | ||
244 | .prio = IPIC_SMPRR_B, | ||
245 | .force = IPIC_SIFCR_L, | ||
246 | .bit = 4, | ||
247 | .prio_mask = 0, | ||
248 | }, | ||
249 | [69] = { | ||
250 | .pend = IPIC_SIPNR_H, | ||
251 | .mask = IPIC_SIMSR_L, | ||
252 | .prio = IPIC_SMPRR_B, | ||
253 | .force = IPIC_SIFCR_L, | ||
254 | .bit = 5, | ||
255 | .prio_mask = 1, | ||
256 | }, | ||
257 | [70] = { | ||
258 | .pend = IPIC_SIPNR_H, | ||
259 | .mask = IPIC_SIMSR_L, | ||
260 | .prio = IPIC_SMPRR_B, | ||
261 | .force = IPIC_SIFCR_L, | ||
262 | .bit = 6, | ||
263 | .prio_mask = 2, | ||
264 | }, | ||
265 | [71] = { | ||
266 | .pend = IPIC_SIPNR_H, | ||
267 | .mask = IPIC_SIMSR_L, | ||
268 | .prio = IPIC_SMPRR_B, | ||
269 | .force = IPIC_SIFCR_L, | ||
270 | .bit = 7, | ||
271 | .prio_mask = 3, | ||
272 | }, | ||
273 | [72] = { | ||
274 | .pend = IPIC_SIPNR_H, | ||
275 | .mask = IPIC_SIMSR_L, | ||
276 | .prio = 0, | ||
277 | .force = IPIC_SIFCR_L, | ||
278 | .bit = 8, | ||
279 | }, | ||
280 | [73] = { | ||
281 | .pend = IPIC_SIPNR_H, | ||
282 | .mask = IPIC_SIMSR_L, | ||
283 | .prio = 0, | ||
284 | .force = IPIC_SIFCR_L, | ||
285 | .bit = 9, | ||
286 | }, | ||
287 | [74] = { | ||
288 | .pend = IPIC_SIPNR_H, | ||
289 | .mask = IPIC_SIMSR_L, | ||
290 | .prio = 0, | ||
291 | .force = IPIC_SIFCR_L, | ||
292 | .bit = 10, | ||
293 | }, | ||
294 | [75] = { | ||
295 | .pend = IPIC_SIPNR_H, | ||
296 | .mask = IPIC_SIMSR_L, | ||
297 | .prio = 0, | ||
298 | .force = IPIC_SIFCR_L, | ||
299 | .bit = 11, | ||
300 | }, | ||
301 | [76] = { | ||
302 | .pend = IPIC_SIPNR_H, | ||
303 | .mask = IPIC_SIMSR_L, | ||
304 | .prio = 0, | ||
305 | .force = IPIC_SIFCR_L, | ||
306 | .bit = 12, | ||
307 | }, | ||
308 | [77] = { | ||
309 | .pend = IPIC_SIPNR_H, | ||
310 | .mask = IPIC_SIMSR_L, | ||
311 | .prio = 0, | ||
312 | .force = IPIC_SIFCR_L, | ||
313 | .bit = 13, | ||
314 | }, | ||
315 | [78] = { | ||
316 | .pend = IPIC_SIPNR_H, | ||
317 | .mask = IPIC_SIMSR_L, | ||
318 | .prio = 0, | ||
319 | .force = IPIC_SIFCR_L, | ||
320 | .bit = 14, | ||
321 | }, | ||
322 | [79] = { | ||
323 | .pend = IPIC_SIPNR_H, | ||
324 | .mask = IPIC_SIMSR_L, | ||
325 | .prio = 0, | ||
326 | .force = IPIC_SIFCR_L, | ||
327 | .bit = 15, | ||
328 | }, | ||
329 | [80] = { | ||
330 | .pend = IPIC_SIPNR_H, | ||
331 | .mask = IPIC_SIMSR_L, | ||
332 | .prio = 0, | ||
333 | .force = IPIC_SIFCR_L, | ||
334 | .bit = 16, | ||
335 | }, | ||
336 | [84] = { | ||
337 | .pend = IPIC_SIPNR_H, | ||
338 | .mask = IPIC_SIMSR_L, | ||
339 | .prio = 0, | ||
340 | .force = IPIC_SIFCR_L, | ||
341 | .bit = 20, | ||
342 | }, | ||
343 | [85] = { | ||
344 | .pend = IPIC_SIPNR_H, | ||
345 | .mask = IPIC_SIMSR_L, | ||
346 | .prio = 0, | ||
347 | .force = IPIC_SIFCR_L, | ||
348 | .bit = 21, | ||
349 | }, | ||
350 | [90] = { | ||
351 | .pend = IPIC_SIPNR_H, | ||
352 | .mask = IPIC_SIMSR_L, | ||
353 | .prio = 0, | ||
354 | .force = IPIC_SIFCR_L, | ||
355 | .bit = 26, | ||
356 | }, | ||
357 | [91] = { | ||
358 | .pend = IPIC_SIPNR_H, | ||
359 | .mask = IPIC_SIMSR_L, | ||
360 | .prio = 0, | ||
361 | .force = IPIC_SIFCR_L, | ||
362 | .bit = 27, | ||
363 | }, | ||
364 | }; | ||
365 | |||
366 | static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg) | ||
367 | { | ||
368 | return in_be32(base + (reg >> 2)); | ||
369 | } | ||
370 | |||
371 | static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 value) | ||
372 | { | ||
373 | out_be32(base + (reg >> 2), value); | ||
374 | } | ||
375 | |||
376 | static inline struct ipic * ipic_from_irq(unsigned int irq) | ||
377 | { | ||
378 | return primary_ipic; | ||
379 | } | ||
380 | |||
381 | static void ipic_enable_irq(unsigned int irq) | ||
382 | { | ||
383 | struct ipic *ipic = ipic_from_irq(irq); | ||
384 | unsigned int src = irq - ipic->irq_offset; | ||
385 | u32 temp; | ||
386 | |||
387 | temp = ipic_read(ipic->regs, ipic_info[src].mask); | ||
388 | temp |= (1 << (31 - ipic_info[src].bit)); | ||
389 | ipic_write(ipic->regs, ipic_info[src].mask, temp); | ||
390 | } | ||
391 | |||
392 | static void ipic_disable_irq(unsigned int irq) | ||
393 | { | ||
394 | struct ipic *ipic = ipic_from_irq(irq); | ||
395 | unsigned int src = irq - ipic->irq_offset; | ||
396 | u32 temp; | ||
397 | |||
398 | temp = ipic_read(ipic->regs, ipic_info[src].mask); | ||
399 | temp &= ~(1 << (31 - ipic_info[src].bit)); | ||
400 | ipic_write(ipic->regs, ipic_info[src].mask, temp); | ||
401 | } | ||
402 | |||
403 | static void ipic_disable_irq_and_ack(unsigned int irq) | ||
404 | { | ||
405 | struct ipic *ipic = ipic_from_irq(irq); | ||
406 | unsigned int src = irq - ipic->irq_offset; | ||
407 | u32 temp; | ||
408 | |||
409 | ipic_disable_irq(irq); | ||
410 | |||
411 | temp = ipic_read(ipic->regs, ipic_info[src].pend); | ||
412 | temp |= (1 << (31 - ipic_info[src].bit)); | ||
413 | ipic_write(ipic->regs, ipic_info[src].pend, temp); | ||
414 | } | ||
415 | |||
416 | static void ipic_end_irq(unsigned int irq) | ||
417 | { | ||
418 | if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
419 | ipic_enable_irq(irq); | ||
420 | } | ||
421 | |||
422 | struct hw_interrupt_type ipic = { | ||
423 | .typename = " IPIC ", | ||
424 | .enable = ipic_enable_irq, | ||
425 | .disable = ipic_disable_irq, | ||
426 | .ack = ipic_disable_irq_and_ack, | ||
427 | .end = ipic_end_irq, | ||
428 | }; | ||
429 | |||
430 | void __init ipic_init(phys_addr_t phys_addr, | ||
431 | unsigned int flags, | ||
432 | unsigned int irq_offset, | ||
433 | unsigned char *senses, | ||
434 | unsigned int senses_count) | ||
435 | { | ||
436 | u32 i, temp = 0; | ||
437 | |||
438 | primary_ipic = &p_ipic; | ||
439 | primary_ipic->regs = ioremap(phys_addr, MPC83xx_IPIC_SIZE); | ||
440 | |||
441 | primary_ipic->irq_offset = irq_offset; | ||
442 | |||
443 | ipic_write(primary_ipic->regs, IPIC_SICNR, 0x0); | ||
444 | |||
445 | /* default priority scheme is grouped. If spread mode is required | ||
446 | * configure SICFR accordingly */ | ||
447 | if (flags & IPIC_SPREADMODE_GRP_A) | ||
448 | temp |= SICFR_IPSA; | ||
449 | if (flags & IPIC_SPREADMODE_GRP_D) | ||
450 | temp |= SICFR_IPSD; | ||
451 | if (flags & IPIC_SPREADMODE_MIX_A) | ||
452 | temp |= SICFR_MPSA; | ||
453 | if (flags & IPIC_SPREADMODE_MIX_B) | ||
454 | temp |= SICFR_MPSB; | ||
455 | |||
456 | ipic_write(primary_ipic->regs, IPIC_SICNR, temp); | ||
457 | |||
458 | /* handle MCP route */ | ||
459 | temp = 0; | ||
460 | if (flags & IPIC_DISABLE_MCP_OUT) | ||
461 | temp = SERCR_MCPR; | ||
462 | ipic_write(primary_ipic->regs, IPIC_SERCR, temp); | ||
463 | |||
464 | /* handle routing of IRQ0 to MCP */ | ||
465 | temp = ipic_read(primary_ipic->regs, IPIC_SEMSR); | ||
466 | |||
467 | if (flags & IPIC_IRQ0_MCP) | ||
468 | temp |= SEMSR_SIRQ0; | ||
469 | else | ||
470 | temp &= ~SEMSR_SIRQ0; | ||
471 | |||
472 | ipic_write(primary_ipic->regs, IPIC_SEMSR, temp); | ||
473 | |||
474 | for (i = 0 ; i < NR_IPIC_INTS ; i++) { | ||
475 | irq_desc[i+irq_offset].handler = &ipic; | ||
476 | irq_desc[i+irq_offset].status = IRQ_LEVEL; | ||
477 | } | ||
478 | |||
479 | temp = 0; | ||
480 | for (i = 0 ; i < senses_count ; i++) { | ||
481 | if ((senses[i] & IRQ_SENSE_MASK) == IRQ_SENSE_EDGE) { | ||
482 | temp |= 1 << (16 - i); | ||
483 | if (i != 0) | ||
484 | irq_desc[i + irq_offset + MPC83xx_IRQ_EXT1 - 1].status = 0; | ||
485 | else | ||
486 | irq_desc[irq_offset + MPC83xx_IRQ_EXT0].status = 0; | ||
487 | } | ||
488 | } | ||
489 | ipic_write(primary_ipic->regs, IPIC_SECNR, temp); | ||
490 | |||
491 | printk ("IPIC (%d IRQ sources, %d External IRQs) at %p\n", NR_IPIC_INTS, | ||
492 | senses_count, primary_ipic->regs); | ||
493 | } | ||
494 | |||
495 | int ipic_set_priority(unsigned int irq, unsigned int priority) | ||
496 | { | ||
497 | struct ipic *ipic = ipic_from_irq(irq); | ||
498 | unsigned int src = irq - ipic->irq_offset; | ||
499 | u32 temp; | ||
500 | |||
501 | if (priority > 7) | ||
502 | return -EINVAL; | ||
503 | if (src > 127) | ||
504 | return -EINVAL; | ||
505 | if (ipic_info[src].prio == 0) | ||
506 | return -EINVAL; | ||
507 | |||
508 | temp = ipic_read(ipic->regs, ipic_info[src].prio); | ||
509 | |||
510 | if (priority < 4) { | ||
511 | temp &= ~(0x7 << (20 + (3 - priority) * 3)); | ||
512 | temp |= ipic_info[src].prio_mask << (20 + (3 - priority) * 3); | ||
513 | } else { | ||
514 | temp &= ~(0x7 << (4 + (7 - priority) * 3)); | ||
515 | temp |= ipic_info[src].prio_mask << (4 + (7 - priority) * 3); | ||
516 | } | ||
517 | |||
518 | ipic_write(ipic->regs, ipic_info[src].prio, temp); | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | void ipic_set_highest_priority(unsigned int irq) | ||
524 | { | ||
525 | struct ipic *ipic = ipic_from_irq(irq); | ||
526 | unsigned int src = irq - ipic->irq_offset; | ||
527 | u32 temp; | ||
528 | |||
529 | temp = ipic_read(ipic->regs, IPIC_SICFR); | ||
530 | |||
531 | /* clear and set HPI */ | ||
532 | temp &= 0x7f000000; | ||
533 | temp |= (src & 0x7f) << 24; | ||
534 | |||
535 | ipic_write(ipic->regs, IPIC_SICFR, temp); | ||
536 | } | ||
537 | |||
538 | void ipic_set_default_priority(void) | ||
539 | { | ||
540 | ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0); | ||
541 | ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1); | ||
542 | ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2); | ||
543 | ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3); | ||
544 | ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4); | ||
545 | ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5); | ||
546 | ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6); | ||
547 | ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7); | ||
548 | |||
549 | ipic_set_priority(MPC83xx_IRQ_UART1, 0); | ||
550 | ipic_set_priority(MPC83xx_IRQ_UART2, 1); | ||
551 | ipic_set_priority(MPC83xx_IRQ_SEC2, 2); | ||
552 | ipic_set_priority(MPC83xx_IRQ_IIC1, 5); | ||
553 | ipic_set_priority(MPC83xx_IRQ_IIC2, 6); | ||
554 | ipic_set_priority(MPC83xx_IRQ_SPI, 7); | ||
555 | ipic_set_priority(MPC83xx_IRQ_RTC_SEC, 0); | ||
556 | ipic_set_priority(MPC83xx_IRQ_PIT, 1); | ||
557 | ipic_set_priority(MPC83xx_IRQ_PCI1, 2); | ||
558 | ipic_set_priority(MPC83xx_IRQ_PCI2, 3); | ||
559 | ipic_set_priority(MPC83xx_IRQ_EXT0, 4); | ||
560 | ipic_set_priority(MPC83xx_IRQ_EXT1, 5); | ||
561 | ipic_set_priority(MPC83xx_IRQ_EXT2, 6); | ||
562 | ipic_set_priority(MPC83xx_IRQ_EXT3, 7); | ||
563 | ipic_set_priority(MPC83xx_IRQ_RTC_ALR, 0); | ||
564 | ipic_set_priority(MPC83xx_IRQ_MU, 1); | ||
565 | ipic_set_priority(MPC83xx_IRQ_SBA, 2); | ||
566 | ipic_set_priority(MPC83xx_IRQ_DMA, 3); | ||
567 | ipic_set_priority(MPC83xx_IRQ_EXT4, 4); | ||
568 | ipic_set_priority(MPC83xx_IRQ_EXT5, 5); | ||
569 | ipic_set_priority(MPC83xx_IRQ_EXT6, 6); | ||
570 | ipic_set_priority(MPC83xx_IRQ_EXT7, 7); | ||
571 | } | ||
572 | |||
573 | void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq) | ||
574 | { | ||
575 | struct ipic *ipic = primary_ipic; | ||
576 | u32 temp; | ||
577 | |||
578 | temp = ipic_read(ipic->regs, IPIC_SERMR); | ||
579 | temp |= (1 << (31 - mcp_irq)); | ||
580 | ipic_write(ipic->regs, IPIC_SERMR, temp); | ||
581 | } | ||
582 | |||
583 | void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq) | ||
584 | { | ||
585 | struct ipic *ipic = primary_ipic; | ||
586 | u32 temp; | ||
587 | |||
588 | temp = ipic_read(ipic->regs, IPIC_SERMR); | ||
589 | temp &= (1 << (31 - mcp_irq)); | ||
590 | ipic_write(ipic->regs, IPIC_SERMR, temp); | ||
591 | } | ||
592 | |||
593 | u32 ipic_get_mcp_status(void) | ||
594 | { | ||
595 | return ipic_read(primary_ipic->regs, IPIC_SERMR); | ||
596 | } | ||
597 | |||
598 | void ipic_clear_mcp_status(u32 mask) | ||
599 | { | ||
600 | ipic_write(primary_ipic->regs, IPIC_SERMR, mask); | ||
601 | } | ||
602 | |||
603 | /* Return an interrupt vector or -1 if no interrupt is pending. */ | ||
604 | int ipic_get_irq(struct pt_regs *regs) | ||
605 | { | ||
606 | int irq; | ||
607 | |||
608 | irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & 0x7f; | ||
609 | |||
610 | if (irq == 0) /* 0 --> no irq is pending */ | ||
611 | irq = -1; | ||
612 | |||
613 | return irq; | ||
614 | } | ||
615 | |||
616 | static struct sysdev_class ipic_sysclass = { | ||
617 | set_kset_name("ipic"), | ||
618 | }; | ||
619 | |||
620 | static struct sys_device device_ipic = { | ||
621 | .id = 0, | ||
622 | .cls = &ipic_sysclass, | ||
623 | }; | ||
624 | |||
625 | static int __init init_ipic_sysfs(void) | ||
626 | { | ||
627 | int rc; | ||
628 | |||
629 | if (!primary_ipic->regs) | ||
630 | return -ENODEV; | ||
631 | printk(KERN_DEBUG "Registering ipic with sysfs...\n"); | ||
632 | |||
633 | rc = sysdev_class_register(&ipic_sysclass); | ||
634 | if (rc) { | ||
635 | printk(KERN_ERR "Failed registering ipic sys class\n"); | ||
636 | return -ENODEV; | ||
637 | } | ||
638 | rc = sysdev_register(&device_ipic); | ||
639 | if (rc) { | ||
640 | printk(KERN_ERR "Failed registering ipic sys device\n"); | ||
641 | return -ENODEV; | ||
642 | } | ||
643 | return 0; | ||
644 | } | ||
645 | |||
646 | subsys_initcall(init_ipic_sysfs); | ||
diff --git a/arch/ppc/syslib/ipic.h b/arch/ppc/syslib/ipic.h new file mode 100644 index 000000000000..2b56a4fcf373 --- /dev/null +++ b/arch/ppc/syslib/ipic.h | |||
@@ -0,0 +1,49 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/ipic.h | ||
3 | * | ||
4 | * IPIC private definitions and structure. | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2005 Freescale Semiconductor, Inc | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | #ifndef __IPIC_H__ | ||
16 | #define __IPIC_H__ | ||
17 | |||
18 | #include <asm/ipic.h> | ||
19 | |||
20 | #define MPC83xx_IPIC_SIZE (0x00100) | ||
21 | |||
22 | /* System Global Interrupt Configuration Register */ | ||
23 | #define SICFR_IPSA 0x00010000 | ||
24 | #define SICFR_IPSD 0x00080000 | ||
25 | #define SICFR_MPSA 0x00200000 | ||
26 | #define SICFR_MPSB 0x00400000 | ||
27 | |||
28 | /* System External Interrupt Mask Register */ | ||
29 | #define SEMSR_SIRQ0 0x00008000 | ||
30 | |||
31 | /* System Error Control Register */ | ||
32 | #define SERCR_MCPR 0x00000001 | ||
33 | |||
34 | struct ipic { | ||
35 | volatile u32 __iomem *regs; | ||
36 | unsigned int irq_offset; | ||
37 | }; | ||
38 | |||
39 | struct ipic_info { | ||
40 | u8 pend; /* pending register offset from base */ | ||
41 | u8 mask; /* mask register offset from base */ | ||
42 | u8 prio; /* priority register offset from base */ | ||
43 | u8 force; /* force register offset from base */ | ||
44 | u8 bit; /* register bit position (as per doc) | ||
45 | bit mask = 1 << (31 - bit) */ | ||
46 | u8 prio_mask; /* priority mask value */ | ||
47 | }; | ||
48 | |||
49 | #endif /* __IPIC_H__ */ | ||
diff --git a/arch/ppc/syslib/m8260_pci.c b/arch/ppc/syslib/m8260_pci.c new file mode 100644 index 000000000000..bd564fb35ab6 --- /dev/null +++ b/arch/ppc/syslib/m8260_pci.c | |||
@@ -0,0 +1,194 @@ | |||
1 | /* | ||
2 | * (C) Copyright 2003 | ||
3 | * Wolfgang Denk, DENX Software Engineering, wd@denx.de. | ||
4 | * | ||
5 | * (C) Copyright 2004 Red Hat, Inc. | ||
6 | * | ||
7 | * See file CREDITS for list of people who contributed to this | ||
8 | * project. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or | ||
11 | * modify it under the terms of the GNU General Public License as | ||
12 | * published by the Free Software Foundation; either version 2 of | ||
13 | * the License, or (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | ||
23 | * MA 02111-1307 USA | ||
24 | */ | ||
25 | |||
26 | #include <linux/kernel.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/pci.h> | ||
29 | #include <linux/slab.h> | ||
30 | #include <linux/delay.h> | ||
31 | |||
32 | #include <asm/byteorder.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/irq.h> | ||
35 | #include <asm/uaccess.h> | ||
36 | #include <asm/machdep.h> | ||
37 | #include <asm/pci-bridge.h> | ||
38 | #include <asm/immap_cpm2.h> | ||
39 | #include <asm/mpc8260.h> | ||
40 | |||
41 | #include "m8260_pci.h" | ||
42 | |||
43 | |||
44 | /* PCI bus configuration registers. | ||
45 | */ | ||
46 | |||
47 | static void __init m8260_setup_pci(struct pci_controller *hose) | ||
48 | { | ||
49 | volatile cpm2_map_t *immap = cpm2_immr; | ||
50 | unsigned long pocmr; | ||
51 | u16 tempShort; | ||
52 | |||
53 | #ifndef CONFIG_ATC /* already done in U-Boot */ | ||
54 | /* | ||
55 | * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]), | ||
56 | * and local bus for PCI (SIUMCR [LBPC]). | ||
57 | */ | ||
58 | immap->im_siu_conf.siu_82xx.sc_siumcr = 0x00640000; | ||
59 | #endif | ||
60 | |||
61 | /* Make PCI lowest priority */ | ||
62 | /* Each 4 bits is a device bus request and the MS 4bits | ||
63 | is highest priority */ | ||
64 | /* Bus 4bit value | ||
65 | --- ---------- | ||
66 | CPM high 0b0000 | ||
67 | CPM middle 0b0001 | ||
68 | CPM low 0b0010 | ||
69 | PCI reguest 0b0011 | ||
70 | Reserved 0b0100 | ||
71 | Reserved 0b0101 | ||
72 | Internal Core 0b0110 | ||
73 | External Master 1 0b0111 | ||
74 | External Master 2 0b1000 | ||
75 | External Master 3 0b1001 | ||
76 | The rest are reserved */ | ||
77 | immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x61207893; | ||
78 | |||
79 | /* Park bus on core while modifying PCI Bus accesses */ | ||
80 | immap->im_siu_conf.siu_82xx.sc_ppc_acr = 0x6; | ||
81 | |||
82 | /* | ||
83 | * Set up master window that allows the CPU to access PCI space. This | ||
84 | * window is set up using the first SIU PCIBR registers. | ||
85 | */ | ||
86 | immap->im_memctl.memc_pcimsk0 = MPC826x_PCI_MASK; | ||
87 | immap->im_memctl.memc_pcibr0 = MPC826x_PCI_BASE | PCIBR_ENABLE; | ||
88 | |||
89 | /* Disable machine check on no response or target abort */ | ||
90 | immap->im_pci.pci_emr = cpu_to_le32(0x1fe7); | ||
91 | /* Release PCI RST (by default the PCI RST signal is held low) */ | ||
92 | immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN); | ||
93 | |||
94 | /* give it some time */ | ||
95 | mdelay(1); | ||
96 | |||
97 | /* | ||
98 | * Set up master window that allows the CPU to access PCI Memory (prefetch) | ||
99 | * space. This window is set up using the first set of Outbound ATU registers. | ||
100 | */ | ||
101 | immap->im_pci.pci_potar0 = cpu_to_le32(MPC826x_PCI_LOWER_MEM >> 12); | ||
102 | immap->im_pci.pci_pobar0 = cpu_to_le32((MPC826x_PCI_LOWER_MEM - MPC826x_PCI_MEM_OFFSET) >> 12); | ||
103 | pocmr = ((MPC826x_PCI_UPPER_MEM - MPC826x_PCI_LOWER_MEM) >> 12) ^ 0xfffff; | ||
104 | immap->im_pci.pci_pocmr0 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PREFETCH_EN); | ||
105 | |||
106 | /* | ||
107 | * Set up master window that allows the CPU to access PCI Memory (non-prefetch) | ||
108 | * space. This window is set up using the second set of Outbound ATU registers. | ||
109 | */ | ||
110 | immap->im_pci.pci_potar1 = cpu_to_le32(MPC826x_PCI_LOWER_MMIO >> 12); | ||
111 | immap->im_pci.pci_pobar1 = cpu_to_le32((MPC826x_PCI_LOWER_MMIO - MPC826x_PCI_MMIO_OFFSET) >> 12); | ||
112 | pocmr = ((MPC826x_PCI_UPPER_MMIO - MPC826x_PCI_LOWER_MMIO) >> 12) ^ 0xfffff; | ||
113 | immap->im_pci.pci_pocmr1 = cpu_to_le32(pocmr | POCMR_ENABLE); | ||
114 | |||
115 | /* | ||
116 | * Set up master window that allows the CPU to access PCI IO space. This window | ||
117 | * is set up using the third set of Outbound ATU registers. | ||
118 | */ | ||
119 | immap->im_pci.pci_potar2 = cpu_to_le32(MPC826x_PCI_IO_BASE >> 12); | ||
120 | immap->im_pci.pci_pobar2 = cpu_to_le32(MPC826x_PCI_LOWER_IO >> 12); | ||
121 | pocmr = ((MPC826x_PCI_UPPER_IO - MPC826x_PCI_LOWER_IO) >> 12) ^ 0xfffff; | ||
122 | immap->im_pci.pci_pocmr2 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PCI_IO); | ||
123 | |||
124 | /* | ||
125 | * Set up slave window that allows PCI masters to access MPC826x local memory. | ||
126 | * This window is set up using the first set of Inbound ATU registers | ||
127 | */ | ||
128 | |||
129 | immap->im_pci.pci_pitar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_LOCAL >> 12); | ||
130 | immap->im_pci.pci_pibar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_BUS >> 12); | ||
131 | pocmr = ((MPC826x_PCI_SLAVE_MEM_SIZE-1) >> 12) ^ 0xfffff; | ||
132 | immap->im_pci.pci_picmr0 = cpu_to_le32(pocmr | PICMR_ENABLE | PICMR_PREFETCH_EN); | ||
133 | |||
134 | /* See above for description - puts PCI request as highest priority */ | ||
135 | immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x03124567; | ||
136 | |||
137 | /* Park the bus on the PCI */ | ||
138 | immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI; | ||
139 | |||
140 | /* Host mode - specify the bridge as a host-PCI bridge */ | ||
141 | early_write_config_word(hose, 0, 0, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_HOST); | ||
142 | |||
143 | /* Enable the host bridge to be a master on the PCI bus, and to act as a PCI memory target */ | ||
144 | early_read_config_word(hose, 0, 0, PCI_COMMAND, &tempShort); | ||
145 | early_write_config_word(hose, 0, 0, PCI_COMMAND, | ||
146 | tempShort | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); | ||
147 | } | ||
148 | |||
149 | void __init m8260_find_bridges(void) | ||
150 | { | ||
151 | extern int pci_assign_all_busses; | ||
152 | struct pci_controller * hose; | ||
153 | |||
154 | pci_assign_all_busses = 1; | ||
155 | |||
156 | hose = pcibios_alloc_controller(); | ||
157 | |||
158 | if (!hose) | ||
159 | return; | ||
160 | |||
161 | ppc_md.pci_swizzle = common_swizzle; | ||
162 | |||
163 | hose->first_busno = 0; | ||
164 | hose->bus_offset = 0; | ||
165 | hose->last_busno = 0xff; | ||
166 | |||
167 | setup_m8260_indirect_pci(hose, | ||
168 | (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr, | ||
169 | (unsigned long)&cpm2_immr->im_pci.pci_cfg_data); | ||
170 | |||
171 | m8260_setup_pci(hose); | ||
172 | hose->pci_mem_offset = MPC826x_PCI_MEM_OFFSET; | ||
173 | |||
174 | isa_io_base = | ||
175 | (unsigned long) ioremap(MPC826x_PCI_IO_BASE, | ||
176 | MPC826x_PCI_IO_SIZE); | ||
177 | hose->io_base_virt = (void *) isa_io_base; | ||
178 | |||
179 | /* setup resources */ | ||
180 | pci_init_resource(&hose->mem_resources[0], | ||
181 | MPC826x_PCI_LOWER_MEM, | ||
182 | MPC826x_PCI_UPPER_MEM, | ||
183 | IORESOURCE_MEM|IORESOURCE_PREFETCH, "PCI prefetchable memory"); | ||
184 | |||
185 | pci_init_resource(&hose->mem_resources[1], | ||
186 | MPC826x_PCI_LOWER_MMIO, | ||
187 | MPC826x_PCI_UPPER_MMIO, | ||
188 | IORESOURCE_MEM, "PCI memory"); | ||
189 | |||
190 | pci_init_resource(&hose->io_resource, | ||
191 | MPC826x_PCI_LOWER_IO, | ||
192 | MPC826x_PCI_UPPER_IO, | ||
193 | IORESOURCE_IO, "PCI I/O"); | ||
194 | } | ||
diff --git a/arch/ppc/syslib/m8260_pci.h b/arch/ppc/syslib/m8260_pci.h new file mode 100644 index 000000000000..d1352120acd7 --- /dev/null +++ b/arch/ppc/syslib/m8260_pci.h | |||
@@ -0,0 +1,76 @@ | |||
1 | |||
2 | #ifndef _PPC_KERNEL_M8260_PCI_H | ||
3 | #define _PPC_KERNEL_M8260_PCI_H | ||
4 | |||
5 | #include <asm/m8260_pci.h> | ||
6 | |||
7 | /* | ||
8 | * Local->PCI map (from CPU) controlled by | ||
9 | * MPC826x master window | ||
10 | * | ||
11 | * 0x80000000 - 0xBFFFFFFF Total CPU2PCI space PCIBR0 | ||
12 | * | ||
13 | * 0x80000000 - 0x9FFFFFFF PCI Mem with prefetch (Outbound ATU #1) | ||
14 | * 0xA0000000 - 0xAFFFFFFF PCI Mem w/o prefetch (Outbound ATU #2) | ||
15 | * 0xB0000000 - 0xB0FFFFFF 32-bit PCI IO (Outbound ATU #3) | ||
16 | * | ||
17 | * PCI->Local map (from PCI) | ||
18 | * MPC826x slave window controlled by | ||
19 | * | ||
20 | * 0x00000000 - 0x07FFFFFF MPC826x local memory (Inbound ATU #1) | ||
21 | */ | ||
22 | |||
23 | /* | ||
24 | * Slave window that allows PCI masters to access MPC826x local memory. | ||
25 | * This window is set up using the first set of Inbound ATU registers | ||
26 | */ | ||
27 | |||
28 | #ifndef MPC826x_PCI_SLAVE_MEM_LOCAL | ||
29 | #define MPC826x_PCI_SLAVE_MEM_LOCAL (((struct bd_info *)__res)->bi_memstart) | ||
30 | #define MPC826x_PCI_SLAVE_MEM_BUS (((struct bd_info *)__res)->bi_memstart) | ||
31 | #define MPC826x_PCI_SLAVE_MEM_SIZE (((struct bd_info *)__res)->bi_memsize) | ||
32 | #endif | ||
33 | |||
34 | /* | ||
35 | * This is the window that allows the CPU to access PCI address space. | ||
36 | * It will be setup with the SIU PCIBR0 register. All three PCI master | ||
37 | * windows, which allow the CPU to access PCI prefetch, non prefetch, | ||
38 | * and IO space (see below), must all fit within this window. | ||
39 | */ | ||
40 | #ifndef MPC826x_PCI_BASE | ||
41 | #define MPC826x_PCI_BASE 0x80000000 | ||
42 | #define MPC826x_PCI_MASK 0xc0000000 | ||
43 | #endif | ||
44 | |||
45 | #ifndef MPC826x_PCI_LOWER_MEM | ||
46 | #define MPC826x_PCI_LOWER_MEM 0x80000000 | ||
47 | #define MPC826x_PCI_UPPER_MEM 0x9fffffff | ||
48 | #define MPC826x_PCI_MEM_OFFSET 0x00000000 | ||
49 | #endif | ||
50 | |||
51 | #ifndef MPC826x_PCI_LOWER_MMIO | ||
52 | #define MPC826x_PCI_LOWER_MMIO 0xa0000000 | ||
53 | #define MPC826x_PCI_UPPER_MMIO 0xafffffff | ||
54 | #define MPC826x_PCI_MMIO_OFFSET 0x00000000 | ||
55 | #endif | ||
56 | |||
57 | #ifndef MPC826x_PCI_LOWER_IO | ||
58 | #define MPC826x_PCI_LOWER_IO 0x00000000 | ||
59 | #define MPC826x_PCI_UPPER_IO 0x00ffffff | ||
60 | #define MPC826x_PCI_IO_BASE 0xb0000000 | ||
61 | #define MPC826x_PCI_IO_SIZE 0x01000000 | ||
62 | #endif | ||
63 | |||
64 | #ifndef _IO_BASE | ||
65 | #define _IO_BASE isa_io_base | ||
66 | #endif | ||
67 | |||
68 | #ifdef CONFIG_8260_PCI9 | ||
69 | struct pci_controller; | ||
70 | extern void setup_m8260_indirect_pci(struct pci_controller* hose, | ||
71 | u32 cfg_addr, u32 cfg_data); | ||
72 | #else | ||
73 | #define setup_m8260_indirect_pci setup_indirect_pci | ||
74 | #endif | ||
75 | |||
76 | #endif /* _PPC_KERNEL_M8260_PCI_H */ | ||
diff --git a/arch/ppc/syslib/m8260_pci_erratum9.c b/arch/ppc/syslib/m8260_pci_erratum9.c new file mode 100644 index 000000000000..9c0582d639e0 --- /dev/null +++ b/arch/ppc/syslib/m8260_pci_erratum9.c | |||
@@ -0,0 +1,473 @@ | |||
1 | /* | ||
2 | * arch/ppc/platforms/mpc8260_pci9.c | ||
3 | * | ||
4 | * Workaround for device erratum PCI 9. | ||
5 | * See Motorola's "XPC826xA Family Device Errata Reference." | ||
6 | * The erratum applies to all 8260 family Hip4 processors. It is scheduled | ||
7 | * to be fixed in HiP4 Rev C. Erratum PCI 9 states that a simultaneous PCI | ||
8 | * inbound write transaction and PCI outbound read transaction can result in a | ||
9 | * bus deadlock. The suggested workaround is to use the IDMA controller to | ||
10 | * perform all reads from PCI configuration, memory, and I/O space. | ||
11 | * | ||
12 | * Author: andy_lowe@mvista.com | ||
13 | * | ||
14 | * 2003 (c) MontaVista Software, Inc. This file is licensed under | ||
15 | * the terms of the GNU General Public License version 2. This program | ||
16 | * is licensed "as is" without any warranty of any kind, whether express | ||
17 | * or implied. | ||
18 | */ | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/config.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/types.h> | ||
24 | #include <linux/string.h> | ||
25 | |||
26 | #include <asm/io.h> | ||
27 | #include <asm/pci-bridge.h> | ||
28 | #include <asm/machdep.h> | ||
29 | #include <asm/byteorder.h> | ||
30 | #include <asm/mpc8260.h> | ||
31 | #include <asm/immap_cpm2.h> | ||
32 | #include <asm/cpm2.h> | ||
33 | |||
34 | #include "m8260_pci.h" | ||
35 | |||
36 | #ifdef CONFIG_8260_PCI9 | ||
37 | /*#include <asm/mpc8260_pci9.h>*/ /* included in asm/io.h */ | ||
38 | |||
39 | #define IDMA_XFER_BUF_SIZE 64 /* size of the IDMA transfer buffer */ | ||
40 | |||
41 | /* define a structure for the IDMA dpram usage */ | ||
42 | typedef struct idma_dpram_s { | ||
43 | idma_t pram; /* IDMA parameter RAM */ | ||
44 | u_char xfer_buf[IDMA_XFER_BUF_SIZE]; /* IDMA transfer buffer */ | ||
45 | idma_bd_t bd; /* buffer descriptor */ | ||
46 | } idma_dpram_t; | ||
47 | |||
48 | /* define offsets relative to start of IDMA dpram */ | ||
49 | #define IDMA_XFER_BUF_OFFSET (sizeof(idma_t)) | ||
50 | #define IDMA_BD_OFFSET (sizeof(idma_t) + IDMA_XFER_BUF_SIZE) | ||
51 | |||
52 | /* define globals */ | ||
53 | static volatile idma_dpram_t *idma_dpram; | ||
54 | |||
55 | /* Exactly one of CONFIG_8260_PCI9_IDMAn must be defined, | ||
56 | * where n is 1, 2, 3, or 4. This selects the IDMA channel used for | ||
57 | * the PCI9 workaround. | ||
58 | */ | ||
59 | #ifdef CONFIG_8260_PCI9_IDMA1 | ||
60 | #define IDMA_CHAN 0 | ||
61 | #define PROFF_IDMA PROFF_IDMA1_BASE | ||
62 | #define IDMA_PAGE CPM_CR_IDMA1_PAGE | ||
63 | #define IDMA_SBLOCK CPM_CR_IDMA1_SBLOCK | ||
64 | #endif | ||
65 | #ifdef CONFIG_8260_PCI9_IDMA2 | ||
66 | #define IDMA_CHAN 1 | ||
67 | #define PROFF_IDMA PROFF_IDMA2_BASE | ||
68 | #define IDMA_PAGE CPM_CR_IDMA2_PAGE | ||
69 | #define IDMA_SBLOCK CPM_CR_IDMA2_SBLOCK | ||
70 | #endif | ||
71 | #ifdef CONFIG_8260_PCI9_IDMA3 | ||
72 | #define IDMA_CHAN 2 | ||
73 | #define PROFF_IDMA PROFF_IDMA3_BASE | ||
74 | #define IDMA_PAGE CPM_CR_IDMA3_PAGE | ||
75 | #define IDMA_SBLOCK CPM_CR_IDMA3_SBLOCK | ||
76 | #endif | ||
77 | #ifdef CONFIG_8260_PCI9_IDMA4 | ||
78 | #define IDMA_CHAN 3 | ||
79 | #define PROFF_IDMA PROFF_IDMA4_BASE | ||
80 | #define IDMA_PAGE CPM_CR_IDMA4_PAGE | ||
81 | #define IDMA_SBLOCK CPM_CR_IDMA4_SBLOCK | ||
82 | #endif | ||
83 | |||
84 | void idma_pci9_init(void) | ||
85 | { | ||
86 | uint dpram_offset; | ||
87 | volatile idma_t *pram; | ||
88 | volatile im_idma_t *idma_reg; | ||
89 | volatile cpm2_map_t *immap = cpm2_immr; | ||
90 | |||
91 | /* allocate IDMA dpram */ | ||
92 | dpram_offset = cpm_dpalloc(sizeof(idma_dpram_t), 64); | ||
93 | idma_dpram = cpm_dpram_addr(dpram_offset); | ||
94 | |||
95 | /* initialize the IDMA parameter RAM */ | ||
96 | memset((void *)idma_dpram, 0, sizeof(idma_dpram_t)); | ||
97 | pram = &idma_dpram->pram; | ||
98 | pram->ibase = dpram_offset + IDMA_BD_OFFSET; | ||
99 | pram->dpr_buf = dpram_offset + IDMA_XFER_BUF_OFFSET; | ||
100 | pram->ss_max = 32; | ||
101 | pram->dts = 32; | ||
102 | |||
103 | /* initialize the IDMA_BASE pointer to the IDMA parameter RAM */ | ||
104 | *((ushort *) &immap->im_dprambase[PROFF_IDMA]) = dpram_offset; | ||
105 | |||
106 | /* initialize the IDMA registers */ | ||
107 | idma_reg = (volatile im_idma_t *) &immap->im_sdma.sdma_idsr1; | ||
108 | idma_reg[IDMA_CHAN].idmr = 0; /* mask all IDMA interrupts */ | ||
109 | idma_reg[IDMA_CHAN].idsr = 0xff; /* clear all event flags */ | ||
110 | |||
111 | printk("<4>Using IDMA%d for MPC8260 device erratum PCI 9 workaround\n", | ||
112 | IDMA_CHAN + 1); | ||
113 | |||
114 | return; | ||
115 | } | ||
116 | |||
117 | /* Use the IDMA controller to transfer data from I/O memory to local RAM. | ||
118 | * The src address must be a physical address suitable for use by the DMA | ||
119 | * controller with no translation. The dst address must be a kernel virtual | ||
120 | * address. The dst address is translated to a physical address via | ||
121 | * virt_to_phys(). | ||
122 | * The sinc argument specifies whether or not the source address is incremented | ||
123 | * by the DMA controller. The source address is incremented if and only if sinc | ||
124 | * is non-zero. The destination address is always incremented since the | ||
125 | * destination is always host RAM. | ||
126 | */ | ||
127 | static void | ||
128 | idma_pci9_read(u8 *dst, u8 *src, int bytes, int unit_size, int sinc) | ||
129 | { | ||
130 | unsigned long flags; | ||
131 | volatile idma_t *pram = &idma_dpram->pram; | ||
132 | volatile idma_bd_t *bd = &idma_dpram->bd; | ||
133 | volatile cpm2_map_t *immap = cpm2_immr; | ||
134 | |||
135 | local_irq_save(flags); | ||
136 | |||
137 | /* initialize IDMA parameter RAM for this transfer */ | ||
138 | if (sinc) | ||
139 | pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC | ||
140 | | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM; | ||
141 | else | ||
142 | pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_DINC | ||
143 | | IDMA_DCM_SD_MEM2MEM; | ||
144 | pram->ibdptr = pram->ibase; | ||
145 | pram->sts = unit_size; | ||
146 | pram->istate = 0; | ||
147 | |||
148 | /* initialize the buffer descriptor */ | ||
149 | bd->dst = virt_to_phys(dst); | ||
150 | bd->src = (uint) src; | ||
151 | bd->len = bytes; | ||
152 | bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL | ||
153 | | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB; | ||
154 | |||
155 | /* issue the START_IDMA command to the CP */ | ||
156 | while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); | ||
157 | immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0, | ||
158 | CPM_CR_START_IDMA) | CPM_CR_FLG; | ||
159 | while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); | ||
160 | |||
161 | /* wait for transfer to complete */ | ||
162 | while(bd->flags & IDMA_BD_V); | ||
163 | |||
164 | local_irq_restore(flags); | ||
165 | |||
166 | return; | ||
167 | } | ||
168 | |||
169 | /* Use the IDMA controller to transfer data from I/O memory to local RAM. | ||
170 | * The dst address must be a physical address suitable for use by the DMA | ||
171 | * controller with no translation. The src address must be a kernel virtual | ||
172 | * address. The src address is translated to a physical address via | ||
173 | * virt_to_phys(). | ||
174 | * The dinc argument specifies whether or not the dest address is incremented | ||
175 | * by the DMA controller. The source address is incremented if and only if sinc | ||
176 | * is non-zero. The source address is always incremented since the | ||
177 | * source is always host RAM. | ||
178 | */ | ||
179 | static void | ||
180 | idma_pci9_write(u8 *dst, u8 *src, int bytes, int unit_size, int dinc) | ||
181 | { | ||
182 | unsigned long flags; | ||
183 | volatile idma_t *pram = &idma_dpram->pram; | ||
184 | volatile idma_bd_t *bd = &idma_dpram->bd; | ||
185 | volatile cpm2_map_t *immap = cpm2_immr; | ||
186 | |||
187 | local_irq_save(flags); | ||
188 | |||
189 | /* initialize IDMA parameter RAM for this transfer */ | ||
190 | if (dinc) | ||
191 | pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC | ||
192 | | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM; | ||
193 | else | ||
194 | pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC | ||
195 | | IDMA_DCM_SD_MEM2MEM; | ||
196 | pram->ibdptr = pram->ibase; | ||
197 | pram->sts = unit_size; | ||
198 | pram->istate = 0; | ||
199 | |||
200 | /* initialize the buffer descriptor */ | ||
201 | bd->dst = (uint) dst; | ||
202 | bd->src = virt_to_phys(src); | ||
203 | bd->len = bytes; | ||
204 | bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL | ||
205 | | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB; | ||
206 | |||
207 | /* issue the START_IDMA command to the CP */ | ||
208 | while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); | ||
209 | immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0, | ||
210 | CPM_CR_START_IDMA) | CPM_CR_FLG; | ||
211 | while (immap->im_cpm.cp_cpcr & CPM_CR_FLG); | ||
212 | |||
213 | /* wait for transfer to complete */ | ||
214 | while(bd->flags & IDMA_BD_V); | ||
215 | |||
216 | local_irq_restore(flags); | ||
217 | |||
218 | return; | ||
219 | } | ||
220 | |||
221 | /* Same as idma_pci9_read, but 16-bit little-endian byte swapping is performed | ||
222 | * if the unit_size is 2, and 32-bit little-endian byte swapping is performed if | ||
223 | * the unit_size is 4. | ||
224 | */ | ||
225 | static void | ||
226 | idma_pci9_read_le(u8 *dst, u8 *src, int bytes, int unit_size, int sinc) | ||
227 | { | ||
228 | int i; | ||
229 | u8 *p; | ||
230 | |||
231 | idma_pci9_read(dst, src, bytes, unit_size, sinc); | ||
232 | switch(unit_size) { | ||
233 | case 2: | ||
234 | for (i = 0, p = dst; i < bytes; i += 2, p += 2) | ||
235 | swab16s((u16 *) p); | ||
236 | break; | ||
237 | case 4: | ||
238 | for (i = 0, p = dst; i < bytes; i += 4, p += 4) | ||
239 | swab32s((u32 *) p); | ||
240 | break; | ||
241 | default: | ||
242 | break; | ||
243 | } | ||
244 | } | ||
245 | EXPORT_SYMBOL(idma_pci9_init); | ||
246 | EXPORT_SYMBOL(idma_pci9_read); | ||
247 | EXPORT_SYMBOL(idma_pci9_read_le); | ||
248 | |||
249 | static inline int is_pci_mem(unsigned long addr) | ||
250 | { | ||
251 | if (addr >= MPC826x_PCI_LOWER_MMIO && | ||
252 | addr <= MPC826x_PCI_UPPER_MMIO) | ||
253 | return 1; | ||
254 | if (addr >= MPC826x_PCI_LOWER_MEM && | ||
255 | addr <= MPC826x_PCI_UPPER_MEM) | ||
256 | return 1; | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | #define is_pci_mem(pa) ( (pa > 0x80000000) && (pa < 0xc0000000)) | ||
261 | int readb(volatile unsigned char *addr) | ||
262 | { | ||
263 | u8 val; | ||
264 | unsigned long pa = iopa((unsigned long) addr); | ||
265 | |||
266 | if (!is_pci_mem(pa)) | ||
267 | return in_8(addr); | ||
268 | |||
269 | idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); | ||
270 | return val; | ||
271 | } | ||
272 | |||
273 | int readw(volatile unsigned short *addr) | ||
274 | { | ||
275 | u16 val; | ||
276 | unsigned long pa = iopa((unsigned long) addr); | ||
277 | |||
278 | if (!is_pci_mem(pa)) | ||
279 | return in_le16(addr); | ||
280 | |||
281 | idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); | ||
282 | return swab16(val); | ||
283 | } | ||
284 | |||
285 | unsigned readl(volatile unsigned *addr) | ||
286 | { | ||
287 | u32 val; | ||
288 | unsigned long pa = iopa((unsigned long) addr); | ||
289 | |||
290 | if (!is_pci_mem(pa)) | ||
291 | return in_le32(addr); | ||
292 | |||
293 | idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0); | ||
294 | return swab32(val); | ||
295 | } | ||
296 | |||
297 | int inb(unsigned port) | ||
298 | { | ||
299 | u8 val; | ||
300 | u8 *addr = (u8 *)(port + _IO_BASE); | ||
301 | |||
302 | idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); | ||
303 | return val; | ||
304 | } | ||
305 | |||
306 | int inw(unsigned port) | ||
307 | { | ||
308 | u16 val; | ||
309 | u8 *addr = (u8 *)(port + _IO_BASE); | ||
310 | |||
311 | idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); | ||
312 | return swab16(val); | ||
313 | } | ||
314 | |||
315 | unsigned inl(unsigned port) | ||
316 | { | ||
317 | u32 val; | ||
318 | u8 *addr = (u8 *)(port + _IO_BASE); | ||
319 | |||
320 | idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0); | ||
321 | return swab32(val); | ||
322 | } | ||
323 | |||
324 | void insb(unsigned port, void *buf, int ns) | ||
325 | { | ||
326 | u8 *addr = (u8 *)(port + _IO_BASE); | ||
327 | |||
328 | idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u8), sizeof(u8), 0); | ||
329 | } | ||
330 | |||
331 | void insw(unsigned port, void *buf, int ns) | ||
332 | { | ||
333 | u8 *addr = (u8 *)(port + _IO_BASE); | ||
334 | |||
335 | idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0); | ||
336 | } | ||
337 | |||
338 | void insl(unsigned port, void *buf, int nl) | ||
339 | { | ||
340 | u8 *addr = (u8 *)(port + _IO_BASE); | ||
341 | |||
342 | idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0); | ||
343 | } | ||
344 | |||
345 | void insw_ns(unsigned port, void *buf, int ns) | ||
346 | { | ||
347 | u8 *addr = (u8 *)(port + _IO_BASE); | ||
348 | |||
349 | idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0); | ||
350 | } | ||
351 | |||
352 | void insl_ns(unsigned port, void *buf, int nl) | ||
353 | { | ||
354 | u8 *addr = (u8 *)(port + _IO_BASE); | ||
355 | |||
356 | idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0); | ||
357 | } | ||
358 | |||
359 | void *memcpy_fromio(void *dest, unsigned long src, size_t count) | ||
360 | { | ||
361 | unsigned long pa = iopa((unsigned long) src); | ||
362 | |||
363 | if (is_pci_mem(pa)) | ||
364 | idma_pci9_read((u8 *)dest, (u8 *)pa, count, 32, 1); | ||
365 | else | ||
366 | memcpy(dest, (void *)src, count); | ||
367 | return dest; | ||
368 | } | ||
369 | |||
370 | EXPORT_SYMBOL(readb); | ||
371 | EXPORT_SYMBOL(readw); | ||
372 | EXPORT_SYMBOL(readl); | ||
373 | EXPORT_SYMBOL(inb); | ||
374 | EXPORT_SYMBOL(inw); | ||
375 | EXPORT_SYMBOL(inl); | ||
376 | EXPORT_SYMBOL(insb); | ||
377 | EXPORT_SYMBOL(insw); | ||
378 | EXPORT_SYMBOL(insl); | ||
379 | EXPORT_SYMBOL(insw_ns); | ||
380 | EXPORT_SYMBOL(insl_ns); | ||
381 | EXPORT_SYMBOL(memcpy_fromio); | ||
382 | |||
383 | #endif /* ifdef CONFIG_8260_PCI9 */ | ||
384 | |||
385 | /* Indirect PCI routines adapted from arch/ppc/kernel/indirect_pci.c. | ||
386 | * Copyright (C) 1998 Gabriel Paubert. | ||
387 | */ | ||
388 | #ifndef CONFIG_8260_PCI9 | ||
389 | #define cfg_read(val, addr, type, op) *val = op((type)(addr)) | ||
390 | #else | ||
391 | #define cfg_read(val, addr, type, op) \ | ||
392 | idma_pci9_read_le((u8*)(val),(u8*)(addr),sizeof(*(val)),sizeof(*(val)),0) | ||
393 | #endif | ||
394 | |||
395 | #define cfg_write(val, addr, type, op) op((type *)(addr), (val)) | ||
396 | |||
397 | static int indirect_write_config(struct pci_bus *pbus, unsigned int devfn, int where, | ||
398 | int size, u32 value) | ||
399 | { | ||
400 | struct pci_controller *hose = pbus->sysdata; | ||
401 | u8 cfg_type = 0; | ||
402 | if (ppc_md.pci_exclude_device) | ||
403 | if (ppc_md.pci_exclude_device(pbus->number, devfn)) | ||
404 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
405 | |||
406 | if (hose->set_cfg_type) | ||
407 | if (pbus->number != hose->first_busno) | ||
408 | cfg_type = 1; | ||
409 | |||
410 | out_be32(hose->cfg_addr, | ||
411 | (((where & 0xfc) | cfg_type) << 24) | (devfn << 16) | ||
412 | | ((pbus->number - hose->bus_offset) << 8) | 0x80); | ||
413 | |||
414 | switch (size) | ||
415 | { | ||
416 | case 1: | ||
417 | cfg_write(value, hose->cfg_data + (where & 3), u8, out_8); | ||
418 | break; | ||
419 | case 2: | ||
420 | cfg_write(value, hose->cfg_data + (where & 2), u16, out_le16); | ||
421 | break; | ||
422 | case 4: | ||
423 | cfg_write(value, hose->cfg_data + (where & 0), u32, out_le32); | ||
424 | break; | ||
425 | } | ||
426 | return PCIBIOS_SUCCESSFUL; | ||
427 | } | ||
428 | |||
429 | static int indirect_read_config(struct pci_bus *pbus, unsigned int devfn, int where, | ||
430 | int size, u32 *value) | ||
431 | { | ||
432 | struct pci_controller *hose = pbus->sysdata; | ||
433 | u8 cfg_type = 0; | ||
434 | if (ppc_md.pci_exclude_device) | ||
435 | if (ppc_md.pci_exclude_device(pbus->number, devfn)) | ||
436 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
437 | |||
438 | if (hose->set_cfg_type) | ||
439 | if (pbus->number != hose->first_busno) | ||
440 | cfg_type = 1; | ||
441 | |||
442 | out_be32(hose->cfg_addr, | ||
443 | (((where & 0xfc) | cfg_type) << 24) | (devfn << 16) | ||
444 | | ((pbus->number - hose->bus_offset) << 8) | 0x80); | ||
445 | |||
446 | switch (size) | ||
447 | { | ||
448 | case 1: | ||
449 | cfg_read(value, hose->cfg_data + (where & 3), u8 *, in_8); | ||
450 | break; | ||
451 | case 2: | ||
452 | cfg_read(value, hose->cfg_data + (where & 2), u16 *, in_le16); | ||
453 | break; | ||
454 | case 4: | ||
455 | cfg_read(value, hose->cfg_data + (where & 0), u32 *, in_le32); | ||
456 | break; | ||
457 | } | ||
458 | return PCIBIOS_SUCCESSFUL; | ||
459 | } | ||
460 | |||
461 | static struct pci_ops indirect_pci_ops = | ||
462 | { | ||
463 | .read = indirect_read_config, | ||
464 | .write = indirect_write_config, | ||
465 | }; | ||
466 | |||
467 | void | ||
468 | setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data) | ||
469 | { | ||
470 | hose->ops = &indirect_pci_ops; | ||
471 | hose->cfg_addr = (unsigned int *) ioremap(cfg_addr, 4); | ||
472 | hose->cfg_data = (unsigned char *) ioremap(cfg_data, 4); | ||
473 | } | ||
diff --git a/arch/ppc/syslib/m8260_setup.c b/arch/ppc/syslib/m8260_setup.c new file mode 100644 index 000000000000..23ea3f694de2 --- /dev/null +++ b/arch/ppc/syslib/m8260_setup.c | |||
@@ -0,0 +1,264 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/m8260_setup.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * Adapted from 'alpha' version by Gary Thomas | ||
6 | * Modified by Cort Dougan (cort@cs.nmt.edu) | ||
7 | * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) | ||
8 | * Further modified for generic 8xx and 8260 by Dan. | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/stddef.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/initrd.h> | ||
19 | #include <linux/root_dev.h> | ||
20 | #include <linux/seq_file.h> | ||
21 | #include <linux/irq.h> | ||
22 | |||
23 | #include <asm/mmu.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/mpc8260.h> | ||
27 | #include <asm/immap_cpm2.h> | ||
28 | #include <asm/machdep.h> | ||
29 | #include <asm/bootinfo.h> | ||
30 | #include <asm/time.h> | ||
31 | |||
32 | #include "cpm2_pic.h" | ||
33 | |||
34 | unsigned char __res[sizeof(bd_t)]; | ||
35 | |||
36 | extern void cpm2_reset(void); | ||
37 | extern void m8260_find_bridges(void); | ||
38 | extern void idma_pci9_init(void); | ||
39 | |||
40 | /* Place-holder for board-specific init */ | ||
41 | void __attribute__ ((weak)) __init | ||
42 | m82xx_board_setup(void) | ||
43 | { | ||
44 | } | ||
45 | |||
46 | static void __init | ||
47 | m8260_setup_arch(void) | ||
48 | { | ||
49 | /* Print out Vendor and Machine info. */ | ||
50 | printk(KERN_INFO "%s %s port\n", CPUINFO_VENDOR, CPUINFO_MACHINE); | ||
51 | |||
52 | /* Reset the Communication Processor Module. */ | ||
53 | cpm2_reset(); | ||
54 | #ifdef CONFIG_8260_PCI9 | ||
55 | /* Initialise IDMA for PCI erratum workaround */ | ||
56 | idma_pci9_init(); | ||
57 | #endif | ||
58 | #ifdef CONFIG_PCI_8260 | ||
59 | m8260_find_bridges(); | ||
60 | #endif | ||
61 | #ifdef CONFIG_BLK_DEV_INITRD | ||
62 | if (initrd_start) | ||
63 | ROOT_DEV = Root_RAM0; | ||
64 | #endif | ||
65 | m82xx_board_setup(); | ||
66 | } | ||
67 | |||
68 | /* The decrementer counts at the system (internal) clock frequency | ||
69 | * divided by four. | ||
70 | */ | ||
71 | static void __init | ||
72 | m8260_calibrate_decr(void) | ||
73 | { | ||
74 | bd_t *binfo = (bd_t *)__res; | ||
75 | int freq, divisor; | ||
76 | |||
77 | freq = binfo->bi_busfreq; | ||
78 | divisor = 4; | ||
79 | tb_ticks_per_jiffy = freq / HZ / divisor; | ||
80 | tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); | ||
81 | } | ||
82 | |||
83 | /* The 8260 has an internal 1-second timer update register that | ||
84 | * we should use for this purpose. | ||
85 | */ | ||
86 | static uint rtc_time; | ||
87 | |||
88 | static int | ||
89 | m8260_set_rtc_time(unsigned long time) | ||
90 | { | ||
91 | rtc_time = time; | ||
92 | |||
93 | return(0); | ||
94 | } | ||
95 | |||
96 | static unsigned long | ||
97 | m8260_get_rtc_time(void) | ||
98 | { | ||
99 | /* Get time from the RTC. | ||
100 | */ | ||
101 | return((unsigned long)rtc_time); | ||
102 | } | ||
103 | |||
104 | #ifndef BOOTROM_RESTART_ADDR | ||
105 | #warning "Using default BOOTROM_RESTART_ADDR!" | ||
106 | #define BOOTROM_RESTART_ADDR 0xff000104 | ||
107 | #endif | ||
108 | |||
109 | static void | ||
110 | m8260_restart(char *cmd) | ||
111 | { | ||
112 | extern void m8260_gorom(bd_t *bi, uint addr); | ||
113 | uint startaddr; | ||
114 | |||
115 | /* Most boot roms have a warmstart as the second instruction | ||
116 | * of the reset vector. If that doesn't work for you, change this | ||
117 | * or the reboot program to send a proper address. | ||
118 | */ | ||
119 | startaddr = BOOTROM_RESTART_ADDR; | ||
120 | if (cmd != NULL) { | ||
121 | if (!strncmp(cmd, "startaddr=", 10)) | ||
122 | startaddr = simple_strtoul(&cmd[10], NULL, 0); | ||
123 | } | ||
124 | |||
125 | m8260_gorom((void*)__pa(__res), startaddr); | ||
126 | } | ||
127 | |||
128 | static void | ||
129 | m8260_halt(void) | ||
130 | { | ||
131 | local_irq_disable(); | ||
132 | while (1); | ||
133 | } | ||
134 | |||
135 | static void | ||
136 | m8260_power_off(void) | ||
137 | { | ||
138 | m8260_halt(); | ||
139 | } | ||
140 | |||
141 | static int | ||
142 | m8260_show_cpuinfo(struct seq_file *m) | ||
143 | { | ||
144 | bd_t *bp = (bd_t *)__res; | ||
145 | |||
146 | seq_printf(m, "vendor\t\t: %s\n" | ||
147 | "machine\t\t: %s\n" | ||
148 | "\n" | ||
149 | "mem size\t\t: 0x%08x\n" | ||
150 | "console baud\t\t: %d\n" | ||
151 | "\n" | ||
152 | "core clock\t: %u MHz\n" | ||
153 | "CPM clock\t: %u MHz\n" | ||
154 | "bus clock\t: %u MHz\n", | ||
155 | CPUINFO_VENDOR, CPUINFO_MACHINE, bp->bi_memsize, | ||
156 | bp->bi_baudrate, bp->bi_intfreq / 1000000, | ||
157 | bp->bi_cpmfreq / 1000000, bp->bi_busfreq / 1000000); | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | /* Initialize the internal interrupt controller. The number of | ||
162 | * interrupts supported can vary with the processor type, and the | ||
163 | * 8260 family can have up to 64. | ||
164 | * External interrupts can be either edge or level triggered, and | ||
165 | * need to be initialized by the appropriate driver. | ||
166 | */ | ||
167 | static void __init | ||
168 | m8260_init_IRQ(void) | ||
169 | { | ||
170 | cpm2_init_IRQ(); | ||
171 | |||
172 | /* Initialize the default interrupt mapping priorities, | ||
173 | * in case the boot rom changed something on us. | ||
174 | */ | ||
175 | cpm2_immr->im_intctl.ic_siprr = 0x05309770; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Same hack as 8xx | ||
180 | */ | ||
181 | static unsigned long __init | ||
182 | m8260_find_end_of_memory(void) | ||
183 | { | ||
184 | bd_t *binfo = (bd_t *)__res; | ||
185 | |||
186 | return binfo->bi_memsize; | ||
187 | } | ||
188 | |||
189 | /* Map the IMMR, plus anything else we can cover | ||
190 | * in that upper space according to the memory controller | ||
191 | * chip select mapping. Grab another bunch of space | ||
192 | * below that for stuff we can't cover in the upper. | ||
193 | */ | ||
194 | static void __init | ||
195 | m8260_map_io(void) | ||
196 | { | ||
197 | uint addr; | ||
198 | |||
199 | /* Map IMMR region to a 256MB BAT */ | ||
200 | addr = (cpm2_immr != NULL) ? (uint)cpm2_immr : CPM_MAP_ADDR; | ||
201 | io_block_mapping(addr, addr, 0x10000000, _PAGE_IO); | ||
202 | |||
203 | /* Map I/O region to a 256MB BAT */ | ||
204 | io_block_mapping(IO_VIRT_ADDR, IO_PHYS_ADDR, 0x10000000, _PAGE_IO); | ||
205 | } | ||
206 | |||
207 | /* Place-holder for board-specific ppc_md hooking */ | ||
208 | void __attribute__ ((weak)) __init | ||
209 | m82xx_board_init(void) | ||
210 | { | ||
211 | } | ||
212 | |||
213 | /* Inputs: | ||
214 | * r3 - Optional pointer to a board information structure. | ||
215 | * r4 - Optional pointer to the physical starting address of the init RAM | ||
216 | * disk. | ||
217 | * r5 - Optional pointer to the physical ending address of the init RAM | ||
218 | * disk. | ||
219 | * r6 - Optional pointer to the physical starting address of any kernel | ||
220 | * command-line parameters. | ||
221 | * r7 - Optional pointer to the physical ending address of any kernel | ||
222 | * command-line parameters. | ||
223 | */ | ||
224 | void __init | ||
225 | platform_init(unsigned long r3, unsigned long r4, unsigned long r5, | ||
226 | unsigned long r6, unsigned long r7) | ||
227 | { | ||
228 | parse_bootinfo(find_bootinfo()); | ||
229 | |||
230 | if ( r3 ) | ||
231 | memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); | ||
232 | |||
233 | #ifdef CONFIG_BLK_DEV_INITRD | ||
234 | /* take care of initrd if we have one */ | ||
235 | if ( r4 ) { | ||
236 | initrd_start = r4 + KERNELBASE; | ||
237 | initrd_end = r5 + KERNELBASE; | ||
238 | } | ||
239 | #endif /* CONFIG_BLK_DEV_INITRD */ | ||
240 | /* take care of cmd line */ | ||
241 | if ( r6 ) { | ||
242 | *(char *)(r7+KERNELBASE) = 0; | ||
243 | strcpy(cmd_line, (char *)(r6+KERNELBASE)); | ||
244 | } | ||
245 | |||
246 | ppc_md.setup_arch = m8260_setup_arch; | ||
247 | ppc_md.show_cpuinfo = m8260_show_cpuinfo; | ||
248 | ppc_md.init_IRQ = m8260_init_IRQ; | ||
249 | ppc_md.get_irq = cpm2_get_irq; | ||
250 | |||
251 | ppc_md.restart = m8260_restart; | ||
252 | ppc_md.power_off = m8260_power_off; | ||
253 | ppc_md.halt = m8260_halt; | ||
254 | |||
255 | ppc_md.set_rtc_time = m8260_set_rtc_time; | ||
256 | ppc_md.get_rtc_time = m8260_get_rtc_time; | ||
257 | ppc_md.calibrate_decr = m8260_calibrate_decr; | ||
258 | |||
259 | ppc_md.find_end_of_memory = m8260_find_end_of_memory; | ||
260 | ppc_md.setup_io_mappings = m8260_map_io; | ||
261 | |||
262 | /* Call back for board-specific settings and overrides. */ | ||
263 | m82xx_board_init(); | ||
264 | } | ||
diff --git a/arch/ppc/syslib/m8xx_setup.c b/arch/ppc/syslib/m8xx_setup.c new file mode 100644 index 000000000000..c1db2ab1d154 --- /dev/null +++ b/arch/ppc/syslib/m8xx_setup.c | |||
@@ -0,0 +1,433 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/setup.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Linus Torvalds | ||
5 | * Adapted from 'alpha' version by Gary Thomas | ||
6 | * Modified by Cort Dougan (cort@cs.nmt.edu) | ||
7 | * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net) | ||
8 | * Further modified for generic 8xx by Dan. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * bootup setup stuff.. | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/sched.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/stddef.h> | ||
21 | #include <linux/unistd.h> | ||
22 | #include <linux/ptrace.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/user.h> | ||
25 | #include <linux/a.out.h> | ||
26 | #include <linux/tty.h> | ||
27 | #include <linux/major.h> | ||
28 | #include <linux/interrupt.h> | ||
29 | #include <linux/reboot.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/initrd.h> | ||
32 | #include <linux/ioport.h> | ||
33 | #include <linux/bootmem.h> | ||
34 | #include <linux/seq_file.h> | ||
35 | #include <linux/root_dev.h> | ||
36 | |||
37 | #include <asm/mmu.h> | ||
38 | #include <asm/reg.h> | ||
39 | #include <asm/residual.h> | ||
40 | #include <asm/io.h> | ||
41 | #include <asm/pgtable.h> | ||
42 | #include <asm/mpc8xx.h> | ||
43 | #include <asm/8xx_immap.h> | ||
44 | #include <asm/machdep.h> | ||
45 | #include <asm/bootinfo.h> | ||
46 | #include <asm/time.h> | ||
47 | #include <asm/xmon.h> | ||
48 | |||
49 | #include "ppc8xx_pic.h" | ||
50 | |||
51 | static int m8xx_set_rtc_time(unsigned long time); | ||
52 | static unsigned long m8xx_get_rtc_time(void); | ||
53 | void m8xx_calibrate_decr(void); | ||
54 | |||
55 | unsigned char __res[sizeof(bd_t)]; | ||
56 | |||
57 | extern void m8xx_ide_init(void); | ||
58 | |||
59 | extern unsigned long find_available_memory(void); | ||
60 | extern void m8xx_cpm_reset(uint cpm_page); | ||
61 | extern void m8xx_wdt_handler_install(bd_t *bp); | ||
62 | extern void rpxfb_alloc_pages(void); | ||
63 | extern void cpm_interrupt_init(void); | ||
64 | |||
65 | void __attribute__ ((weak)) | ||
66 | board_init(void) | ||
67 | { | ||
68 | } | ||
69 | |||
70 | void __init | ||
71 | m8xx_setup_arch(void) | ||
72 | { | ||
73 | int cpm_page; | ||
74 | |||
75 | cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE); | ||
76 | |||
77 | /* Reset the Communication Processor Module. | ||
78 | */ | ||
79 | m8xx_cpm_reset(cpm_page); | ||
80 | |||
81 | #ifdef CONFIG_FB_RPX | ||
82 | rpxfb_alloc_pages(); | ||
83 | #endif | ||
84 | |||
85 | #ifdef notdef | ||
86 | ROOT_DEV = Root_HDA1; /* hda1 */ | ||
87 | #endif | ||
88 | |||
89 | #ifdef CONFIG_BLK_DEV_INITRD | ||
90 | #if 0 | ||
91 | ROOT_DEV = Root_FD0; /* floppy */ | ||
92 | rd_prompt = 1; | ||
93 | rd_doload = 1; | ||
94 | rd_image_start = 0; | ||
95 | #endif | ||
96 | #if 0 /* XXX this may need to be updated for the new bootmem stuff, | ||
97 | or possibly just deleted (see set_phys_avail() in init.c). | ||
98 | - paulus. */ | ||
99 | /* initrd_start and size are setup by boot/head.S and kernel/head.S */ | ||
100 | if ( initrd_start ) | ||
101 | { | ||
102 | if (initrd_end > *memory_end_p) | ||
103 | { | ||
104 | printk("initrd extends beyond end of memory " | ||
105 | "(0x%08lx > 0x%08lx)\ndisabling initrd\n", | ||
106 | initrd_end,*memory_end_p); | ||
107 | initrd_start = 0; | ||
108 | } | ||
109 | } | ||
110 | #endif | ||
111 | #endif | ||
112 | board_init(); | ||
113 | } | ||
114 | |||
115 | void | ||
116 | abort(void) | ||
117 | { | ||
118 | #ifdef CONFIG_XMON | ||
119 | xmon(0); | ||
120 | #endif | ||
121 | machine_restart(NULL); | ||
122 | |||
123 | /* not reached */ | ||
124 | for (;;); | ||
125 | } | ||
126 | |||
127 | /* A place holder for time base interrupts, if they are ever enabled. */ | ||
128 | irqreturn_t timebase_interrupt(int irq, void * dev, struct pt_regs * regs) | ||
129 | { | ||
130 | printk ("timebase_interrupt()\n"); | ||
131 | |||
132 | return IRQ_HANDLED; | ||
133 | } | ||
134 | |||
135 | static struct irqaction tbint_irqaction = { | ||
136 | .handler = timebase_interrupt, | ||
137 | .mask = CPU_MASK_NONE, | ||
138 | .name = "tbint", | ||
139 | }; | ||
140 | |||
141 | /* The decrementer counts at the system (internal) clock frequency divided by | ||
142 | * sixteen, or external oscillator divided by four. We force the processor | ||
143 | * to use system clock divided by sixteen. | ||
144 | */ | ||
145 | void __init m8xx_calibrate_decr(void) | ||
146 | { | ||
147 | bd_t *binfo = (bd_t *)__res; | ||
148 | int freq, fp, divisor; | ||
149 | |||
150 | /* Unlock the SCCR. */ | ||
151 | ((volatile immap_t *)IMAP_ADDR)->im_clkrstk.cark_sccrk = ~KAPWR_KEY; | ||
152 | ((volatile immap_t *)IMAP_ADDR)->im_clkrstk.cark_sccrk = KAPWR_KEY; | ||
153 | |||
154 | /* Force all 8xx processors to use divide by 16 processor clock. */ | ||
155 | ((volatile immap_t *)IMAP_ADDR)->im_clkrst.car_sccr |= 0x02000000; | ||
156 | |||
157 | /* Processor frequency is MHz. | ||
158 | * The value 'fp' is the number of decrementer ticks per second. | ||
159 | */ | ||
160 | fp = binfo->bi_intfreq / 16; | ||
161 | freq = fp*60; /* try to make freq/1e6 an integer */ | ||
162 | divisor = 60; | ||
163 | printk("Decrementer Frequency = %d/%d\n", freq, divisor); | ||
164 | tb_ticks_per_jiffy = freq / HZ / divisor; | ||
165 | tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); | ||
166 | |||
167 | /* Perform some more timer/timebase initialization. This used | ||
168 | * to be done elsewhere, but other changes caused it to get | ||
169 | * called more than once....that is a bad thing. | ||
170 | * | ||
171 | * First, unlock all of the registers we are going to modify. | ||
172 | * To protect them from corruption during power down, registers | ||
173 | * that are maintained by keep alive power are "locked". To | ||
174 | * modify these registers we have to write the key value to | ||
175 | * the key location associated with the register. | ||
176 | * Some boards power up with these unlocked, while others | ||
177 | * are locked. Writing anything (including the unlock code?) | ||
178 | * to the unlocked registers will lock them again. So, here | ||
179 | * we guarantee the registers are locked, then we unlock them | ||
180 | * for our use. | ||
181 | */ | ||
182 | ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = ~KAPWR_KEY; | ||
183 | ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = ~KAPWR_KEY; | ||
184 | ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk = ~KAPWR_KEY; | ||
185 | ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY; | ||
186 | ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY; | ||
187 | ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk = KAPWR_KEY; | ||
188 | |||
189 | /* Disable the RTC one second and alarm interrupts. */ | ||
190 | ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc &= | ||
191 | ~(RTCSC_SIE | RTCSC_ALE); | ||
192 | /* Enable the RTC */ | ||
193 | ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc |= | ||
194 | (RTCSC_RTF | RTCSC_RTE); | ||
195 | |||
196 | /* Enabling the decrementer also enables the timebase interrupts | ||
197 | * (or from the other point of view, to get decrementer interrupts | ||
198 | * we have to enable the timebase). The decrementer interrupt | ||
199 | * is wired into the vector table, nothing to do here for that. | ||
200 | */ | ||
201 | ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_tbscr = | ||
202 | ((mk_int_int_mask(DEC_INTERRUPT) << 8) | | ||
203 | (TBSCR_TBF | TBSCR_TBE)); | ||
204 | |||
205 | if (setup_irq(DEC_INTERRUPT, &tbint_irqaction)) | ||
206 | panic("Could not allocate timer IRQ!"); | ||
207 | |||
208 | #ifdef CONFIG_8xx_WDT | ||
209 | /* Install watchdog timer handler early because it might be | ||
210 | * already enabled by the bootloader | ||
211 | */ | ||
212 | m8xx_wdt_handler_install(binfo); | ||
213 | #endif | ||
214 | } | ||
215 | |||
216 | /* The RTC on the MPC8xx is an internal register. | ||
217 | * We want to protect this during power down, so we need to unlock, | ||
218 | * modify, and re-lock. | ||
219 | */ | ||
220 | static int | ||
221 | m8xx_set_rtc_time(unsigned long time) | ||
222 | { | ||
223 | ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY; | ||
224 | ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtc = time; | ||
225 | ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY; | ||
226 | return(0); | ||
227 | } | ||
228 | |||
229 | static unsigned long | ||
230 | m8xx_get_rtc_time(void) | ||
231 | { | ||
232 | /* Get time from the RTC. */ | ||
233 | return((unsigned long)(((immap_t *)IMAP_ADDR)->im_sit.sit_rtc)); | ||
234 | } | ||
235 | |||
236 | static void | ||
237 | m8xx_restart(char *cmd) | ||
238 | { | ||
239 | __volatile__ unsigned char dummy; | ||
240 | |||
241 | local_irq_disable(); | ||
242 | ((immap_t *)IMAP_ADDR)->im_clkrst.car_plprcr |= 0x00000080; | ||
243 | |||
244 | /* Clear the ME bit in MSR to cause checkstop on machine check | ||
245 | */ | ||
246 | mtmsr(mfmsr() & ~0x1000); | ||
247 | |||
248 | dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0]; | ||
249 | printk("Restart failed\n"); | ||
250 | while(1); | ||
251 | } | ||
252 | |||
253 | static void | ||
254 | m8xx_power_off(void) | ||
255 | { | ||
256 | m8xx_restart(NULL); | ||
257 | } | ||
258 | |||
259 | static void | ||
260 | m8xx_halt(void) | ||
261 | { | ||
262 | m8xx_restart(NULL); | ||
263 | } | ||
264 | |||
265 | |||
266 | static int | ||
267 | m8xx_show_percpuinfo(struct seq_file *m, int i) | ||
268 | { | ||
269 | bd_t *bp; | ||
270 | |||
271 | bp = (bd_t *)__res; | ||
272 | |||
273 | seq_printf(m, "clock\t\t: %ldMHz\n" | ||
274 | "bus clock\t: %ldMHz\n", | ||
275 | bp->bi_intfreq / 1000000, | ||
276 | bp->bi_busfreq / 1000000); | ||
277 | |||
278 | return 0; | ||
279 | } | ||
280 | |||
281 | #ifdef CONFIG_PCI | ||
282 | static struct irqaction mbx_i8259_irqaction = { | ||
283 | .handler = mbx_i8259_action, | ||
284 | .mask = CPU_MASK_NONE, | ||
285 | .name = "i8259 cascade", | ||
286 | }; | ||
287 | #endif | ||
288 | |||
289 | /* Initialize the internal interrupt controller. The number of | ||
290 | * interrupts supported can vary with the processor type, and the | ||
291 | * 82xx family can have up to 64. | ||
292 | * External interrupts can be either edge or level triggered, and | ||
293 | * need to be initialized by the appropriate driver. | ||
294 | */ | ||
295 | static void __init | ||
296 | m8xx_init_IRQ(void) | ||
297 | { | ||
298 | int i; | ||
299 | |||
300 | for (i = SIU_IRQ_OFFSET ; i < SIU_IRQ_OFFSET + NR_SIU_INTS ; i++) | ||
301 | irq_desc[i].handler = &ppc8xx_pic; | ||
302 | |||
303 | cpm_interrupt_init(); | ||
304 | |||
305 | #if defined(CONFIG_PCI) | ||
306 | for (i = I8259_IRQ_OFFSET ; i < I8259_IRQ_OFFSET + NR_8259_INTS ; i++) | ||
307 | irq_desc[i].handler = &i8259_pic; | ||
308 | |||
309 | i8259_pic_irq_offset = I8259_IRQ_OFFSET; | ||
310 | i8259_init(0); | ||
311 | |||
312 | /* The i8259 cascade interrupt must be level sensitive. */ | ||
313 | ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel &= | ||
314 | ~(0x80000000 >> ISA_BRIDGE_INT); | ||
315 | |||
316 | if (setup_irq(ISA_BRIDGE_INT, &mbx_i8259_irqaction)) | ||
317 | enable_irq(ISA_BRIDGE_INT); | ||
318 | #endif /* CONFIG_PCI */ | ||
319 | } | ||
320 | |||
321 | /* -------------------------------------------------------------------- */ | ||
322 | |||
323 | /* | ||
324 | * This is a big hack right now, but it may turn into something real | ||
325 | * someday. | ||
326 | * | ||
327 | * For the 8xx boards (at this time anyway), there is nothing to initialize | ||
328 | * associated the PROM. Rather than include all of the prom.c | ||
329 | * functions in the image just to get prom_init, all we really need right | ||
330 | * now is the initialization of the physical memory region. | ||
331 | */ | ||
332 | static unsigned long __init | ||
333 | m8xx_find_end_of_memory(void) | ||
334 | { | ||
335 | bd_t *binfo; | ||
336 | extern unsigned char __res[]; | ||
337 | |||
338 | binfo = (bd_t *)__res; | ||
339 | |||
340 | return binfo->bi_memsize; | ||
341 | } | ||
342 | |||
343 | /* | ||
344 | * Now map in some of the I/O space that is generically needed | ||
345 | * or shared with multiple devices. | ||
346 | * All of this fits into the same 4Mbyte region, so it only | ||
347 | * requires one page table page. (or at least it used to -- paulus) | ||
348 | */ | ||
349 | static void __init | ||
350 | m8xx_map_io(void) | ||
351 | { | ||
352 | io_block_mapping(IMAP_ADDR, IMAP_ADDR, IMAP_SIZE, _PAGE_IO); | ||
353 | #ifdef CONFIG_MBX | ||
354 | io_block_mapping(NVRAM_ADDR, NVRAM_ADDR, NVRAM_SIZE, _PAGE_IO); | ||
355 | io_block_mapping(MBX_CSR_ADDR, MBX_CSR_ADDR, MBX_CSR_SIZE, _PAGE_IO); | ||
356 | io_block_mapping(PCI_CSR_ADDR, PCI_CSR_ADDR, PCI_CSR_SIZE, _PAGE_IO); | ||
357 | |||
358 | /* Map some of the PCI/ISA I/O space to get the IDE interface. | ||
359 | */ | ||
360 | io_block_mapping(PCI_ISA_IO_ADDR, PCI_ISA_IO_ADDR, 0x4000, _PAGE_IO); | ||
361 | io_block_mapping(PCI_IDE_ADDR, PCI_IDE_ADDR, 0x4000, _PAGE_IO); | ||
362 | #endif | ||
363 | #if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC) | ||
364 | io_block_mapping(RPX_CSR_ADDR, RPX_CSR_ADDR, RPX_CSR_SIZE, _PAGE_IO); | ||
365 | #if !defined(CONFIG_PCI) | ||
366 | io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO); | ||
367 | #endif | ||
368 | #endif | ||
369 | #if defined(CONFIG_HTDMSOUND) || defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX) | ||
370 | io_block_mapping(HIOX_CSR_ADDR, HIOX_CSR_ADDR, HIOX_CSR_SIZE, _PAGE_IO); | ||
371 | #endif | ||
372 | #ifdef CONFIG_FADS | ||
373 | io_block_mapping(BCSR_ADDR, BCSR_ADDR, BCSR_SIZE, _PAGE_IO); | ||
374 | #endif | ||
375 | #ifdef CONFIG_PCI | ||
376 | io_block_mapping(PCI_CSR_ADDR, PCI_CSR_ADDR, PCI_CSR_SIZE, _PAGE_IO); | ||
377 | #endif | ||
378 | #if defined(CONFIG_NETTA) | ||
379 | io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO); | ||
380 | #endif | ||
381 | } | ||
382 | |||
383 | void __init | ||
384 | platform_init(unsigned long r3, unsigned long r4, unsigned long r5, | ||
385 | unsigned long r6, unsigned long r7) | ||
386 | { | ||
387 | parse_bootinfo(find_bootinfo()); | ||
388 | |||
389 | if ( r3 ) | ||
390 | memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) ); | ||
391 | |||
392 | #ifdef CONFIG_PCI | ||
393 | m8xx_setup_pci_ptrs(); | ||
394 | #endif | ||
395 | |||
396 | #ifdef CONFIG_BLK_DEV_INITRD | ||
397 | /* take care of initrd if we have one */ | ||
398 | if ( r4 ) | ||
399 | { | ||
400 | initrd_start = r4 + KERNELBASE; | ||
401 | initrd_end = r5 + KERNELBASE; | ||
402 | } | ||
403 | #endif /* CONFIG_BLK_DEV_INITRD */ | ||
404 | /* take care of cmd line */ | ||
405 | if ( r6 ) | ||
406 | { | ||
407 | *(char *)(r7+KERNELBASE) = 0; | ||
408 | strcpy(cmd_line, (char *)(r6+KERNELBASE)); | ||
409 | } | ||
410 | |||
411 | ppc_md.setup_arch = m8xx_setup_arch; | ||
412 | ppc_md.show_percpuinfo = m8xx_show_percpuinfo; | ||
413 | ppc_md.irq_canonicalize = NULL; | ||
414 | ppc_md.init_IRQ = m8xx_init_IRQ; | ||
415 | ppc_md.get_irq = m8xx_get_irq; | ||
416 | ppc_md.init = NULL; | ||
417 | |||
418 | ppc_md.restart = m8xx_restart; | ||
419 | ppc_md.power_off = m8xx_power_off; | ||
420 | ppc_md.halt = m8xx_halt; | ||
421 | |||
422 | ppc_md.time_init = NULL; | ||
423 | ppc_md.set_rtc_time = m8xx_set_rtc_time; | ||
424 | ppc_md.get_rtc_time = m8xx_get_rtc_time; | ||
425 | ppc_md.calibrate_decr = m8xx_calibrate_decr; | ||
426 | |||
427 | ppc_md.find_end_of_memory = m8xx_find_end_of_memory; | ||
428 | ppc_md.setup_io_mappings = m8xx_map_io; | ||
429 | |||
430 | #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) | ||
431 | m8xx_ide_init(); | ||
432 | #endif | ||
433 | } | ||
diff --git a/arch/ppc/syslib/m8xx_wdt.c b/arch/ppc/syslib/m8xx_wdt.c new file mode 100644 index 000000000000..7838a44bfd1d --- /dev/null +++ b/arch/ppc/syslib/m8xx_wdt.c | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * m8xx_wdt.c - MPC8xx watchdog driver | ||
3 | * | ||
4 | * Author: Florian Schirmer <jolt@tuxbox.org> | ||
5 | * | ||
6 | * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under | ||
7 | * the terms of the GNU General Public License version 2. This program | ||
8 | * is licensed "as is" without any warranty of any kind, whether express | ||
9 | * or implied. | ||
10 | */ | ||
11 | |||
12 | #include <linux/init.h> | ||
13 | #include <linux/interrupt.h> | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <asm/8xx_immap.h> | ||
17 | #include <syslib/m8xx_wdt.h> | ||
18 | |||
19 | static int wdt_timeout; | ||
20 | |||
21 | void m8xx_wdt_reset(void) | ||
22 | { | ||
23 | volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; | ||
24 | |||
25 | imap->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */ | ||
26 | imap->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */ | ||
27 | } | ||
28 | |||
29 | static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs) | ||
30 | { | ||
31 | volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; | ||
32 | |||
33 | m8xx_wdt_reset(); | ||
34 | |||
35 | imap->im_sit.sit_piscr |= PISCR_PS; /* clear irq */ | ||
36 | |||
37 | return IRQ_HANDLED; | ||
38 | } | ||
39 | |||
40 | void __init m8xx_wdt_handler_install(bd_t * binfo) | ||
41 | { | ||
42 | volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR; | ||
43 | u32 pitc; | ||
44 | u32 sypcr; | ||
45 | u32 pitrtclk; | ||
46 | |||
47 | sypcr = imap->im_siu_conf.sc_sypcr; | ||
48 | |||
49 | if (!(sypcr & 0x04)) { | ||
50 | printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n", | ||
51 | sypcr); | ||
52 | return; | ||
53 | } | ||
54 | |||
55 | m8xx_wdt_reset(); | ||
56 | |||
57 | printk(KERN_NOTICE | ||
58 | "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n", | ||
59 | (sypcr >> 16), sypcr & 0x01); | ||
60 | |||
61 | wdt_timeout = (sypcr >> 16) & 0xFFFF; | ||
62 | |||
63 | if (!wdt_timeout) | ||
64 | wdt_timeout = 0xFFFF; | ||
65 | |||
66 | if (sypcr & 0x01) | ||
67 | wdt_timeout *= 2048; | ||
68 | |||
69 | /* | ||
70 | * Fire trigger if half of the wdt ticked down | ||
71 | */ | ||
72 | |||
73 | if (imap->im_sit.sit_rtcsc & RTCSC_38K) | ||
74 | pitrtclk = 9600; | ||
75 | else | ||
76 | pitrtclk = 8192; | ||
77 | |||
78 | if ((wdt_timeout) > (UINT_MAX / pitrtclk)) | ||
79 | pitc = wdt_timeout / binfo->bi_intfreq * pitrtclk / 2; | ||
80 | else | ||
81 | pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2; | ||
82 | |||
83 | imap->im_sit.sit_pitc = pitc << 16; | ||
84 | imap->im_sit.sit_piscr = | ||
85 | (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE; | ||
86 | |||
87 | if (request_irq(PIT_INTERRUPT, m8xx_wdt_interrupt, 0, "watchdog", NULL)) | ||
88 | panic("m8xx_wdt: could not allocate watchdog irq!"); | ||
89 | |||
90 | printk(KERN_NOTICE | ||
91 | "m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc); | ||
92 | |||
93 | wdt_timeout /= binfo->bi_intfreq; | ||
94 | } | ||
95 | |||
96 | int m8xx_wdt_get_timeout(void) | ||
97 | { | ||
98 | return wdt_timeout; | ||
99 | } | ||
diff --git a/arch/ppc/syslib/m8xx_wdt.h b/arch/ppc/syslib/m8xx_wdt.h new file mode 100644 index 000000000000..0d81a9f8155f --- /dev/null +++ b/arch/ppc/syslib/m8xx_wdt.h | |||
@@ -0,0 +1,16 @@ | |||
1 | /* | ||
2 | * Author: Florian Schirmer <jolt@tuxbox.org> | ||
3 | * | ||
4 | * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under | ||
5 | * the terms of the GNU General Public License version 2. This program | ||
6 | * is licensed "as is" without any warranty of any kind, whether express | ||
7 | * or implied. | ||
8 | */ | ||
9 | #ifndef _PPC_SYSLIB_M8XX_WDT_H | ||
10 | #define _PPC_SYSLIB_M8XX_WDT_H | ||
11 | |||
12 | extern void m8xx_wdt_handler_install(bd_t * binfo); | ||
13 | extern int m8xx_wdt_get_timeout(void); | ||
14 | extern void m8xx_wdt_reset(void); | ||
15 | |||
16 | #endif /* _PPC_SYSLIB_M8XX_WDT_H */ | ||
diff --git a/arch/ppc/syslib/mpc10x_common.c b/arch/ppc/syslib/mpc10x_common.c new file mode 100644 index 000000000000..fd93adfd464c --- /dev/null +++ b/arch/ppc/syslib/mpc10x_common.c | |||
@@ -0,0 +1,510 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mpc10x_common.c | ||
3 | * | ||
4 | * Common routines for the Motorola SPS MPC106, MPC107 and MPC8240 Host bridge, | ||
5 | * Mem ctlr, EPIC, etc. | ||
6 | * | ||
7 | * Author: Mark A. Greer | ||
8 | * mgreer@mvista.com | ||
9 | * | ||
10 | * 2001 (c) MontaVista, Software, Inc. This file is licensed under | ||
11 | * the terms of the GNU General Public License version 2. This program | ||
12 | * is licensed "as is" without any warranty of any kind, whether express | ||
13 | * or implied. | ||
14 | */ | ||
15 | |||
16 | /* | ||
17 | * *** WARNING - A BAT MUST be set to access the PCI config addr/data regs *** | ||
18 | */ | ||
19 | |||
20 | #include <linux/kernel.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/slab.h> | ||
24 | |||
25 | #include <asm/byteorder.h> | ||
26 | #include <asm/io.h> | ||
27 | #include <asm/irq.h> | ||
28 | #include <asm/uaccess.h> | ||
29 | #include <asm/machdep.h> | ||
30 | #include <asm/pci-bridge.h> | ||
31 | #include <asm/open_pic.h> | ||
32 | #include <asm/mpc10x.h> | ||
33 | #include <asm/ocp.h> | ||
34 | |||
35 | /* The OCP structure is fixed by code below, before OCP initialises. | ||
36 | paddr depends on where the board places the EUMB. | ||
37 | - fixed in mpc10x_bridge_init(). | ||
38 | irq depends on two things: | ||
39 | > does the board use the EPIC at all? (PCORE does not). | ||
40 | > is the EPIC in serial or parallel mode? | ||
41 | - fixed in mpc10x_set_openpic(). | ||
42 | */ | ||
43 | |||
44 | #ifdef CONFIG_MPC10X_OPENPIC | ||
45 | #ifdef CONFIG_EPIC_SERIAL_MODE | ||
46 | #define EPIC_IRQ_BASE (epic_serial_mode ? 16 : 5) | ||
47 | #else | ||
48 | #define EPIC_IRQ_BASE 5 | ||
49 | #endif | ||
50 | #define MPC10X_I2C_IRQ (EPIC_IRQ_BASE + NUM_8259_INTERRUPTS) | ||
51 | #define MPC10X_DMA0_IRQ (EPIC_IRQ_BASE + 1 + NUM_8259_INTERRUPTS) | ||
52 | #define MPC10X_DMA1_IRQ (EPIC_IRQ_BASE + 2 + NUM_8259_INTERRUPTS) | ||
53 | #else | ||
54 | #define MPC10X_I2C_IRQ OCP_IRQ_NA | ||
55 | #define MPC10X_DMA0_IRQ OCP_IRQ_NA | ||
56 | #define MPC10X_DMA1_IRQ OCP_IRQ_NA | ||
57 | #endif | ||
58 | |||
59 | |||
60 | struct ocp_def core_ocp[] = { | ||
61 | { .vendor = OCP_VENDOR_INVALID | ||
62 | } | ||
63 | }; | ||
64 | |||
65 | static struct ocp_fs_i2c_data mpc10x_i2c_data = { | ||
66 | .flags = 0 | ||
67 | }; | ||
68 | static struct ocp_def mpc10x_i2c_ocp = { | ||
69 | .vendor = OCP_VENDOR_MOTOROLA, | ||
70 | .function = OCP_FUNC_IIC, | ||
71 | .index = 0, | ||
72 | .additions = &mpc10x_i2c_data | ||
73 | }; | ||
74 | |||
75 | static struct ocp_def mpc10x_dma_ocp[2] = { | ||
76 | { .vendor = OCP_VENDOR_MOTOROLA, | ||
77 | .function = OCP_FUNC_DMA, | ||
78 | .index = 0 }, | ||
79 | { .vendor = OCP_VENDOR_MOTOROLA, | ||
80 | .function = OCP_FUNC_DMA, | ||
81 | .index = 1 } | ||
82 | }; | ||
83 | |||
84 | /* Set resources to match bridge memory map */ | ||
85 | void __init | ||
86 | mpc10x_bridge_set_resources(int map, struct pci_controller *hose) | ||
87 | { | ||
88 | |||
89 | switch (map) { | ||
90 | case MPC10X_MEM_MAP_A: | ||
91 | pci_init_resource(&hose->io_resource, | ||
92 | 0x00000000, | ||
93 | 0x3f7fffff, | ||
94 | IORESOURCE_IO, | ||
95 | "PCI host bridge"); | ||
96 | |||
97 | pci_init_resource (&hose->mem_resources[0], | ||
98 | 0xc0000000, | ||
99 | 0xfeffffff, | ||
100 | IORESOURCE_MEM, | ||
101 | "PCI host bridge"); | ||
102 | break; | ||
103 | case MPC10X_MEM_MAP_B: | ||
104 | pci_init_resource(&hose->io_resource, | ||
105 | 0x00000000, | ||
106 | 0x00bfffff, | ||
107 | IORESOURCE_IO, | ||
108 | "PCI host bridge"); | ||
109 | |||
110 | pci_init_resource (&hose->mem_resources[0], | ||
111 | 0x80000000, | ||
112 | 0xfcffffff, | ||
113 | IORESOURCE_MEM, | ||
114 | "PCI host bridge"); | ||
115 | break; | ||
116 | default: | ||
117 | printk("mpc10x_bridge_set_resources: " | ||
118 | "Invalid map specified\n"); | ||
119 | if (ppc_md.progress) | ||
120 | ppc_md.progress("mpc10x:exit1", 0x100); | ||
121 | } | ||
122 | } | ||
123 | /* | ||
124 | * Do some initialization and put the EUMB registers at the specified address | ||
125 | * (also map the EPIC registers into virtual space--OpenPIC_Addr will be set). | ||
126 | * | ||
127 | * The EPIC is not on the 106, only the 8240 and 107. | ||
128 | */ | ||
129 | int __init | ||
130 | mpc10x_bridge_init(struct pci_controller *hose, | ||
131 | uint current_map, | ||
132 | uint new_map, | ||
133 | uint phys_eumb_base) | ||
134 | { | ||
135 | int host_bridge, picr1, picr1_bit; | ||
136 | ulong pci_config_addr, pci_config_data; | ||
137 | u_char pir, byte; | ||
138 | |||
139 | if (ppc_md.progress) ppc_md.progress("mpc10x:enter", 0x100); | ||
140 | |||
141 | /* Set up for current map so we can get at config regs */ | ||
142 | switch (current_map) { | ||
143 | case MPC10X_MEM_MAP_A: | ||
144 | setup_indirect_pci(hose, | ||
145 | MPC10X_MAPA_CNFG_ADDR, | ||
146 | MPC10X_MAPA_CNFG_DATA); | ||
147 | break; | ||
148 | case MPC10X_MEM_MAP_B: | ||
149 | setup_indirect_pci(hose, | ||
150 | MPC10X_MAPB_CNFG_ADDR, | ||
151 | MPC10X_MAPB_CNFG_DATA); | ||
152 | break; | ||
153 | default: | ||
154 | printk("mpc10x_bridge_init: %s\n", | ||
155 | "Invalid current map specified"); | ||
156 | if (ppc_md.progress) | ||
157 | ppc_md.progress("mpc10x:exit1", 0x100); | ||
158 | return -1; | ||
159 | } | ||
160 | |||
161 | /* Make sure it's a supported bridge */ | ||
162 | early_read_config_dword(hose, | ||
163 | 0, | ||
164 | PCI_DEVFN(0,0), | ||
165 | PCI_VENDOR_ID, | ||
166 | &host_bridge); | ||
167 | |||
168 | switch (host_bridge) { | ||
169 | case MPC10X_BRIDGE_106: | ||
170 | case MPC10X_BRIDGE_8240: | ||
171 | case MPC10X_BRIDGE_107: | ||
172 | case MPC10X_BRIDGE_8245: | ||
173 | break; | ||
174 | default: | ||
175 | if (ppc_md.progress) | ||
176 | ppc_md.progress("mpc10x:exit2", 0x100); | ||
177 | return -1; | ||
178 | } | ||
179 | |||
180 | switch (new_map) { | ||
181 | case MPC10X_MEM_MAP_A: | ||
182 | MPC10X_SETUP_HOSE(hose, A); | ||
183 | pci_config_addr = MPC10X_MAPA_CNFG_ADDR; | ||
184 | pci_config_data = MPC10X_MAPA_CNFG_DATA; | ||
185 | picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_A; | ||
186 | break; | ||
187 | case MPC10X_MEM_MAP_B: | ||
188 | MPC10X_SETUP_HOSE(hose, B); | ||
189 | pci_config_addr = MPC10X_MAPB_CNFG_ADDR; | ||
190 | pci_config_data = MPC10X_MAPB_CNFG_DATA; | ||
191 | picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_B; | ||
192 | break; | ||
193 | default: | ||
194 | printk("mpc10x_bridge_init: %s\n", | ||
195 | "Invalid new map specified"); | ||
196 | if (ppc_md.progress) | ||
197 | ppc_md.progress("mpc10x:exit3", 0x100); | ||
198 | return -1; | ||
199 | } | ||
200 | |||
201 | /* Make bridge use the 'new_map', if not already usng it */ | ||
202 | if (current_map != new_map) { | ||
203 | early_read_config_dword(hose, | ||
204 | 0, | ||
205 | PCI_DEVFN(0,0), | ||
206 | MPC10X_CFG_PICR1_REG, | ||
207 | &picr1); | ||
208 | |||
209 | picr1 = (picr1 & ~MPC10X_CFG_PICR1_ADDR_MAP_MASK) | | ||
210 | picr1_bit; | ||
211 | |||
212 | early_write_config_dword(hose, | ||
213 | 0, | ||
214 | PCI_DEVFN(0,0), | ||
215 | MPC10X_CFG_PICR1_REG, | ||
216 | picr1); | ||
217 | |||
218 | asm volatile("sync"); | ||
219 | |||
220 | /* Undo old mappings & map in new cfg data/addr regs */ | ||
221 | iounmap((void *)hose->cfg_addr); | ||
222 | iounmap((void *)hose->cfg_data); | ||
223 | |||
224 | setup_indirect_pci(hose, | ||
225 | pci_config_addr, | ||
226 | pci_config_data); | ||
227 | } | ||
228 | |||
229 | /* Setup resources to match map */ | ||
230 | mpc10x_bridge_set_resources(new_map, hose); | ||
231 | |||
232 | /* | ||
233 | * Want processor accesses of 0xFDxxxxxx to be mapped | ||
234 | * to PCI memory space at 0x00000000. Do not want | ||
235 | * host bridge to respond to PCI memory accesses of | ||
236 | * 0xFDxxxxxx. Do not want host bridge to respond | ||
237 | * to PCI memory addresses 0xFD000000-0xFDFFFFFF; | ||
238 | * want processor accesses from 0x000A0000-0x000BFFFF | ||
239 | * to be forwarded to system memory. | ||
240 | * | ||
241 | * Only valid if not in agent mode and using MAP B. | ||
242 | */ | ||
243 | if (new_map == MPC10X_MEM_MAP_B) { | ||
244 | early_read_config_byte(hose, | ||
245 | 0, | ||
246 | PCI_DEVFN(0,0), | ||
247 | MPC10X_CFG_MAPB_OPTIONS_REG, | ||
248 | &byte); | ||
249 | |||
250 | byte &= ~(MPC10X_CFG_MAPB_OPTIONS_PFAE | | ||
251 | MPC10X_CFG_MAPB_OPTIONS_PCICH | | ||
252 | MPC10X_CFG_MAPB_OPTIONS_PROCCH); | ||
253 | |||
254 | if (host_bridge != MPC10X_BRIDGE_106) { | ||
255 | byte |= MPC10X_CFG_MAPB_OPTIONS_CFAE; | ||
256 | } | ||
257 | |||
258 | early_write_config_byte(hose, | ||
259 | 0, | ||
260 | PCI_DEVFN(0,0), | ||
261 | MPC10X_CFG_MAPB_OPTIONS_REG, | ||
262 | byte); | ||
263 | } | ||
264 | |||
265 | if (host_bridge != MPC10X_BRIDGE_106) { | ||
266 | early_read_config_byte(hose, | ||
267 | 0, | ||
268 | PCI_DEVFN(0,0), | ||
269 | MPC10X_CFG_PIR_REG, | ||
270 | &pir); | ||
271 | |||
272 | if (pir != MPC10X_CFG_PIR_HOST_BRIDGE) { | ||
273 | printk("Host bridge in Agent mode\n"); | ||
274 | /* Read or Set LMBAR & PCSRBAR? */ | ||
275 | } | ||
276 | |||
277 | /* Set base addr of the 8240/107 EUMB. */ | ||
278 | early_write_config_dword(hose, | ||
279 | 0, | ||
280 | PCI_DEVFN(0,0), | ||
281 | MPC10X_CFG_EUMBBAR, | ||
282 | phys_eumb_base); | ||
283 | #ifdef CONFIG_MPC10X_OPENPIC | ||
284 | /* Map EPIC register part of EUMB into vitual memory - PCORE | ||
285 | uses an i8259 instead of EPIC. */ | ||
286 | OpenPIC_Addr = | ||
287 | ioremap(phys_eumb_base + MPC10X_EUMB_EPIC_OFFSET, | ||
288 | MPC10X_EUMB_EPIC_SIZE); | ||
289 | #endif | ||
290 | mpc10x_i2c_ocp.paddr = phys_eumb_base + MPC10X_EUMB_I2C_OFFSET; | ||
291 | mpc10x_i2c_ocp.irq = MPC10X_I2C_IRQ; | ||
292 | ocp_add_one_device(&mpc10x_i2c_ocp); | ||
293 | mpc10x_dma_ocp[0].paddr = phys_eumb_base + | ||
294 | MPC10X_EUMB_DMA_OFFSET + 0x100; | ||
295 | mpc10x_dma_ocp[0].irq = MPC10X_DMA0_IRQ; | ||
296 | ocp_add_one_device(&mpc10x_dma_ocp[0]); | ||
297 | mpc10x_dma_ocp[1].paddr = phys_eumb_base + | ||
298 | MPC10X_EUMB_DMA_OFFSET + 0x200; | ||
299 | mpc10x_dma_ocp[1].irq = MPC10X_DMA1_IRQ; | ||
300 | ocp_add_one_device(&mpc10x_dma_ocp[1]); | ||
301 | } | ||
302 | |||
303 | #ifdef CONFIG_MPC10X_STORE_GATHERING | ||
304 | mpc10x_enable_store_gathering(hose); | ||
305 | #else | ||
306 | mpc10x_disable_store_gathering(hose); | ||
307 | #endif | ||
308 | |||
309 | /* | ||
310 | * 8240 erratum 26, 8241/8245 erratum 29, 107 erratum 23: speculative | ||
311 | * PCI reads may return stale data so turn off. | ||
312 | */ | ||
313 | if ((host_bridge == MPC10X_BRIDGE_8240) | ||
314 | || (host_bridge == MPC10X_BRIDGE_8245) | ||
315 | || (host_bridge == MPC10X_BRIDGE_107)) { | ||
316 | |||
317 | early_read_config_dword(hose, 0, PCI_DEVFN(0,0), | ||
318 | MPC10X_CFG_PICR1_REG, &picr1); | ||
319 | |||
320 | picr1 &= ~MPC10X_CFG_PICR1_SPEC_PCI_RD; | ||
321 | |||
322 | early_write_config_dword(hose, 0, PCI_DEVFN(0,0), | ||
323 | MPC10X_CFG_PICR1_REG, picr1); | ||
324 | } | ||
325 | |||
326 | /* | ||
327 | * 8241/8245 erratum 28: PCI reads from local memory may return | ||
328 | * stale data. Workaround by setting PICR2[0] to disable copyback | ||
329 | * optimization. Oddly, the latest available user manual for the | ||
330 | * 8245 (Rev 2., dated 10/2003) says PICR2[0] is reserverd. | ||
331 | */ | ||
332 | if (host_bridge == MPC10X_BRIDGE_8245) { | ||
333 | ulong picr2; | ||
334 | |||
335 | early_read_config_dword(hose, 0, PCI_DEVFN(0,0), | ||
336 | MPC10X_CFG_PICR2_REG, &picr2); | ||
337 | |||
338 | picr2 |= MPC10X_CFG_PICR2_COPYBACK_OPT; | ||
339 | |||
340 | early_write_config_dword(hose, 0, PCI_DEVFN(0,0), | ||
341 | MPC10X_CFG_PICR2_REG, picr2); | ||
342 | } | ||
343 | |||
344 | if (ppc_md.progress) ppc_md.progress("mpc10x:exit", 0x100); | ||
345 | return 0; | ||
346 | } | ||
347 | |||
348 | /* | ||
349 | * Need to make our own PCI config space access macros because | ||
350 | * mpc10x_get_mem_size() is called before the data structures are set up for | ||
351 | * the 'early_xxx' and 'indirect_xxx' routines to work. | ||
352 | * Assumes bus 0. | ||
353 | */ | ||
354 | #define MPC10X_CFG_read(val, addr, type, op) *val = op((type)(addr)) | ||
355 | #define MPC10X_CFG_write(val, addr, type, op) op((type *)(addr), (val)) | ||
356 | |||
357 | #define MPC10X_PCI_OP(rw, size, type, op, mask) \ | ||
358 | static void \ | ||
359 | mpc10x_##rw##_config_##size(uint *cfg_addr, uint *cfg_data, int devfn, int offset, type val) \ | ||
360 | { \ | ||
361 | out_be32(cfg_addr, \ | ||
362 | ((offset & 0xfc) << 24) | (devfn << 16) \ | ||
363 | | (0 << 8) | 0x80); \ | ||
364 | MPC10X_CFG_##rw(val, cfg_data + (offset & mask), type, op); \ | ||
365 | return; \ | ||
366 | } | ||
367 | |||
368 | MPC10X_PCI_OP(read, byte, u8 *, in_8, 3) | ||
369 | MPC10X_PCI_OP(read, dword, u32 *, in_le32, 0) | ||
370 | #if 0 /* Not used */ | ||
371 | MPC10X_PCI_OP(write, byte, u8, out_8, 3) | ||
372 | MPC10X_PCI_OP(read, word, u16 *, in_le16, 2) | ||
373 | MPC10X_PCI_OP(write, word, u16, out_le16, 2) | ||
374 | MPC10X_PCI_OP(write, dword, u32, out_le32, 0) | ||
375 | #endif | ||
376 | |||
377 | /* | ||
378 | * Read the memory controller registers to determine the amount of memory in | ||
379 | * the system. This assumes that the firmware has correctly set up the memory | ||
380 | * controller registers. | ||
381 | */ | ||
382 | unsigned long __init | ||
383 | mpc10x_get_mem_size(uint mem_map) | ||
384 | { | ||
385 | uint *config_addr, *config_data, val; | ||
386 | ulong start, end, total, offset; | ||
387 | int i; | ||
388 | u_char bank_enables; | ||
389 | |||
390 | switch (mem_map) { | ||
391 | case MPC10X_MEM_MAP_A: | ||
392 | config_addr = (uint *)MPC10X_MAPA_CNFG_ADDR; | ||
393 | config_data = (uint *)MPC10X_MAPA_CNFG_DATA; | ||
394 | break; | ||
395 | case MPC10X_MEM_MAP_B: | ||
396 | config_addr = (uint *)MPC10X_MAPB_CNFG_ADDR; | ||
397 | config_data = (uint *)MPC10X_MAPB_CNFG_DATA; | ||
398 | break; | ||
399 | default: | ||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | mpc10x_read_config_byte(config_addr, | ||
404 | config_data, | ||
405 | PCI_DEVFN(0,0), | ||
406 | MPC10X_MCTLR_MEM_BANK_ENABLES, | ||
407 | &bank_enables); | ||
408 | |||
409 | total = 0; | ||
410 | |||
411 | for (i=0; i<8; i++) { | ||
412 | if (bank_enables & (1 << i)) { | ||
413 | offset = MPC10X_MCTLR_MEM_START_1 + ((i > 3) ? 4 : 0); | ||
414 | mpc10x_read_config_dword(config_addr, | ||
415 | config_data, | ||
416 | PCI_DEVFN(0,0), | ||
417 | offset, | ||
418 | &val); | ||
419 | start = (val >> ((i & 3) << 3)) & 0xff; | ||
420 | |||
421 | offset = MPC10X_MCTLR_EXT_MEM_START_1 + ((i>3) ? 4 : 0); | ||
422 | mpc10x_read_config_dword(config_addr, | ||
423 | config_data, | ||
424 | PCI_DEVFN(0,0), | ||
425 | offset, | ||
426 | &val); | ||
427 | val = (val >> ((i & 3) << 3)) & 0x03; | ||
428 | start = (val << 28) | (start << 20); | ||
429 | |||
430 | offset = MPC10X_MCTLR_MEM_END_1 + ((i > 3) ? 4 : 0); | ||
431 | mpc10x_read_config_dword(config_addr, | ||
432 | config_data, | ||
433 | PCI_DEVFN(0,0), | ||
434 | offset, | ||
435 | &val); | ||
436 | end = (val >> ((i & 3) << 3)) & 0xff; | ||
437 | |||
438 | offset = MPC10X_MCTLR_EXT_MEM_END_1 + ((i > 3) ? 4 : 0); | ||
439 | mpc10x_read_config_dword(config_addr, | ||
440 | config_data, | ||
441 | PCI_DEVFN(0,0), | ||
442 | offset, | ||
443 | &val); | ||
444 | val = (val >> ((i & 3) << 3)) & 0x03; | ||
445 | end = (val << 28) | (end << 20) | 0xfffff; | ||
446 | |||
447 | total += (end - start + 1); | ||
448 | } | ||
449 | } | ||
450 | |||
451 | return total; | ||
452 | } | ||
453 | |||
454 | int __init | ||
455 | mpc10x_enable_store_gathering(struct pci_controller *hose) | ||
456 | { | ||
457 | uint picr1; | ||
458 | |||
459 | early_read_config_dword(hose, | ||
460 | 0, | ||
461 | PCI_DEVFN(0,0), | ||
462 | MPC10X_CFG_PICR1_REG, | ||
463 | &picr1); | ||
464 | |||
465 | picr1 |= MPC10X_CFG_PICR1_ST_GATH_EN; | ||
466 | |||
467 | early_write_config_dword(hose, | ||
468 | 0, | ||
469 | PCI_DEVFN(0,0), | ||
470 | MPC10X_CFG_PICR1_REG, | ||
471 | picr1); | ||
472 | |||
473 | return 0; | ||
474 | } | ||
475 | |||
476 | int __init | ||
477 | mpc10x_disable_store_gathering(struct pci_controller *hose) | ||
478 | { | ||
479 | uint picr1; | ||
480 | |||
481 | early_read_config_dword(hose, | ||
482 | 0, | ||
483 | PCI_DEVFN(0,0), | ||
484 | MPC10X_CFG_PICR1_REG, | ||
485 | &picr1); | ||
486 | |||
487 | picr1 &= ~MPC10X_CFG_PICR1_ST_GATH_EN; | ||
488 | |||
489 | early_write_config_dword(hose, | ||
490 | 0, | ||
491 | PCI_DEVFN(0,0), | ||
492 | MPC10X_CFG_PICR1_REG, | ||
493 | picr1); | ||
494 | |||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | #ifdef CONFIG_MPC10X_OPENPIC | ||
499 | void __init mpc10x_set_openpic(void) | ||
500 | { | ||
501 | /* Map external IRQs */ | ||
502 | openpic_set_sources(0, EPIC_IRQ_BASE, OpenPIC_Addr + 0x10200); | ||
503 | /* Skip reserved space and map i2c and DMA Ch[01] */ | ||
504 | openpic_set_sources(EPIC_IRQ_BASE, 3, OpenPIC_Addr + 0x11020); | ||
505 | /* Skip reserved space and map Message Unit Interrupt (I2O) */ | ||
506 | openpic_set_sources(EPIC_IRQ_BASE + 3, 1, OpenPIC_Addr + 0x110C0); | ||
507 | |||
508 | openpic_init(NUM_8259_INTERRUPTS); | ||
509 | } | ||
510 | #endif | ||
diff --git a/arch/ppc/syslib/mpc52xx_devices.c b/arch/ppc/syslib/mpc52xx_devices.c new file mode 100644 index 000000000000..ad5182efca1d --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_devices.c | |||
@@ -0,0 +1,318 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mpc52xx_devices.c | ||
3 | * | ||
4 | * Freescale MPC52xx device descriptions | ||
5 | * | ||
6 | * | ||
7 | * Maintainer : Sylvain Munaut <tnt@246tNt.com> | ||
8 | * | ||
9 | * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com> | ||
10 | * | ||
11 | * This file is licensed under the terms of the GNU General Public License | ||
12 | * version 2. This program is licensed "as is" without any warranty of any | ||
13 | * kind, whether express or implied. | ||
14 | */ | ||
15 | |||
16 | #include <linux/fsl_devices.h> | ||
17 | #include <linux/resource.h> | ||
18 | #include <asm/mpc52xx.h> | ||
19 | #include <asm/ppc_sys.h> | ||
20 | |||
21 | |||
22 | static u64 mpc52xx_dma_mask = 0xffffffffULL; | ||
23 | |||
24 | static struct fsl_i2c_platform_data mpc52xx_fsl_i2c_pdata = { | ||
25 | .device_flags = FSL_I2C_DEV_CLOCK_5200, | ||
26 | }; | ||
27 | |||
28 | |||
29 | /* We use relative offsets for IORESOURCE_MEM to be independent from the | ||
30 | * MBAR location at compile time | ||
31 | */ | ||
32 | |||
33 | /* TODO Add the BestComm initiator channel to the device definitions, | ||
34 | possibly using IORESOURCE_DMA. But that's when BestComm is ready ... */ | ||
35 | |||
36 | struct platform_device ppc_sys_platform_devices[] = { | ||
37 | [MPC52xx_MSCAN1] = { | ||
38 | .name = "mpc52xx-mscan", | ||
39 | .id = 0, | ||
40 | .num_resources = 2, | ||
41 | .resource = (struct resource[]) { | ||
42 | { | ||
43 | .start = 0x0900, | ||
44 | .end = 0x097f, | ||
45 | .flags = IORESOURCE_MEM, | ||
46 | }, | ||
47 | { | ||
48 | .start = MPC52xx_MSCAN1_IRQ, | ||
49 | .end = MPC52xx_MSCAN1_IRQ, | ||
50 | .flags = IORESOURCE_IRQ, | ||
51 | }, | ||
52 | }, | ||
53 | }, | ||
54 | [MPC52xx_MSCAN2] = { | ||
55 | .name = "mpc52xx-mscan", | ||
56 | .id = 1, | ||
57 | .num_resources = 2, | ||
58 | .resource = (struct resource[]) { | ||
59 | { | ||
60 | .start = 0x0980, | ||
61 | .end = 0x09ff, | ||
62 | .flags = IORESOURCE_MEM, | ||
63 | }, | ||
64 | { | ||
65 | .start = MPC52xx_MSCAN2_IRQ, | ||
66 | .end = MPC52xx_MSCAN2_IRQ, | ||
67 | .flags = IORESOURCE_IRQ, | ||
68 | }, | ||
69 | }, | ||
70 | }, | ||
71 | [MPC52xx_SPI] = { | ||
72 | .name = "mpc52xx-spi", | ||
73 | .id = -1, | ||
74 | .num_resources = 3, | ||
75 | .resource = (struct resource[]) { | ||
76 | { | ||
77 | .start = 0x0f00, | ||
78 | .end = 0x0f1f, | ||
79 | .flags = IORESOURCE_MEM, | ||
80 | }, | ||
81 | { | ||
82 | .name = "modf", | ||
83 | .start = MPC52xx_SPI_MODF_IRQ, | ||
84 | .end = MPC52xx_SPI_MODF_IRQ, | ||
85 | .flags = IORESOURCE_IRQ, | ||
86 | }, | ||
87 | { | ||
88 | .name = "spif", | ||
89 | .start = MPC52xx_SPI_SPIF_IRQ, | ||
90 | .end = MPC52xx_SPI_SPIF_IRQ, | ||
91 | .flags = IORESOURCE_IRQ, | ||
92 | }, | ||
93 | }, | ||
94 | }, | ||
95 | [MPC52xx_USB] = { | ||
96 | .name = "ppc-soc-ohci", | ||
97 | .id = -1, | ||
98 | .num_resources = 2, | ||
99 | .dev.dma_mask = &mpc52xx_dma_mask, | ||
100 | .dev.coherent_dma_mask = 0xffffffffULL, | ||
101 | .resource = (struct resource[]) { | ||
102 | { | ||
103 | .start = 0x1000, | ||
104 | .end = 0x10ff, | ||
105 | .flags = IORESOURCE_MEM, | ||
106 | }, | ||
107 | { | ||
108 | .start = MPC52xx_USB_IRQ, | ||
109 | .end = MPC52xx_USB_IRQ, | ||
110 | .flags = IORESOURCE_IRQ, | ||
111 | }, | ||
112 | }, | ||
113 | }, | ||
114 | [MPC52xx_BDLC] = { | ||
115 | .name = "mpc52xx-bdlc", | ||
116 | .id = -1, | ||
117 | .num_resources = 2, | ||
118 | .resource = (struct resource[]) { | ||
119 | { | ||
120 | .start = 0x1300, | ||
121 | .end = 0x130f, | ||
122 | .flags = IORESOURCE_MEM, | ||
123 | }, | ||
124 | { | ||
125 | .start = MPC52xx_BDLC_IRQ, | ||
126 | .end = MPC52xx_BDLC_IRQ, | ||
127 | .flags = IORESOURCE_IRQ, | ||
128 | }, | ||
129 | }, | ||
130 | }, | ||
131 | [MPC52xx_PSC1] = { | ||
132 | .name = "mpc52xx-psc", | ||
133 | .id = 0, | ||
134 | .num_resources = 2, | ||
135 | .resource = (struct resource[]) { | ||
136 | { | ||
137 | .start = 0x2000, | ||
138 | .end = 0x209f, | ||
139 | .flags = IORESOURCE_MEM, | ||
140 | }, | ||
141 | { | ||
142 | .start = MPC52xx_PSC1_IRQ, | ||
143 | .end = MPC52xx_PSC1_IRQ, | ||
144 | .flags = IORESOURCE_IRQ, | ||
145 | }, | ||
146 | }, | ||
147 | }, | ||
148 | [MPC52xx_PSC2] = { | ||
149 | .name = "mpc52xx-psc", | ||
150 | .id = 1, | ||
151 | .num_resources = 2, | ||
152 | .resource = (struct resource[]) { | ||
153 | { | ||
154 | .start = 0x2200, | ||
155 | .end = 0x229f, | ||
156 | .flags = IORESOURCE_MEM, | ||
157 | }, | ||
158 | { | ||
159 | .start = MPC52xx_PSC2_IRQ, | ||
160 | .end = MPC52xx_PSC2_IRQ, | ||
161 | .flags = IORESOURCE_IRQ, | ||
162 | }, | ||
163 | }, | ||
164 | }, | ||
165 | [MPC52xx_PSC3] = { | ||
166 | .name = "mpc52xx-psc", | ||
167 | .id = 2, | ||
168 | .num_resources = 2, | ||
169 | .resource = (struct resource[]) { | ||
170 | { | ||
171 | .start = 0x2400, | ||
172 | .end = 0x249f, | ||
173 | .flags = IORESOURCE_MEM, | ||
174 | }, | ||
175 | { | ||
176 | .start = MPC52xx_PSC3_IRQ, | ||
177 | .end = MPC52xx_PSC3_IRQ, | ||
178 | .flags = IORESOURCE_IRQ, | ||
179 | }, | ||
180 | }, | ||
181 | }, | ||
182 | [MPC52xx_PSC4] = { | ||
183 | .name = "mpc52xx-psc", | ||
184 | .id = 3, | ||
185 | .num_resources = 2, | ||
186 | .resource = (struct resource[]) { | ||
187 | { | ||
188 | .start = 0x2600, | ||
189 | .end = 0x269f, | ||
190 | .flags = IORESOURCE_MEM, | ||
191 | }, | ||
192 | { | ||
193 | .start = MPC52xx_PSC4_IRQ, | ||
194 | .end = MPC52xx_PSC4_IRQ, | ||
195 | .flags = IORESOURCE_IRQ, | ||
196 | }, | ||
197 | }, | ||
198 | }, | ||
199 | [MPC52xx_PSC5] = { | ||
200 | .name = "mpc52xx-psc", | ||
201 | .id = 4, | ||
202 | .num_resources = 2, | ||
203 | .resource = (struct resource[]) { | ||
204 | { | ||
205 | .start = 0x2800, | ||
206 | .end = 0x289f, | ||
207 | .flags = IORESOURCE_MEM, | ||
208 | }, | ||
209 | { | ||
210 | .start = MPC52xx_PSC5_IRQ, | ||
211 | .end = MPC52xx_PSC5_IRQ, | ||
212 | .flags = IORESOURCE_IRQ, | ||
213 | }, | ||
214 | }, | ||
215 | }, | ||
216 | [MPC52xx_PSC6] = { | ||
217 | .name = "mpc52xx-psc", | ||
218 | .id = 5, | ||
219 | .num_resources = 2, | ||
220 | .resource = (struct resource[]) { | ||
221 | { | ||
222 | .start = 0x2c00, | ||
223 | .end = 0x2c9f, | ||
224 | .flags = IORESOURCE_MEM, | ||
225 | }, | ||
226 | { | ||
227 | .start = MPC52xx_PSC6_IRQ, | ||
228 | .end = MPC52xx_PSC6_IRQ, | ||
229 | .flags = IORESOURCE_IRQ, | ||
230 | }, | ||
231 | }, | ||
232 | }, | ||
233 | [MPC52xx_FEC] = { | ||
234 | .name = "mpc52xx-fec", | ||
235 | .id = -1, | ||
236 | .num_resources = 2, | ||
237 | .resource = (struct resource[]) { | ||
238 | { | ||
239 | .start = 0x3000, | ||
240 | .end = 0x33ff, | ||
241 | .flags = IORESOURCE_MEM, | ||
242 | }, | ||
243 | { | ||
244 | .start = MPC52xx_FEC_IRQ, | ||
245 | .end = MPC52xx_FEC_IRQ, | ||
246 | .flags = IORESOURCE_IRQ, | ||
247 | }, | ||
248 | }, | ||
249 | }, | ||
250 | [MPC52xx_ATA] = { | ||
251 | .name = "mpc52xx-ata", | ||
252 | .id = -1, | ||
253 | .num_resources = 2, | ||
254 | .resource = (struct resource[]) { | ||
255 | { | ||
256 | .start = 0x3a00, | ||
257 | .end = 0x3aff, | ||
258 | .flags = IORESOURCE_MEM, | ||
259 | }, | ||
260 | { | ||
261 | .start = MPC52xx_ATA_IRQ, | ||
262 | .end = MPC52xx_ATA_IRQ, | ||
263 | .flags = IORESOURCE_IRQ, | ||
264 | }, | ||
265 | }, | ||
266 | }, | ||
267 | [MPC52xx_I2C1] = { | ||
268 | .name = "fsl-i2c", | ||
269 | .id = 0, | ||
270 | .dev.platform_data = &mpc52xx_fsl_i2c_pdata, | ||
271 | .num_resources = 2, | ||
272 | .resource = (struct resource[]) { | ||
273 | { | ||
274 | .start = 0x3d00, | ||
275 | .end = 0x3d1f, | ||
276 | .flags = IORESOURCE_MEM, | ||
277 | }, | ||
278 | { | ||
279 | .start = MPC52xx_I2C1_IRQ, | ||
280 | .end = MPC52xx_I2C1_IRQ, | ||
281 | .flags = IORESOURCE_IRQ, | ||
282 | }, | ||
283 | }, | ||
284 | }, | ||
285 | [MPC52xx_I2C2] = { | ||
286 | .name = "fsl-i2c", | ||
287 | .id = 1, | ||
288 | .dev.platform_data = &mpc52xx_fsl_i2c_pdata, | ||
289 | .num_resources = 2, | ||
290 | .resource = (struct resource[]) { | ||
291 | { | ||
292 | .start = 0x3d40, | ||
293 | .end = 0x3d5f, | ||
294 | .flags = IORESOURCE_MEM, | ||
295 | }, | ||
296 | { | ||
297 | .start = MPC52xx_I2C2_IRQ, | ||
298 | .end = MPC52xx_I2C2_IRQ, | ||
299 | .flags = IORESOURCE_IRQ, | ||
300 | }, | ||
301 | }, | ||
302 | }, | ||
303 | }; | ||
304 | |||
305 | |||
306 | static int __init mach_mpc52xx_fixup(struct platform_device *pdev) | ||
307 | { | ||
308 | ppc_sys_fixup_mem_resource(pdev, MPC52xx_MBAR); | ||
309 | return 0; | ||
310 | } | ||
311 | |||
312 | static int __init mach_mpc52xx_init(void) | ||
313 | { | ||
314 | ppc_sys_device_fixup = mach_mpc52xx_fixup; | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | postcore_initcall(mach_mpc52xx_init); | ||
diff --git a/arch/ppc/syslib/mpc52xx_pci.c b/arch/ppc/syslib/mpc52xx_pci.c new file mode 100644 index 000000000000..c723efd954a6 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pci.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mpc52xx_pci.c | ||
3 | * | ||
4 | * PCI code for the Freescale MPC52xx embedded CPU. | ||
5 | * | ||
6 | * | ||
7 | * Maintainer : Sylvain Munaut <tnt@246tNt.com> | ||
8 | * | ||
9 | * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> | ||
10 | * | ||
11 | * This file is licensed under the terms of the GNU General Public License | ||
12 | * version 2. This program is licensed "as is" without any warranty of any | ||
13 | * kind, whether express or implied. | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | |||
18 | #include <asm/pci.h> | ||
19 | |||
20 | #include <asm/mpc52xx.h> | ||
21 | #include "mpc52xx_pci.h" | ||
22 | |||
23 | #include <asm/delay.h> | ||
24 | |||
25 | |||
26 | static int | ||
27 | mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, | ||
28 | int offset, int len, u32 *val) | ||
29 | { | ||
30 | struct pci_controller *hose = bus->sysdata; | ||
31 | u32 value; | ||
32 | |||
33 | if (ppc_md.pci_exclude_device) | ||
34 | if (ppc_md.pci_exclude_device(bus->number, devfn)) | ||
35 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
36 | |||
37 | out_be32(hose->cfg_addr, | ||
38 | (1 << 31) | | ||
39 | ((bus->number - hose->bus_offset) << 16) | | ||
40 | (devfn << 8) | | ||
41 | (offset & 0xfc)); | ||
42 | |||
43 | value = in_le32(hose->cfg_data); | ||
44 | |||
45 | if (len != 4) { | ||
46 | value >>= ((offset & 0x3) << 3); | ||
47 | value &= 0xffffffff >> (32 - (len << 3)); | ||
48 | } | ||
49 | |||
50 | *val = value; | ||
51 | |||
52 | out_be32(hose->cfg_addr, 0); | ||
53 | |||
54 | return PCIBIOS_SUCCESSFUL; | ||
55 | } | ||
56 | |||
57 | static int | ||
58 | mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, | ||
59 | int offset, int len, u32 val) | ||
60 | { | ||
61 | struct pci_controller *hose = bus->sysdata; | ||
62 | u32 value, mask; | ||
63 | |||
64 | if (ppc_md.pci_exclude_device) | ||
65 | if (ppc_md.pci_exclude_device(bus->number, devfn)) | ||
66 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
67 | |||
68 | out_be32(hose->cfg_addr, | ||
69 | (1 << 31) | | ||
70 | ((bus->number - hose->bus_offset) << 16) | | ||
71 | (devfn << 8) | | ||
72 | (offset & 0xfc)); | ||
73 | |||
74 | if (len != 4) { | ||
75 | value = in_le32(hose->cfg_data); | ||
76 | |||
77 | offset = (offset & 0x3) << 3; | ||
78 | mask = (0xffffffff >> (32 - (len << 3))); | ||
79 | mask <<= offset; | ||
80 | |||
81 | value &= ~mask; | ||
82 | val = value | ((val << offset) & mask); | ||
83 | } | ||
84 | |||
85 | out_le32(hose->cfg_data, val); | ||
86 | |||
87 | out_be32(hose->cfg_addr, 0); | ||
88 | |||
89 | return PCIBIOS_SUCCESSFUL; | ||
90 | } | ||
91 | |||
92 | static struct pci_ops mpc52xx_pci_ops = { | ||
93 | .read = mpc52xx_pci_read_config, | ||
94 | .write = mpc52xx_pci_write_config | ||
95 | }; | ||
96 | |||
97 | |||
98 | static void __init | ||
99 | mpc52xx_pci_setup(struct mpc52xx_pci __iomem *pci_regs) | ||
100 | { | ||
101 | |||
102 | /* Setup control regs */ | ||
103 | /* Nothing to do afaik */ | ||
104 | |||
105 | /* Setup windows */ | ||
106 | out_be32(&pci_regs->iw0btar, MPC52xx_PCI_IWBTAR_TRANSLATION( | ||
107 | MPC52xx_PCI_MEM_START + MPC52xx_PCI_MEM_OFFSET, | ||
108 | MPC52xx_PCI_MEM_START, | ||
109 | MPC52xx_PCI_MEM_SIZE )); | ||
110 | |||
111 | out_be32(&pci_regs->iw1btar, MPC52xx_PCI_IWBTAR_TRANSLATION( | ||
112 | MPC52xx_PCI_MMIO_START + MPC52xx_PCI_MEM_OFFSET, | ||
113 | MPC52xx_PCI_MMIO_START, | ||
114 | MPC52xx_PCI_MMIO_SIZE )); | ||
115 | |||
116 | out_be32(&pci_regs->iw2btar, MPC52xx_PCI_IWBTAR_TRANSLATION( | ||
117 | MPC52xx_PCI_IO_BASE, | ||
118 | MPC52xx_PCI_IO_START, | ||
119 | MPC52xx_PCI_IO_SIZE )); | ||
120 | |||
121 | out_be32(&pci_regs->iwcr, MPC52xx_PCI_IWCR_PACK( | ||
122 | ( MPC52xx_PCI_IWCR_ENABLE | /* iw0btar */ | ||
123 | MPC52xx_PCI_IWCR_READ_MULTI | | ||
124 | MPC52xx_PCI_IWCR_MEM ), | ||
125 | ( MPC52xx_PCI_IWCR_ENABLE | /* iw1btar */ | ||
126 | MPC52xx_PCI_IWCR_READ | | ||
127 | MPC52xx_PCI_IWCR_MEM ), | ||
128 | ( MPC52xx_PCI_IWCR_ENABLE | /* iw2btar */ | ||
129 | MPC52xx_PCI_IWCR_IO ) | ||
130 | )); | ||
131 | |||
132 | |||
133 | out_be32(&pci_regs->tbatr0, | ||
134 | MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_IO ); | ||
135 | out_be32(&pci_regs->tbatr1, | ||
136 | MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_MEM ); | ||
137 | |||
138 | out_be32(&pci_regs->tcr, MPC52xx_PCI_TCR_LD); | ||
139 | |||
140 | /* Reset the exteral bus ( internal PCI controller is NOT resetted ) */ | ||
141 | /* Not necessary and can be a bad thing if for example the bootloader | ||
142 | is displaying a splash screen or ... Just left here for | ||
143 | documentation purpose if anyone need it */ | ||
144 | #if 0 | ||
145 | u32 tmp; | ||
146 | tmp = in_be32(&pci_regs->gscr); | ||
147 | out_be32(&pci_regs->gscr, tmp | MPC52xx_PCI_GSCR_PR); | ||
148 | udelay(50); | ||
149 | out_be32(&pci_regs->gscr, tmp); | ||
150 | #endif | ||
151 | } | ||
152 | |||
153 | static void __init | ||
154 | mpc52xx_pci_fixup_resources(struct pci_dev *dev) | ||
155 | { | ||
156 | int i; | ||
157 | |||
158 | /* We don't rely on boot loader for PCI and resets all | ||
159 | devices */ | ||
160 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | ||
161 | struct resource *res = &dev->resource[i]; | ||
162 | if (res->end > res->start) { /* Only valid resources */ | ||
163 | res->end -= res->start; | ||
164 | res->start = 0; | ||
165 | res->flags |= IORESOURCE_UNSET; | ||
166 | } | ||
167 | } | ||
168 | |||
169 | /* The PCI Host bridge of MPC52xx has a prefetch memory resource | ||
170 | fixed to 1Gb. Doesn't fit in the resource system so we remove it */ | ||
171 | if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) && | ||
172 | (dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200) ) { | ||
173 | struct resource *res = &dev->resource[1]; | ||
174 | res->start = res->end = res->flags = 0; | ||
175 | } | ||
176 | } | ||
177 | |||
178 | void __init | ||
179 | mpc52xx_find_bridges(void) | ||
180 | { | ||
181 | struct mpc52xx_pci __iomem *pci_regs; | ||
182 | struct pci_controller *hose; | ||
183 | |||
184 | pci_assign_all_busses = 1; | ||
185 | |||
186 | pci_regs = ioremap(MPC52xx_PA(MPC52xx_PCI_OFFSET), MPC52xx_PCI_SIZE); | ||
187 | if (!pci_regs) | ||
188 | return; | ||
189 | |||
190 | hose = pcibios_alloc_controller(); | ||
191 | if (!hose) { | ||
192 | iounmap(pci_regs); | ||
193 | return; | ||
194 | } | ||
195 | |||
196 | ppc_md.pci_swizzle = common_swizzle; | ||
197 | ppc_md.pcibios_fixup_resources = mpc52xx_pci_fixup_resources; | ||
198 | |||
199 | hose->first_busno = 0; | ||
200 | hose->last_busno = 0xff; | ||
201 | hose->bus_offset = 0; | ||
202 | hose->ops = &mpc52xx_pci_ops; | ||
203 | |||
204 | mpc52xx_pci_setup(pci_regs); | ||
205 | |||
206 | hose->pci_mem_offset = MPC52xx_PCI_MEM_OFFSET; | ||
207 | |||
208 | isa_io_base = | ||
209 | (unsigned long) ioremap(MPC52xx_PCI_IO_BASE, | ||
210 | MPC52xx_PCI_IO_SIZE); | ||
211 | hose->io_base_virt = (void *) isa_io_base; | ||
212 | |||
213 | hose->cfg_addr = &pci_regs->car; | ||
214 | hose->cfg_data = (void __iomem *) isa_io_base; | ||
215 | |||
216 | /* Setup resources */ | ||
217 | pci_init_resource(&hose->mem_resources[0], | ||
218 | MPC52xx_PCI_MEM_START, | ||
219 | MPC52xx_PCI_MEM_STOP, | ||
220 | IORESOURCE_MEM|IORESOURCE_PREFETCH, | ||
221 | "PCI prefetchable memory"); | ||
222 | |||
223 | pci_init_resource(&hose->mem_resources[1], | ||
224 | MPC52xx_PCI_MMIO_START, | ||
225 | MPC52xx_PCI_MMIO_STOP, | ||
226 | IORESOURCE_MEM, | ||
227 | "PCI memory"); | ||
228 | |||
229 | pci_init_resource(&hose->io_resource, | ||
230 | MPC52xx_PCI_IO_START, | ||
231 | MPC52xx_PCI_IO_STOP, | ||
232 | IORESOURCE_IO, | ||
233 | "PCI I/O"); | ||
234 | |||
235 | } | ||
diff --git a/arch/ppc/syslib/mpc52xx_pci.h b/arch/ppc/syslib/mpc52xx_pci.h new file mode 100644 index 000000000000..04b509a02530 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pci.h | |||
@@ -0,0 +1,139 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mpc52xx_pci.h | ||
3 | * | ||
4 | * PCI Include file the Freescale MPC52xx embedded cpu chips | ||
5 | * | ||
6 | * | ||
7 | * Maintainer : Sylvain Munaut <tnt@246tNt.com> | ||
8 | * | ||
9 | * Inspired from code written by Dale Farnsworth <dfarnsworth@mvista.com> | ||
10 | * for the 2.4 kernel. | ||
11 | * | ||
12 | * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> | ||
13 | * Copyright (C) 2003 MontaVista, Software, Inc. | ||
14 | * | ||
15 | * This file is licensed under the terms of the GNU General Public License | ||
16 | * version 2. This program is licensed "as is" without any warranty of any | ||
17 | * kind, whether express or implied. | ||
18 | */ | ||
19 | |||
20 | #ifndef __SYSLIB_MPC52xx_PCI_H__ | ||
21 | #define __SYSLIB_MPC52xx_PCI_H__ | ||
22 | |||
23 | /* ======================================================================== */ | ||
24 | /* PCI windows config */ | ||
25 | /* ======================================================================== */ | ||
26 | |||
27 | /* | ||
28 | * Master windows : MPC52xx -> PCI | ||
29 | * | ||
30 | * 0x80000000 -> 0x9FFFFFFF PCI Mem prefetchable IW0BTAR | ||
31 | * 0xA0000000 -> 0xAFFFFFFF PCI Mem IW1BTAR | ||
32 | * 0xB0000000 -> 0xB0FFFFFF PCI IO IW2BTAR | ||
33 | * | ||
34 | * Slave windows : PCI -> MPC52xx | ||
35 | * | ||
36 | * 0xF0000000 -> 0xF003FFFF MPC52xx MBAR TBATR0 | ||
37 | * 0x00000000 -> 0x3FFFFFFF MPC52xx local memory TBATR1 | ||
38 | */ | ||
39 | |||
40 | #define MPC52xx_PCI_MEM_OFFSET 0x00000000 /* Offset for MEM MMIO */ | ||
41 | |||
42 | #define MPC52xx_PCI_MEM_START 0x80000000 | ||
43 | #define MPC52xx_PCI_MEM_SIZE 0x20000000 | ||
44 | #define MPC52xx_PCI_MEM_STOP (MPC52xx_PCI_MEM_START+MPC52xx_PCI_MEM_SIZE-1) | ||
45 | |||
46 | #define MPC52xx_PCI_MMIO_START 0xa0000000 | ||
47 | #define MPC52xx_PCI_MMIO_SIZE 0x10000000 | ||
48 | #define MPC52xx_PCI_MMIO_STOP (MPC52xx_PCI_MMIO_START+MPC52xx_PCI_MMIO_SIZE-1) | ||
49 | |||
50 | #define MPC52xx_PCI_IO_BASE 0xb0000000 | ||
51 | |||
52 | #define MPC52xx_PCI_IO_START 0x00000000 | ||
53 | #define MPC52xx_PCI_IO_SIZE 0x01000000 | ||
54 | #define MPC52xx_PCI_IO_STOP (MPC52xx_PCI_IO_START+MPC52xx_PCI_IO_SIZE-1) | ||
55 | |||
56 | |||
57 | #define MPC52xx_PCI_TARGET_IO MPC52xx_MBAR | ||
58 | #define MPC52xx_PCI_TARGET_MEM 0x00000000 | ||
59 | |||
60 | |||
61 | /* ======================================================================== */ | ||
62 | /* Structures mapping & Defines for PCI Unit */ | ||
63 | /* ======================================================================== */ | ||
64 | |||
65 | #define MPC52xx_PCI_GSCR_BM 0x40000000 | ||
66 | #define MPC52xx_PCI_GSCR_PE 0x20000000 | ||
67 | #define MPC52xx_PCI_GSCR_SE 0x10000000 | ||
68 | #define MPC52xx_PCI_GSCR_XLB2PCI_MASK 0x07000000 | ||
69 | #define MPC52xx_PCI_GSCR_XLB2PCI_SHIFT 24 | ||
70 | #define MPC52xx_PCI_GSCR_IPG2PCI_MASK 0x00070000 | ||
71 | #define MPC52xx_PCI_GSCR_IPG2PCI_SHIFT 16 | ||
72 | #define MPC52xx_PCI_GSCR_BME 0x00004000 | ||
73 | #define MPC52xx_PCI_GSCR_PEE 0x00002000 | ||
74 | #define MPC52xx_PCI_GSCR_SEE 0x00001000 | ||
75 | #define MPC52xx_PCI_GSCR_PR 0x00000001 | ||
76 | |||
77 | |||
78 | #define MPC52xx_PCI_IWBTAR_TRANSLATION(proc_ad,pci_ad,size) \ | ||
79 | ( ( (proc_ad) & 0xff000000 ) | \ | ||
80 | ( (((size) - 1) >> 8) & 0x00ff0000 ) | \ | ||
81 | ( ((pci_ad) >> 16) & 0x0000ff00 ) ) | ||
82 | |||
83 | #define MPC52xx_PCI_IWCR_PACK(win0,win1,win2) (((win0) << 24) | \ | ||
84 | ((win1) << 16) | \ | ||
85 | ((win2) << 8)) | ||
86 | |||
87 | #define MPC52xx_PCI_IWCR_DISABLE 0x0 | ||
88 | #define MPC52xx_PCI_IWCR_ENABLE 0x1 | ||
89 | #define MPC52xx_PCI_IWCR_READ 0x0 | ||
90 | #define MPC52xx_PCI_IWCR_READ_LINE 0x2 | ||
91 | #define MPC52xx_PCI_IWCR_READ_MULTI 0x4 | ||
92 | #define MPC52xx_PCI_IWCR_MEM 0x0 | ||
93 | #define MPC52xx_PCI_IWCR_IO 0x8 | ||
94 | |||
95 | #define MPC52xx_PCI_TCR_P 0x01000000 | ||
96 | #define MPC52xx_PCI_TCR_LD 0x00010000 | ||
97 | |||
98 | #define MPC52xx_PCI_TBATR_DISABLE 0x0 | ||
99 | #define MPC52xx_PCI_TBATR_ENABLE 0x1 | ||
100 | |||
101 | |||
102 | #ifndef __ASSEMBLY__ | ||
103 | |||
104 | struct mpc52xx_pci { | ||
105 | u32 idr; /* PCI + 0x00 */ | ||
106 | u32 scr; /* PCI + 0x04 */ | ||
107 | u32 ccrir; /* PCI + 0x08 */ | ||
108 | u32 cr1; /* PCI + 0x0C */ | ||
109 | u32 bar0; /* PCI + 0x10 */ | ||
110 | u32 bar1; /* PCI + 0x14 */ | ||
111 | u8 reserved1[16]; /* PCI + 0x18 */ | ||
112 | u32 ccpr; /* PCI + 0x28 */ | ||
113 | u32 sid; /* PCI + 0x2C */ | ||
114 | u32 erbar; /* PCI + 0x30 */ | ||
115 | u32 cpr; /* PCI + 0x34 */ | ||
116 | u8 reserved2[4]; /* PCI + 0x38 */ | ||
117 | u32 cr2; /* PCI + 0x3C */ | ||
118 | u8 reserved3[32]; /* PCI + 0x40 */ | ||
119 | u32 gscr; /* PCI + 0x60 */ | ||
120 | u32 tbatr0; /* PCI + 0x64 */ | ||
121 | u32 tbatr1; /* PCI + 0x68 */ | ||
122 | u32 tcr; /* PCI + 0x6C */ | ||
123 | u32 iw0btar; /* PCI + 0x70 */ | ||
124 | u32 iw1btar; /* PCI + 0x74 */ | ||
125 | u32 iw2btar; /* PCI + 0x78 */ | ||
126 | u8 reserved4[4]; /* PCI + 0x7C */ | ||
127 | u32 iwcr; /* PCI + 0x80 */ | ||
128 | u32 icr; /* PCI + 0x84 */ | ||
129 | u32 isr; /* PCI + 0x88 */ | ||
130 | u32 arb; /* PCI + 0x8C */ | ||
131 | u8 reserved5[104]; /* PCI + 0x90 */ | ||
132 | u32 car; /* PCI + 0xF8 */ | ||
133 | u8 reserved6[4]; /* PCI + 0xFC */ | ||
134 | }; | ||
135 | |||
136 | #endif /* __ASSEMBLY__ */ | ||
137 | |||
138 | |||
139 | #endif /* __SYSLIB_MPC52xx_PCI_H__ */ | ||
diff --git a/arch/ppc/syslib/mpc52xx_pic.c b/arch/ppc/syslib/mpc52xx_pic.c new file mode 100644 index 000000000000..4c4497e62517 --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_pic.c | |||
@@ -0,0 +1,257 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mpc52xx_pic.c | ||
3 | * | ||
4 | * Programmable Interrupt Controller functions for the Freescale MPC52xx | ||
5 | * embedded CPU. | ||
6 | * | ||
7 | * | ||
8 | * Maintainer : Sylvain Munaut <tnt@246tNt.com> | ||
9 | * | ||
10 | * Based on (well, mostly copied from) the code from the 2.4 kernel by | ||
11 | * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg. | ||
12 | * | ||
13 | * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> | ||
14 | * Copyright (C) 2003 Montavista Software, Inc | ||
15 | * | ||
16 | * This file is licensed under the terms of the GNU General Public License | ||
17 | * version 2. This program is licensed "as is" without any warranty of any | ||
18 | * kind, whether express or implied. | ||
19 | */ | ||
20 | |||
21 | #include <linux/stddef.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/signal.h> | ||
25 | #include <linux/stddef.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/irq.h> | ||
28 | |||
29 | #include <asm/io.h> | ||
30 | #include <asm/processor.h> | ||
31 | #include <asm/system.h> | ||
32 | #include <asm/irq.h> | ||
33 | #include <asm/mpc52xx.h> | ||
34 | |||
35 | |||
36 | static struct mpc52xx_intr __iomem *intr; | ||
37 | static struct mpc52xx_sdma __iomem *sdma; | ||
38 | |||
39 | static void | ||
40 | mpc52xx_ic_disable(unsigned int irq) | ||
41 | { | ||
42 | u32 val; | ||
43 | |||
44 | if (irq == MPC52xx_IRQ0) { | ||
45 | val = in_be32(&intr->ctrl); | ||
46 | val &= ~(1 << 11); | ||
47 | out_be32(&intr->ctrl, val); | ||
48 | } | ||
49 | else if (irq < MPC52xx_IRQ1) { | ||
50 | BUG(); | ||
51 | } | ||
52 | else if (irq <= MPC52xx_IRQ3) { | ||
53 | val = in_be32(&intr->ctrl); | ||
54 | val &= ~(1 << (10 - (irq - MPC52xx_IRQ1))); | ||
55 | out_be32(&intr->ctrl, val); | ||
56 | } | ||
57 | else if (irq < MPC52xx_SDMA_IRQ_BASE) { | ||
58 | val = in_be32(&intr->main_mask); | ||
59 | val |= 1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE)); | ||
60 | out_be32(&intr->main_mask, val); | ||
61 | } | ||
62 | else if (irq < MPC52xx_PERP_IRQ_BASE) { | ||
63 | val = in_be32(&sdma->IntMask); | ||
64 | val |= 1 << (irq - MPC52xx_SDMA_IRQ_BASE); | ||
65 | out_be32(&sdma->IntMask, val); | ||
66 | } | ||
67 | else { | ||
68 | val = in_be32(&intr->per_mask); | ||
69 | val |= 1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE)); | ||
70 | out_be32(&intr->per_mask, val); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | static void | ||
75 | mpc52xx_ic_enable(unsigned int irq) | ||
76 | { | ||
77 | u32 val; | ||
78 | |||
79 | if (irq == MPC52xx_IRQ0) { | ||
80 | val = in_be32(&intr->ctrl); | ||
81 | val |= 1 << 11; | ||
82 | out_be32(&intr->ctrl, val); | ||
83 | } | ||
84 | else if (irq < MPC52xx_IRQ1) { | ||
85 | BUG(); | ||
86 | } | ||
87 | else if (irq <= MPC52xx_IRQ3) { | ||
88 | val = in_be32(&intr->ctrl); | ||
89 | val |= 1 << (10 - (irq - MPC52xx_IRQ1)); | ||
90 | out_be32(&intr->ctrl, val); | ||
91 | } | ||
92 | else if (irq < MPC52xx_SDMA_IRQ_BASE) { | ||
93 | val = in_be32(&intr->main_mask); | ||
94 | val &= ~(1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE))); | ||
95 | out_be32(&intr->main_mask, val); | ||
96 | } | ||
97 | else if (irq < MPC52xx_PERP_IRQ_BASE) { | ||
98 | val = in_be32(&sdma->IntMask); | ||
99 | val &= ~(1 << (irq - MPC52xx_SDMA_IRQ_BASE)); | ||
100 | out_be32(&sdma->IntMask, val); | ||
101 | } | ||
102 | else { | ||
103 | val = in_be32(&intr->per_mask); | ||
104 | val &= ~(1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE))); | ||
105 | out_be32(&intr->per_mask, val); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | static void | ||
110 | mpc52xx_ic_ack(unsigned int irq) | ||
111 | { | ||
112 | u32 val; | ||
113 | |||
114 | /* | ||
115 | * Only some irqs are reset here, others in interrupting hardware. | ||
116 | */ | ||
117 | |||
118 | switch (irq) { | ||
119 | case MPC52xx_IRQ0: | ||
120 | val = in_be32(&intr->ctrl); | ||
121 | val |= 0x08000000; | ||
122 | out_be32(&intr->ctrl, val); | ||
123 | break; | ||
124 | case MPC52xx_CCS_IRQ: | ||
125 | val = in_be32(&intr->enc_status); | ||
126 | val |= 0x00000400; | ||
127 | out_be32(&intr->enc_status, val); | ||
128 | break; | ||
129 | case MPC52xx_IRQ1: | ||
130 | val = in_be32(&intr->ctrl); | ||
131 | val |= 0x04000000; | ||
132 | out_be32(&intr->ctrl, val); | ||
133 | break; | ||
134 | case MPC52xx_IRQ2: | ||
135 | val = in_be32(&intr->ctrl); | ||
136 | val |= 0x02000000; | ||
137 | out_be32(&intr->ctrl, val); | ||
138 | break; | ||
139 | case MPC52xx_IRQ3: | ||
140 | val = in_be32(&intr->ctrl); | ||
141 | val |= 0x01000000; | ||
142 | out_be32(&intr->ctrl, val); | ||
143 | break; | ||
144 | default: | ||
145 | if (irq >= MPC52xx_SDMA_IRQ_BASE | ||
146 | && irq < (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)) { | ||
147 | out_be32(&sdma->IntPend, | ||
148 | 1 << (irq - MPC52xx_SDMA_IRQ_BASE)); | ||
149 | } | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | |||
154 | static void | ||
155 | mpc52xx_ic_disable_and_ack(unsigned int irq) | ||
156 | { | ||
157 | mpc52xx_ic_disable(irq); | ||
158 | mpc52xx_ic_ack(irq); | ||
159 | } | ||
160 | |||
161 | static void | ||
162 | mpc52xx_ic_end(unsigned int irq) | ||
163 | { | ||
164 | if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) | ||
165 | mpc52xx_ic_enable(irq); | ||
166 | } | ||
167 | |||
168 | static struct hw_interrupt_type mpc52xx_ic = { | ||
169 | .typename = " MPC52xx ", | ||
170 | .enable = mpc52xx_ic_enable, | ||
171 | .disable = mpc52xx_ic_disable, | ||
172 | .ack = mpc52xx_ic_disable_and_ack, | ||
173 | .end = mpc52xx_ic_end, | ||
174 | }; | ||
175 | |||
176 | void __init | ||
177 | mpc52xx_init_irq(void) | ||
178 | { | ||
179 | int i; | ||
180 | u32 intr_ctrl; | ||
181 | |||
182 | /* Remap the necessary zones */ | ||
183 | intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE); | ||
184 | sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE); | ||
185 | |||
186 | if ((intr==NULL) || (sdma==NULL)) | ||
187 | panic("Can't ioremap PIC/SDMA register for init_irq !"); | ||
188 | |||
189 | /* Disable all interrupt sources. */ | ||
190 | out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */ | ||
191 | out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */ | ||
192 | out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */ | ||
193 | out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */ | ||
194 | intr_ctrl = in_be32(&intr->ctrl); | ||
195 | intr_ctrl &= 0x00ff0000; /* Keeps IRQ[0-3] config */ | ||
196 | intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */ | ||
197 | 0x00001000 | /* MEE master external enable */ | ||
198 | 0x00000000 | /* 0 means disable IRQ 0-3 */ | ||
199 | 0x00000001; /* CEb route critical normally */ | ||
200 | out_be32(&intr->ctrl, intr_ctrl); | ||
201 | |||
202 | /* Zero a bunch of the priority settings. */ | ||
203 | out_be32(&intr->per_pri1, 0); | ||
204 | out_be32(&intr->per_pri2, 0); | ||
205 | out_be32(&intr->per_pri3, 0); | ||
206 | out_be32(&intr->main_pri1, 0); | ||
207 | out_be32(&intr->main_pri2, 0); | ||
208 | |||
209 | /* Initialize irq_desc[i].handler's with mpc52xx_ic. */ | ||
210 | for (i = 0; i < NR_IRQS; i++) { | ||
211 | irq_desc[i].handler = &mpc52xx_ic; | ||
212 | irq_desc[i].status = IRQ_LEVEL; | ||
213 | } | ||
214 | |||
215 | #define IRQn_MODE(intr_ctrl,irq) (((intr_ctrl) >> (22-(i<<1))) & 0x03) | ||
216 | for (i=0 ; i<4 ; i++) { | ||
217 | int mode; | ||
218 | mode = IRQn_MODE(intr_ctrl,i); | ||
219 | if ((mode == 0x1) || (mode == 0x2)) | ||
220 | irq_desc[i?MPC52xx_IRQ1+i-1:MPC52xx_IRQ0].status = 0; | ||
221 | } | ||
222 | } | ||
223 | |||
224 | int | ||
225 | mpc52xx_get_irq(struct pt_regs *regs) | ||
226 | { | ||
227 | u32 status; | ||
228 | int irq = -1; | ||
229 | |||
230 | status = in_be32(&intr->enc_status); | ||
231 | |||
232 | if (status & 0x00000400) { /* critical */ | ||
233 | irq = (status >> 8) & 0x3; | ||
234 | if (irq == 2) /* high priority peripheral */ | ||
235 | goto peripheral; | ||
236 | irq += MPC52xx_CRIT_IRQ_BASE; | ||
237 | } | ||
238 | else if (status & 0x00200000) { /* main */ | ||
239 | irq = (status >> 16) & 0x1f; | ||
240 | if (irq == 4) /* low priority peripheral */ | ||
241 | goto peripheral; | ||
242 | irq += MPC52xx_MAIN_IRQ_BASE; | ||
243 | } | ||
244 | else if (status & 0x20000000) { /* peripheral */ | ||
245 | peripheral: | ||
246 | irq = (status >> 24) & 0x1f; | ||
247 | if (irq == 0) { /* bestcomm */ | ||
248 | status = in_be32(&sdma->IntPend); | ||
249 | irq = ffs(status) + MPC52xx_SDMA_IRQ_BASE-1; | ||
250 | } | ||
251 | else | ||
252 | irq += MPC52xx_PERP_IRQ_BASE; | ||
253 | } | ||
254 | |||
255 | return irq; | ||
256 | } | ||
257 | |||
diff --git a/arch/ppc/syslib/mpc52xx_setup.c b/arch/ppc/syslib/mpc52xx_setup.c new file mode 100644 index 000000000000..bb2374585a7b --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_setup.c | |||
@@ -0,0 +1,230 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mpc52xx_setup.c | ||
3 | * | ||
4 | * Common code for the boards based on Freescale MPC52xx embedded CPU. | ||
5 | * | ||
6 | * | ||
7 | * Maintainer : Sylvain Munaut <tnt@246tNt.com> | ||
8 | * | ||
9 | * Support for other bootloaders than UBoot by Dale Farnsworth | ||
10 | * <dfarnsworth@mvista.com> | ||
11 | * | ||
12 | * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com> | ||
13 | * Copyright (C) 2003 Montavista Software, Inc | ||
14 | * | ||
15 | * This file is licensed under the terms of the GNU General Public License | ||
16 | * version 2. This program is licensed "as is" without any warranty of any | ||
17 | * kind, whether express or implied. | ||
18 | */ | ||
19 | |||
20 | #include <linux/config.h> | ||
21 | |||
22 | #include <asm/io.h> | ||
23 | #include <asm/time.h> | ||
24 | #include <asm/mpc52xx.h> | ||
25 | #include <asm/mpc52xx_psc.h> | ||
26 | #include <asm/pgtable.h> | ||
27 | #include <asm/ppcboot.h> | ||
28 | |||
29 | extern bd_t __res; | ||
30 | |||
31 | static int core_mult[] = { /* CPU Frequency multiplier, taken */ | ||
32 | 0, 0, 0, 10, 20, 20, 25, 45, /* from the datasheet used to compute */ | ||
33 | 30, 55, 40, 50, 0, 60, 35, 0, /* CPU frequency from XLB freq and */ | ||
34 | 30, 25, 65, 10, 70, 20, 75, 45, /* external jumper config */ | ||
35 | 0, 55, 40, 50, 80, 60, 35, 0 | ||
36 | }; | ||
37 | |||
38 | void | ||
39 | mpc52xx_restart(char *cmd) | ||
40 | { | ||
41 | struct mpc52xx_gpt __iomem *gpt0 = MPC52xx_VA(MPC52xx_GPTx_OFFSET(0)); | ||
42 | |||
43 | local_irq_disable(); | ||
44 | |||
45 | /* Turn on the watchdog and wait for it to expire. It effectively | ||
46 | does a reset */ | ||
47 | out_be32(&gpt0->count, 0x000000ff); | ||
48 | out_be32(&gpt0->mode, 0x00009004); | ||
49 | |||
50 | while (1); | ||
51 | } | ||
52 | |||
53 | void | ||
54 | mpc52xx_halt(void) | ||
55 | { | ||
56 | local_irq_disable(); | ||
57 | |||
58 | while (1); | ||
59 | } | ||
60 | |||
61 | void | ||
62 | mpc52xx_power_off(void) | ||
63 | { | ||
64 | /* By default we don't have any way of shut down. | ||
65 | If a specific board wants to, it can set the power down | ||
66 | code to any hardware implementation dependent code */ | ||
67 | mpc52xx_halt(); | ||
68 | } | ||
69 | |||
70 | |||
71 | void __init | ||
72 | mpc52xx_set_bat(void) | ||
73 | { | ||
74 | /* Set BAT 2 to map the 0xf0000000 area */ | ||
75 | /* This mapping is used during mpc52xx_progress, | ||
76 | * mpc52xx_find_end_of_memory, and UARTs/GPIO access for debug | ||
77 | */ | ||
78 | mb(); | ||
79 | mtspr(SPRN_DBAT2U, 0xf0001ffe); | ||
80 | mtspr(SPRN_DBAT2L, 0xf000002a); | ||
81 | mb(); | ||
82 | } | ||
83 | |||
84 | void __init | ||
85 | mpc52xx_map_io(void) | ||
86 | { | ||
87 | /* Here we only map the MBAR */ | ||
88 | io_block_mapping( | ||
89 | MPC52xx_MBAR_VIRT, MPC52xx_MBAR, MPC52xx_MBAR_SIZE, _PAGE_IO); | ||
90 | } | ||
91 | |||
92 | |||
93 | #ifdef CONFIG_SERIAL_TEXT_DEBUG | ||
94 | #ifndef MPC52xx_PF_CONSOLE_PORT | ||
95 | #error "mpc52xx PSC for console not selected" | ||
96 | #endif | ||
97 | |||
98 | static void | ||
99 | mpc52xx_psc_putc(struct mpc52xx_psc __iomem *psc, unsigned char c) | ||
100 | { | ||
101 | while (!(in_be16(&psc->mpc52xx_psc_status) & | ||
102 | MPC52xx_PSC_SR_TXRDY)); | ||
103 | out_8(&psc->mpc52xx_psc_buffer_8, c); | ||
104 | } | ||
105 | |||
106 | void | ||
107 | mpc52xx_progress(char *s, unsigned short hex) | ||
108 | { | ||
109 | char c; | ||
110 | struct mpc52xx_psc __iomem *psc; | ||
111 | |||
112 | psc = MPC52xx_VA(MPC52xx_PSCx_OFFSET(MPC52xx_PF_CONSOLE_PORT)); | ||
113 | |||
114 | while ((c = *s++) != 0) { | ||
115 | if (c == '\n') | ||
116 | mpc52xx_psc_putc(psc, '\r'); | ||
117 | mpc52xx_psc_putc(psc, c); | ||
118 | } | ||
119 | |||
120 | mpc52xx_psc_putc(psc, '\r'); | ||
121 | mpc52xx_psc_putc(psc, '\n'); | ||
122 | } | ||
123 | |||
124 | #endif /* CONFIG_SERIAL_TEXT_DEBUG */ | ||
125 | |||
126 | |||
127 | unsigned long __init | ||
128 | mpc52xx_find_end_of_memory(void) | ||
129 | { | ||
130 | u32 ramsize = __res.bi_memsize; | ||
131 | |||
132 | /* | ||
133 | * if bootloader passed a memsize, just use it | ||
134 | * else get size from sdram config registers | ||
135 | */ | ||
136 | if (ramsize == 0) { | ||
137 | struct mpc52xx_mmap_ctl __iomem *mmap_ctl; | ||
138 | u32 sdram_config_0, sdram_config_1; | ||
139 | |||
140 | /* Temp BAT2 mapping active when this is called ! */ | ||
141 | mmap_ctl = MPC52xx_VA(MPC52xx_MMAP_CTL_OFFSET); | ||
142 | |||
143 | sdram_config_0 = in_be32(&mmap_ctl->sdram0); | ||
144 | sdram_config_1 = in_be32(&mmap_ctl->sdram1); | ||
145 | |||
146 | if ((sdram_config_0 & 0x1f) >= 0x13) | ||
147 | ramsize = 1 << ((sdram_config_0 & 0xf) + 17); | ||
148 | |||
149 | if (((sdram_config_1 & 0x1f) >= 0x13) && | ||
150 | ((sdram_config_1 & 0xfff00000) == ramsize)) | ||
151 | ramsize += 1 << ((sdram_config_1 & 0xf) + 17); | ||
152 | } | ||
153 | |||
154 | return ramsize; | ||
155 | } | ||
156 | |||
157 | void __init | ||
158 | mpc52xx_calibrate_decr(void) | ||
159 | { | ||
160 | int current_time, previous_time; | ||
161 | int tbl_start, tbl_end; | ||
162 | unsigned int xlbfreq, cpufreq, ipbfreq, pcifreq, divisor; | ||
163 | |||
164 | xlbfreq = __res.bi_busfreq; | ||
165 | /* if bootloader didn't pass bus frequencies, calculate them */ | ||
166 | if (xlbfreq == 0) { | ||
167 | /* Get RTC & Clock manager modules */ | ||
168 | struct mpc52xx_rtc __iomem *rtc; | ||
169 | struct mpc52xx_cdm __iomem *cdm; | ||
170 | |||
171 | rtc = ioremap(MPC52xx_PA(MPC52xx_RTC_OFFSET), MPC52xx_RTC_SIZE); | ||
172 | cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE); | ||
173 | |||
174 | if ((rtc==NULL) || (cdm==NULL)) | ||
175 | panic("Can't ioremap RTC/CDM while computing bus freq"); | ||
176 | |||
177 | /* Count bus clock during 1/64 sec */ | ||
178 | out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */ | ||
179 | previous_time = in_be32(&rtc->time); | ||
180 | while ((current_time = in_be32(&rtc->time)) == previous_time) ; | ||
181 | tbl_start = get_tbl(); | ||
182 | previous_time = current_time; | ||
183 | while ((current_time = in_be32(&rtc->time)) == previous_time) ; | ||
184 | tbl_end = get_tbl(); | ||
185 | out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */ | ||
186 | |||
187 | /* Compute all frequency from that & CDM settings */ | ||
188 | xlbfreq = (tbl_end - tbl_start) << 8; | ||
189 | cpufreq = (xlbfreq * core_mult[in_be32(&cdm->rstcfg)&0x1f])/10; | ||
190 | ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ? | ||
191 | xlbfreq / 2 : xlbfreq; | ||
192 | switch (in_8(&cdm->pci_clk_sel) & 3) { | ||
193 | case 0: | ||
194 | pcifreq = ipbfreq; | ||
195 | break; | ||
196 | case 1: | ||
197 | pcifreq = ipbfreq / 2; | ||
198 | break; | ||
199 | default: | ||
200 | pcifreq = xlbfreq / 4; | ||
201 | break; | ||
202 | } | ||
203 | __res.bi_busfreq = xlbfreq; | ||
204 | __res.bi_intfreq = cpufreq; | ||
205 | __res.bi_ipbfreq = ipbfreq; | ||
206 | __res.bi_pcifreq = pcifreq; | ||
207 | |||
208 | /* Release mapping */ | ||
209 | iounmap(rtc); | ||
210 | iounmap(cdm); | ||
211 | } | ||
212 | |||
213 | divisor = 4; | ||
214 | |||
215 | tb_ticks_per_jiffy = xlbfreq / HZ / divisor; | ||
216 | tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000); | ||
217 | } | ||
218 | |||
219 | int mpc52xx_match_psc_function(int psc_idx, const char *func) | ||
220 | { | ||
221 | struct mpc52xx_psc_func *cf = mpc52xx_psc_functions; | ||
222 | |||
223 | while ((cf->id != -1) && (cf->func != NULL)) { | ||
224 | if ((cf->id == psc_idx) && !strcmp(cf->func,func)) | ||
225 | return 1; | ||
226 | cf++; | ||
227 | } | ||
228 | |||
229 | return 0; | ||
230 | } | ||
diff --git a/arch/ppc/syslib/mpc52xx_sys.c b/arch/ppc/syslib/mpc52xx_sys.c new file mode 100644 index 000000000000..9a0f90aa8aac --- /dev/null +++ b/arch/ppc/syslib/mpc52xx_sys.c | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mpc52xx_sys.c | ||
3 | * | ||
4 | * Freescale MPC52xx system descriptions | ||
5 | * | ||
6 | * | ||
7 | * Maintainer : Sylvain Munaut <tnt@246tNt.com> | ||
8 | * | ||
9 | * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com> | ||
10 | * | ||
11 | * This file is licensed under the terms of the GNU General Public License | ||
12 | * version 2. This program is licensed "as is" without any warranty of any | ||
13 | * kind, whether express or implied. | ||
14 | */ | ||
15 | |||
16 | #include <asm/ppc_sys.h> | ||
17 | |||
18 | struct ppc_sys_spec *cur_ppc_sys_spec; | ||
19 | struct ppc_sys_spec ppc_sys_specs[] = { | ||
20 | { | ||
21 | .ppc_sys_name = "5200", | ||
22 | .mask = 0xffff0000, | ||
23 | .value = 0x80110000, | ||
24 | .num_devices = 15, | ||
25 | .device_list = (enum ppc_sys_devices[]) | ||
26 | { | ||
27 | MPC52xx_MSCAN1, MPC52xx_MSCAN2, MPC52xx_SPI, | ||
28 | MPC52xx_USB, MPC52xx_BDLC, MPC52xx_PSC1, MPC52xx_PSC2, | ||
29 | MPC52xx_PSC3, MPC52xx_PSC4, MPC52xx_PSC5, MPC52xx_PSC6, | ||
30 | MPC52xx_FEC, MPC52xx_ATA, MPC52xx_I2C1, MPC52xx_I2C2, | ||
31 | }, | ||
32 | }, | ||
33 | { /* default match */ | ||
34 | .ppc_sys_name = "", | ||
35 | .mask = 0x00000000, | ||
36 | .value = 0x00000000, | ||
37 | }, | ||
38 | }; | ||
diff --git a/arch/ppc/syslib/mpc83xx_devices.c b/arch/ppc/syslib/mpc83xx_devices.c new file mode 100644 index 000000000000..5c1a919eaabf --- /dev/null +++ b/arch/ppc/syslib/mpc83xx_devices.c | |||
@@ -0,0 +1,237 @@ | |||
1 | /* | ||
2 | * arch/ppc/platforms/83xx/mpc83xx_devices.c | ||
3 | * | ||
4 | * MPC83xx Device descriptions | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2005 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/serial_8250.h> | ||
20 | #include <linux/fsl_devices.h> | ||
21 | #include <asm/mpc83xx.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/ppc_sys.h> | ||
24 | |||
25 | /* We use offsets for IORESOURCE_MEM since we do not know at compile time | ||
26 | * what IMMRBAR is, will get fixed up by mach_mpc83xx_fixup | ||
27 | */ | ||
28 | |||
29 | static struct gianfar_platform_data mpc83xx_tsec1_pdata = { | ||
30 | .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | | ||
31 | FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON | | ||
32 | FSL_GIANFAR_DEV_HAS_MULTI_INTR, | ||
33 | .phy_reg_addr = 0x24000, | ||
34 | }; | ||
35 | |||
36 | static struct gianfar_platform_data mpc83xx_tsec2_pdata = { | ||
37 | .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | | ||
38 | FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON | | ||
39 | FSL_GIANFAR_DEV_HAS_MULTI_INTR, | ||
40 | .phy_reg_addr = 0x24000, | ||
41 | }; | ||
42 | |||
43 | static struct fsl_i2c_platform_data mpc83xx_fsl_i2c1_pdata = { | ||
44 | .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR, | ||
45 | }; | ||
46 | |||
47 | static struct fsl_i2c_platform_data mpc83xx_fsl_i2c2_pdata = { | ||
48 | .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR, | ||
49 | }; | ||
50 | |||
51 | static struct plat_serial8250_port serial_platform_data[] = { | ||
52 | [0] = { | ||
53 | .mapbase = 0x4500, | ||
54 | .irq = MPC83xx_IRQ_UART1, | ||
55 | .iotype = UPIO_MEM, | ||
56 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | ||
57 | }, | ||
58 | [1] = { | ||
59 | .mapbase = 0x4600, | ||
60 | .irq = MPC83xx_IRQ_UART2, | ||
61 | .iotype = UPIO_MEM, | ||
62 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, | ||
63 | }, | ||
64 | }; | ||
65 | |||
66 | struct platform_device ppc_sys_platform_devices[] = { | ||
67 | [MPC83xx_TSEC1] = { | ||
68 | .name = "fsl-gianfar", | ||
69 | .id = 1, | ||
70 | .dev.platform_data = &mpc83xx_tsec1_pdata, | ||
71 | .num_resources = 4, | ||
72 | .resource = (struct resource[]) { | ||
73 | { | ||
74 | .start = 0x24000, | ||
75 | .end = 0x24fff, | ||
76 | .flags = IORESOURCE_MEM, | ||
77 | }, | ||
78 | { | ||
79 | .name = "tx", | ||
80 | .start = MPC83xx_IRQ_TSEC1_TX, | ||
81 | .end = MPC83xx_IRQ_TSEC1_TX, | ||
82 | .flags = IORESOURCE_IRQ, | ||
83 | }, | ||
84 | { | ||
85 | .name = "rx", | ||
86 | .start = MPC83xx_IRQ_TSEC1_RX, | ||
87 | .end = MPC83xx_IRQ_TSEC1_RX, | ||
88 | .flags = IORESOURCE_IRQ, | ||
89 | }, | ||
90 | { | ||
91 | .name = "error", | ||
92 | .start = MPC83xx_IRQ_TSEC1_ERROR, | ||
93 | .end = MPC83xx_IRQ_TSEC1_ERROR, | ||
94 | .flags = IORESOURCE_IRQ, | ||
95 | }, | ||
96 | }, | ||
97 | }, | ||
98 | [MPC83xx_TSEC2] = { | ||
99 | .name = "fsl-gianfar", | ||
100 | .id = 2, | ||
101 | .dev.platform_data = &mpc83xx_tsec2_pdata, | ||
102 | .num_resources = 4, | ||
103 | .resource = (struct resource[]) { | ||
104 | { | ||
105 | .start = 0x25000, | ||
106 | .end = 0x25fff, | ||
107 | .flags = IORESOURCE_MEM, | ||
108 | }, | ||
109 | { | ||
110 | .name = "tx", | ||
111 | .start = MPC83xx_IRQ_TSEC2_TX, | ||
112 | .end = MPC83xx_IRQ_TSEC2_TX, | ||
113 | .flags = IORESOURCE_IRQ, | ||
114 | }, | ||
115 | { | ||
116 | .name = "rx", | ||
117 | .start = MPC83xx_IRQ_TSEC2_RX, | ||
118 | .end = MPC83xx_IRQ_TSEC2_RX, | ||
119 | .flags = IORESOURCE_IRQ, | ||
120 | }, | ||
121 | { | ||
122 | .name = "error", | ||
123 | .start = MPC83xx_IRQ_TSEC2_ERROR, | ||
124 | .end = MPC83xx_IRQ_TSEC2_ERROR, | ||
125 | .flags = IORESOURCE_IRQ, | ||
126 | }, | ||
127 | }, | ||
128 | }, | ||
129 | [MPC83xx_IIC1] = { | ||
130 | .name = "fsl-i2c", | ||
131 | .id = 1, | ||
132 | .dev.platform_data = &mpc83xx_fsl_i2c1_pdata, | ||
133 | .num_resources = 2, | ||
134 | .resource = (struct resource[]) { | ||
135 | { | ||
136 | .start = 0x3000, | ||
137 | .end = 0x30ff, | ||
138 | .flags = IORESOURCE_MEM, | ||
139 | }, | ||
140 | { | ||
141 | .start = MPC83xx_IRQ_IIC1, | ||
142 | .end = MPC83xx_IRQ_IIC1, | ||
143 | .flags = IORESOURCE_IRQ, | ||
144 | }, | ||
145 | }, | ||
146 | }, | ||
147 | [MPC83xx_IIC2] = { | ||
148 | .name = "fsl-i2c", | ||
149 | .id = 2, | ||
150 | .dev.platform_data = &mpc83xx_fsl_i2c2_pdata, | ||
151 | .num_resources = 2, | ||
152 | .resource = (struct resource[]) { | ||
153 | { | ||
154 | .start = 0x3100, | ||
155 | .end = 0x31ff, | ||
156 | .flags = IORESOURCE_MEM, | ||
157 | }, | ||
158 | { | ||
159 | .start = MPC83xx_IRQ_IIC2, | ||
160 | .end = MPC83xx_IRQ_IIC2, | ||
161 | .flags = IORESOURCE_IRQ, | ||
162 | }, | ||
163 | }, | ||
164 | }, | ||
165 | [MPC83xx_DUART] = { | ||
166 | .name = "serial8250", | ||
167 | .id = 0, | ||
168 | .dev.platform_data = serial_platform_data, | ||
169 | }, | ||
170 | [MPC83xx_SEC2] = { | ||
171 | .name = "fsl-sec2", | ||
172 | .id = 1, | ||
173 | .num_resources = 2, | ||
174 | .resource = (struct resource[]) { | ||
175 | { | ||
176 | .start = 0x30000, | ||
177 | .end = 0x3ffff, | ||
178 | .flags = IORESOURCE_MEM, | ||
179 | }, | ||
180 | { | ||
181 | .start = MPC83xx_IRQ_SEC2, | ||
182 | .end = MPC83xx_IRQ_SEC2, | ||
183 | .flags = IORESOURCE_IRQ, | ||
184 | }, | ||
185 | }, | ||
186 | }, | ||
187 | [MPC83xx_USB2_DR] = { | ||
188 | .name = "fsl-usb2-dr", | ||
189 | .id = 1, | ||
190 | .num_resources = 2, | ||
191 | .resource = (struct resource[]) { | ||
192 | { | ||
193 | .start = 0x22000, | ||
194 | .end = 0x22fff, | ||
195 | .flags = IORESOURCE_MEM, | ||
196 | }, | ||
197 | { | ||
198 | .start = MPC83xx_IRQ_USB2_DR, | ||
199 | .end = MPC83xx_IRQ_USB2_DR, | ||
200 | .flags = IORESOURCE_IRQ, | ||
201 | }, | ||
202 | }, | ||
203 | }, | ||
204 | [MPC83xx_USB2_MPH] = { | ||
205 | .name = "fsl-usb2-mph", | ||
206 | .id = 1, | ||
207 | .num_resources = 2, | ||
208 | .resource = (struct resource[]) { | ||
209 | { | ||
210 | .start = 0x23000, | ||
211 | .end = 0x23fff, | ||
212 | .flags = IORESOURCE_MEM, | ||
213 | }, | ||
214 | { | ||
215 | .start = MPC83xx_IRQ_USB2_MPH, | ||
216 | .end = MPC83xx_IRQ_USB2_MPH, | ||
217 | .flags = IORESOURCE_IRQ, | ||
218 | }, | ||
219 | }, | ||
220 | }, | ||
221 | }; | ||
222 | |||
223 | static int __init mach_mpc83xx_fixup(struct platform_device *pdev) | ||
224 | { | ||
225 | ppc_sys_fixup_mem_resource(pdev, immrbar); | ||
226 | return 0; | ||
227 | } | ||
228 | |||
229 | static int __init mach_mpc83xx_init(void) | ||
230 | { | ||
231 | if (ppc_md.progress) | ||
232 | ppc_md.progress("mach_mpc83xx_init:enter", 0); | ||
233 | ppc_sys_device_fixup = mach_mpc83xx_fixup; | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | postcore_initcall(mach_mpc83xx_init); | ||
diff --git a/arch/ppc/syslib/mpc83xx_sys.c b/arch/ppc/syslib/mpc83xx_sys.c new file mode 100644 index 000000000000..29aa63350025 --- /dev/null +++ b/arch/ppc/syslib/mpc83xx_sys.c | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * arch/ppc/platforms/83xx/mpc83xx_sys.c | ||
3 | * | ||
4 | * MPC83xx System descriptions | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2005 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <asm/ppc_sys.h> | ||
20 | |||
21 | struct ppc_sys_spec *cur_ppc_sys_spec; | ||
22 | struct ppc_sys_spec ppc_sys_specs[] = { | ||
23 | { | ||
24 | .ppc_sys_name = "8349E", | ||
25 | .mask = 0xFFFF0000, | ||
26 | .value = 0x80500000, | ||
27 | .num_devices = 8, | ||
28 | .device_list = (enum ppc_sys_devices[]) | ||
29 | { | ||
30 | MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, | ||
31 | MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2, | ||
32 | MPC83xx_USB2_DR, MPC83xx_USB2_MPH | ||
33 | }, | ||
34 | }, | ||
35 | { | ||
36 | .ppc_sys_name = "8349", | ||
37 | .mask = 0xFFFF0000, | ||
38 | .value = 0x80510000, | ||
39 | .num_devices = 7, | ||
40 | .device_list = (enum ppc_sys_devices[]) | ||
41 | { | ||
42 | MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, | ||
43 | MPC83xx_IIC2, MPC83xx_DUART, | ||
44 | MPC83xx_USB2_DR, MPC83xx_USB2_MPH | ||
45 | }, | ||
46 | }, | ||
47 | { | ||
48 | .ppc_sys_name = "8347E", | ||
49 | .mask = 0xFFFF0000, | ||
50 | .value = 0x80520000, | ||
51 | .num_devices = 8, | ||
52 | .device_list = (enum ppc_sys_devices[]) | ||
53 | { | ||
54 | MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, | ||
55 | MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2, | ||
56 | MPC83xx_USB2_DR, MPC83xx_USB2_MPH | ||
57 | }, | ||
58 | }, | ||
59 | { | ||
60 | .ppc_sys_name = "8347", | ||
61 | .mask = 0xFFFF0000, | ||
62 | .value = 0x80530000, | ||
63 | .num_devices = 7, | ||
64 | .device_list = (enum ppc_sys_devices[]) | ||
65 | { | ||
66 | MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, | ||
67 | MPC83xx_IIC2, MPC83xx_DUART, | ||
68 | MPC83xx_USB2_DR, MPC83xx_USB2_MPH | ||
69 | }, | ||
70 | }, | ||
71 | { | ||
72 | .ppc_sys_name = "8343E", | ||
73 | .mask = 0xFFFF0000, | ||
74 | .value = 0x80540000, | ||
75 | .num_devices = 7, | ||
76 | .device_list = (enum ppc_sys_devices[]) | ||
77 | { | ||
78 | MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, | ||
79 | MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2, | ||
80 | MPC83xx_USB2_DR, | ||
81 | }, | ||
82 | }, | ||
83 | { | ||
84 | .ppc_sys_name = "8343", | ||
85 | .mask = 0xFFFF0000, | ||
86 | .value = 0x80550000, | ||
87 | .num_devices = 6, | ||
88 | .device_list = (enum ppc_sys_devices[]) | ||
89 | { | ||
90 | MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1, | ||
91 | MPC83xx_IIC2, MPC83xx_DUART, | ||
92 | MPC83xx_USB2_DR, | ||
93 | }, | ||
94 | }, | ||
95 | { /* default match */ | ||
96 | .ppc_sys_name = "", | ||
97 | .mask = 0x00000000, | ||
98 | .value = 0x00000000, | ||
99 | }, | ||
100 | }; | ||
diff --git a/arch/ppc/syslib/mpc85xx_devices.c b/arch/ppc/syslib/mpc85xx_devices.c new file mode 100644 index 000000000000..a231795ee26f --- /dev/null +++ b/arch/ppc/syslib/mpc85xx_devices.c | |||
@@ -0,0 +1,552 @@ | |||
1 | /* | ||
2 | * arch/ppc/platforms/85xx/mpc85xx_devices.c | ||
3 | * | ||
4 | * MPC85xx Device descriptions | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2005 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/serial_8250.h> | ||
20 | #include <linux/fsl_devices.h> | ||
21 | #include <asm/mpc85xx.h> | ||
22 | #include <asm/irq.h> | ||
23 | #include <asm/ppc_sys.h> | ||
24 | |||
25 | /* We use offsets for IORESOURCE_MEM since we do not know at compile time | ||
26 | * what CCSRBAR is, will get fixed up by mach_mpc85xx_fixup | ||
27 | */ | ||
28 | |||
29 | static struct gianfar_platform_data mpc85xx_tsec1_pdata = { | ||
30 | .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | | ||
31 | FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON | | ||
32 | FSL_GIANFAR_DEV_HAS_MULTI_INTR, | ||
33 | .phy_reg_addr = MPC85xx_ENET1_OFFSET, | ||
34 | }; | ||
35 | |||
36 | static struct gianfar_platform_data mpc85xx_tsec2_pdata = { | ||
37 | .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT | | ||
38 | FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON | | ||
39 | FSL_GIANFAR_DEV_HAS_MULTI_INTR, | ||
40 | .phy_reg_addr = MPC85xx_ENET1_OFFSET, | ||
41 | }; | ||
42 | |||
43 | static struct gianfar_platform_data mpc85xx_fec_pdata = { | ||
44 | .phy_reg_addr = MPC85xx_ENET1_OFFSET, | ||
45 | }; | ||
46 | |||
47 | static struct fsl_i2c_platform_data mpc85xx_fsl_i2c_pdata = { | ||
48 | .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR, | ||
49 | }; | ||
50 | |||
51 | static struct plat_serial8250_port serial_platform_data[] = { | ||
52 | [0] = { | ||
53 | .mapbase = 0x4500, | ||
54 | .irq = MPC85xx_IRQ_DUART, | ||
55 | .iotype = UPIO_MEM, | ||
56 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ, | ||
57 | }, | ||
58 | [1] = { | ||
59 | .mapbase = 0x4600, | ||
60 | .irq = MPC85xx_IRQ_DUART, | ||
61 | .iotype = UPIO_MEM, | ||
62 | .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ, | ||
63 | }, | ||
64 | }; | ||
65 | |||
66 | struct platform_device ppc_sys_platform_devices[] = { | ||
67 | [MPC85xx_TSEC1] = { | ||
68 | .name = "fsl-gianfar", | ||
69 | .id = 1, | ||
70 | .dev.platform_data = &mpc85xx_tsec1_pdata, | ||
71 | .num_resources = 4, | ||
72 | .resource = (struct resource[]) { | ||
73 | { | ||
74 | .start = MPC85xx_ENET1_OFFSET, | ||
75 | .end = MPC85xx_ENET1_OFFSET + | ||
76 | MPC85xx_ENET1_SIZE - 1, | ||
77 | .flags = IORESOURCE_MEM, | ||
78 | }, | ||
79 | { | ||
80 | .name = "tx", | ||
81 | .start = MPC85xx_IRQ_TSEC1_TX, | ||
82 | .end = MPC85xx_IRQ_TSEC1_TX, | ||
83 | .flags = IORESOURCE_IRQ, | ||
84 | }, | ||
85 | { | ||
86 | .name = "rx", | ||
87 | .start = MPC85xx_IRQ_TSEC1_RX, | ||
88 | .end = MPC85xx_IRQ_TSEC1_RX, | ||
89 | .flags = IORESOURCE_IRQ, | ||
90 | }, | ||
91 | { | ||
92 | .name = "error", | ||
93 | .start = MPC85xx_IRQ_TSEC1_ERROR, | ||
94 | .end = MPC85xx_IRQ_TSEC1_ERROR, | ||
95 | .flags = IORESOURCE_IRQ, | ||
96 | }, | ||
97 | }, | ||
98 | }, | ||
99 | [MPC85xx_TSEC2] = { | ||
100 | .name = "fsl-gianfar", | ||
101 | .id = 2, | ||
102 | .dev.platform_data = &mpc85xx_tsec2_pdata, | ||
103 | .num_resources = 4, | ||
104 | .resource = (struct resource[]) { | ||
105 | { | ||
106 | .start = MPC85xx_ENET2_OFFSET, | ||
107 | .end = MPC85xx_ENET2_OFFSET + | ||
108 | MPC85xx_ENET2_SIZE - 1, | ||
109 | .flags = IORESOURCE_MEM, | ||
110 | }, | ||
111 | { | ||
112 | .name = "tx", | ||
113 | .start = MPC85xx_IRQ_TSEC2_TX, | ||
114 | .end = MPC85xx_IRQ_TSEC2_TX, | ||
115 | .flags = IORESOURCE_IRQ, | ||
116 | }, | ||
117 | { | ||
118 | .name = "rx", | ||
119 | .start = MPC85xx_IRQ_TSEC2_RX, | ||
120 | .end = MPC85xx_IRQ_TSEC2_RX, | ||
121 | .flags = IORESOURCE_IRQ, | ||
122 | }, | ||
123 | { | ||
124 | .name = "error", | ||
125 | .start = MPC85xx_IRQ_TSEC2_ERROR, | ||
126 | .end = MPC85xx_IRQ_TSEC2_ERROR, | ||
127 | .flags = IORESOURCE_IRQ, | ||
128 | }, | ||
129 | }, | ||
130 | }, | ||
131 | [MPC85xx_FEC] = { | ||
132 | .name = "fsl-gianfar", | ||
133 | .id = 3, | ||
134 | .dev.platform_data = &mpc85xx_fec_pdata, | ||
135 | .num_resources = 2, | ||
136 | .resource = (struct resource[]) { | ||
137 | { | ||
138 | .start = MPC85xx_ENET3_OFFSET, | ||
139 | .end = MPC85xx_ENET3_OFFSET + | ||
140 | MPC85xx_ENET3_SIZE - 1, | ||
141 | .flags = IORESOURCE_MEM, | ||
142 | |||
143 | }, | ||
144 | { | ||
145 | .start = MPC85xx_IRQ_FEC, | ||
146 | .end = MPC85xx_IRQ_FEC, | ||
147 | .flags = IORESOURCE_IRQ, | ||
148 | }, | ||
149 | }, | ||
150 | }, | ||
151 | [MPC85xx_IIC1] = { | ||
152 | .name = "fsl-i2c", | ||
153 | .id = 1, | ||
154 | .dev.platform_data = &mpc85xx_fsl_i2c_pdata, | ||
155 | .num_resources = 2, | ||
156 | .resource = (struct resource[]) { | ||
157 | { | ||
158 | .start = MPC85xx_IIC1_OFFSET, | ||
159 | .end = MPC85xx_IIC1_OFFSET + | ||
160 | MPC85xx_IIC1_SIZE - 1, | ||
161 | .flags = IORESOURCE_MEM, | ||
162 | }, | ||
163 | { | ||
164 | .start = MPC85xx_IRQ_IIC1, | ||
165 | .end = MPC85xx_IRQ_IIC1, | ||
166 | .flags = IORESOURCE_IRQ, | ||
167 | }, | ||
168 | }, | ||
169 | }, | ||
170 | [MPC85xx_DMA0] = { | ||
171 | .name = "fsl-dma", | ||
172 | .id = 0, | ||
173 | .num_resources = 2, | ||
174 | .resource = (struct resource[]) { | ||
175 | { | ||
176 | .start = MPC85xx_DMA0_OFFSET, | ||
177 | .end = MPC85xx_DMA0_OFFSET + | ||
178 | MPC85xx_DMA0_SIZE - 1, | ||
179 | .flags = IORESOURCE_MEM, | ||
180 | }, | ||
181 | { | ||
182 | .start = MPC85xx_IRQ_DMA0, | ||
183 | .end = MPC85xx_IRQ_DMA0, | ||
184 | .flags = IORESOURCE_IRQ, | ||
185 | }, | ||
186 | }, | ||
187 | }, | ||
188 | [MPC85xx_DMA1] = { | ||
189 | .name = "fsl-dma", | ||
190 | .id = 1, | ||
191 | .num_resources = 2, | ||
192 | .resource = (struct resource[]) { | ||
193 | { | ||
194 | .start = MPC85xx_DMA1_OFFSET, | ||
195 | .end = MPC85xx_DMA1_OFFSET + | ||
196 | MPC85xx_DMA1_SIZE - 1, | ||
197 | .flags = IORESOURCE_MEM, | ||
198 | }, | ||
199 | { | ||
200 | .start = MPC85xx_IRQ_DMA1, | ||
201 | .end = MPC85xx_IRQ_DMA1, | ||
202 | .flags = IORESOURCE_IRQ, | ||
203 | }, | ||
204 | }, | ||
205 | }, | ||
206 | [MPC85xx_DMA2] = { | ||
207 | .name = "fsl-dma", | ||
208 | .id = 2, | ||
209 | .num_resources = 2, | ||
210 | .resource = (struct resource[]) { | ||
211 | { | ||
212 | .start = MPC85xx_DMA2_OFFSET, | ||
213 | .end = MPC85xx_DMA2_OFFSET + | ||
214 | MPC85xx_DMA2_SIZE - 1, | ||
215 | .flags = IORESOURCE_MEM, | ||
216 | }, | ||
217 | { | ||
218 | .start = MPC85xx_IRQ_DMA2, | ||
219 | .end = MPC85xx_IRQ_DMA2, | ||
220 | .flags = IORESOURCE_IRQ, | ||
221 | }, | ||
222 | }, | ||
223 | }, | ||
224 | [MPC85xx_DMA3] = { | ||
225 | .name = "fsl-dma", | ||
226 | .id = 3, | ||
227 | .num_resources = 2, | ||
228 | .resource = (struct resource[]) { | ||
229 | { | ||
230 | .start = MPC85xx_DMA3_OFFSET, | ||
231 | .end = MPC85xx_DMA3_OFFSET + | ||
232 | MPC85xx_DMA3_SIZE - 1, | ||
233 | .flags = IORESOURCE_MEM, | ||
234 | }, | ||
235 | { | ||
236 | .start = MPC85xx_IRQ_DMA3, | ||
237 | .end = MPC85xx_IRQ_DMA3, | ||
238 | .flags = IORESOURCE_IRQ, | ||
239 | }, | ||
240 | }, | ||
241 | }, | ||
242 | [MPC85xx_DUART] = { | ||
243 | .name = "serial8250", | ||
244 | .id = 0, | ||
245 | .dev.platform_data = serial_platform_data, | ||
246 | }, | ||
247 | [MPC85xx_PERFMON] = { | ||
248 | .name = "fsl-perfmon", | ||
249 | .id = 1, | ||
250 | .num_resources = 2, | ||
251 | .resource = (struct resource[]) { | ||
252 | { | ||
253 | .start = MPC85xx_PERFMON_OFFSET, | ||
254 | .end = MPC85xx_PERFMON_OFFSET + | ||
255 | MPC85xx_PERFMON_SIZE - 1, | ||
256 | .flags = IORESOURCE_MEM, | ||
257 | }, | ||
258 | { | ||
259 | .start = MPC85xx_IRQ_PERFMON, | ||
260 | .end = MPC85xx_IRQ_PERFMON, | ||
261 | .flags = IORESOURCE_IRQ, | ||
262 | }, | ||
263 | }, | ||
264 | }, | ||
265 | [MPC85xx_SEC2] = { | ||
266 | .name = "fsl-sec2", | ||
267 | .id = 1, | ||
268 | .num_resources = 2, | ||
269 | .resource = (struct resource[]) { | ||
270 | { | ||
271 | .start = MPC85xx_SEC2_OFFSET, | ||
272 | .end = MPC85xx_SEC2_OFFSET + | ||
273 | MPC85xx_SEC2_SIZE - 1, | ||
274 | .flags = IORESOURCE_MEM, | ||
275 | }, | ||
276 | { | ||
277 | .start = MPC85xx_IRQ_SEC2, | ||
278 | .end = MPC85xx_IRQ_SEC2, | ||
279 | .flags = IORESOURCE_IRQ, | ||
280 | }, | ||
281 | }, | ||
282 | }, | ||
283 | #ifdef CONFIG_CPM2 | ||
284 | [MPC85xx_CPM_FCC1] = { | ||
285 | .name = "fsl-cpm-fcc", | ||
286 | .id = 1, | ||
287 | .num_resources = 3, | ||
288 | .resource = (struct resource[]) { | ||
289 | { | ||
290 | .start = 0x91300, | ||
291 | .end = 0x9131F, | ||
292 | .flags = IORESOURCE_MEM, | ||
293 | }, | ||
294 | { | ||
295 | .start = 0x91380, | ||
296 | .end = 0x9139F, | ||
297 | .flags = IORESOURCE_MEM, | ||
298 | }, | ||
299 | { | ||
300 | .start = SIU_INT_FCC1, | ||
301 | .end = SIU_INT_FCC1, | ||
302 | .flags = IORESOURCE_IRQ, | ||
303 | }, | ||
304 | }, | ||
305 | }, | ||
306 | [MPC85xx_CPM_FCC2] = { | ||
307 | .name = "fsl-cpm-fcc", | ||
308 | .id = 2, | ||
309 | .num_resources = 3, | ||
310 | .resource = (struct resource[]) { | ||
311 | { | ||
312 | .start = 0x91320, | ||
313 | .end = 0x9133F, | ||
314 | .flags = IORESOURCE_MEM, | ||
315 | }, | ||
316 | { | ||
317 | .start = 0x913A0, | ||
318 | .end = 0x913CF, | ||
319 | .flags = IORESOURCE_MEM, | ||
320 | }, | ||
321 | { | ||
322 | .start = SIU_INT_FCC2, | ||
323 | .end = SIU_INT_FCC2, | ||
324 | .flags = IORESOURCE_IRQ, | ||
325 | }, | ||
326 | }, | ||
327 | }, | ||
328 | [MPC85xx_CPM_FCC3] = { | ||
329 | .name = "fsl-cpm-fcc", | ||
330 | .id = 3, | ||
331 | .num_resources = 3, | ||
332 | .resource = (struct resource[]) { | ||
333 | { | ||
334 | .start = 0x91340, | ||
335 | .end = 0x9135F, | ||
336 | .flags = IORESOURCE_MEM, | ||
337 | }, | ||
338 | { | ||
339 | .start = 0x913D0, | ||
340 | .end = 0x913FF, | ||
341 | .flags = IORESOURCE_MEM, | ||
342 | }, | ||
343 | { | ||
344 | .start = SIU_INT_FCC3, | ||
345 | .end = SIU_INT_FCC3, | ||
346 | .flags = IORESOURCE_IRQ, | ||
347 | }, | ||
348 | }, | ||
349 | }, | ||
350 | [MPC85xx_CPM_I2C] = { | ||
351 | .name = "fsl-cpm-i2c", | ||
352 | .id = 1, | ||
353 | .num_resources = 2, | ||
354 | .resource = (struct resource[]) { | ||
355 | { | ||
356 | .start = 0x91860, | ||
357 | .end = 0x918BF, | ||
358 | .flags = IORESOURCE_MEM, | ||
359 | }, | ||
360 | { | ||
361 | .start = SIU_INT_I2C, | ||
362 | .end = SIU_INT_I2C, | ||
363 | .flags = IORESOURCE_IRQ, | ||
364 | }, | ||
365 | }, | ||
366 | }, | ||
367 | [MPC85xx_CPM_SCC1] = { | ||
368 | .name = "fsl-cpm-scc", | ||
369 | .id = 1, | ||
370 | .num_resources = 2, | ||
371 | .resource = (struct resource[]) { | ||
372 | { | ||
373 | .start = 0x91A00, | ||
374 | .end = 0x91A1F, | ||
375 | .flags = IORESOURCE_MEM, | ||
376 | }, | ||
377 | { | ||
378 | .start = SIU_INT_SCC1, | ||
379 | .end = SIU_INT_SCC1, | ||
380 | .flags = IORESOURCE_IRQ, | ||
381 | }, | ||
382 | }, | ||
383 | }, | ||
384 | [MPC85xx_CPM_SCC2] = { | ||
385 | .name = "fsl-cpm-scc", | ||
386 | .id = 2, | ||
387 | .num_resources = 2, | ||
388 | .resource = (struct resource[]) { | ||
389 | { | ||
390 | .start = 0x91A20, | ||
391 | .end = 0x91A3F, | ||
392 | .flags = IORESOURCE_MEM, | ||
393 | }, | ||
394 | { | ||
395 | .start = SIU_INT_SCC2, | ||
396 | .end = SIU_INT_SCC2, | ||
397 | .flags = IORESOURCE_IRQ, | ||
398 | }, | ||
399 | }, | ||
400 | }, | ||
401 | [MPC85xx_CPM_SCC3] = { | ||
402 | .name = "fsl-cpm-scc", | ||
403 | .id = 3, | ||
404 | .num_resources = 2, | ||
405 | .resource = (struct resource[]) { | ||
406 | { | ||
407 | .start = 0x91A40, | ||
408 | .end = 0x91A5F, | ||
409 | .flags = IORESOURCE_MEM, | ||
410 | }, | ||
411 | { | ||
412 | .start = SIU_INT_SCC3, | ||
413 | .end = SIU_INT_SCC3, | ||
414 | .flags = IORESOURCE_IRQ, | ||
415 | }, | ||
416 | }, | ||
417 | }, | ||
418 | [MPC85xx_CPM_SCC4] = { | ||
419 | .name = "fsl-cpm-scc", | ||
420 | .id = 4, | ||
421 | .num_resources = 2, | ||
422 | .resource = (struct resource[]) { | ||
423 | { | ||
424 | .start = 0x91A60, | ||
425 | .end = 0x91A7F, | ||
426 | .flags = IORESOURCE_MEM, | ||
427 | }, | ||
428 | { | ||
429 | .start = SIU_INT_SCC4, | ||
430 | .end = SIU_INT_SCC4, | ||
431 | .flags = IORESOURCE_IRQ, | ||
432 | }, | ||
433 | }, | ||
434 | }, | ||
435 | [MPC85xx_CPM_SPI] = { | ||
436 | .name = "fsl-cpm-spi", | ||
437 | .id = 1, | ||
438 | .num_resources = 2, | ||
439 | .resource = (struct resource[]) { | ||
440 | { | ||
441 | .start = 0x91AA0, | ||
442 | .end = 0x91AFF, | ||
443 | .flags = IORESOURCE_MEM, | ||
444 | }, | ||
445 | { | ||
446 | .start = SIU_INT_SPI, | ||
447 | .end = SIU_INT_SPI, | ||
448 | .flags = IORESOURCE_IRQ, | ||
449 | }, | ||
450 | }, | ||
451 | }, | ||
452 | [MPC85xx_CPM_MCC1] = { | ||
453 | .name = "fsl-cpm-mcc", | ||
454 | .id = 1, | ||
455 | .num_resources = 2, | ||
456 | .resource = (struct resource[]) { | ||
457 | { | ||
458 | .start = 0x91B30, | ||
459 | .end = 0x91B3F, | ||
460 | .flags = IORESOURCE_MEM, | ||
461 | }, | ||
462 | { | ||
463 | .start = SIU_INT_MCC1, | ||
464 | .end = SIU_INT_MCC1, | ||
465 | .flags = IORESOURCE_IRQ, | ||
466 | }, | ||
467 | }, | ||
468 | }, | ||
469 | [MPC85xx_CPM_MCC2] = { | ||
470 | .name = "fsl-cpm-mcc", | ||
471 | .id = 2, | ||
472 | .num_resources = 2, | ||
473 | .resource = (struct resource[]) { | ||
474 | { | ||
475 | .start = 0x91B50, | ||
476 | .end = 0x91B5F, | ||
477 | .flags = IORESOURCE_MEM, | ||
478 | }, | ||
479 | { | ||
480 | .start = SIU_INT_MCC2, | ||
481 | .end = SIU_INT_MCC2, | ||
482 | .flags = IORESOURCE_IRQ, | ||
483 | }, | ||
484 | }, | ||
485 | }, | ||
486 | [MPC85xx_CPM_SMC1] = { | ||
487 | .name = "fsl-cpm-smc", | ||
488 | .id = 1, | ||
489 | .num_resources = 2, | ||
490 | .resource = (struct resource[]) { | ||
491 | { | ||
492 | .start = 0x91A80, | ||
493 | .end = 0x91A8F, | ||
494 | .flags = IORESOURCE_MEM, | ||
495 | }, | ||
496 | { | ||
497 | .start = SIU_INT_SMC1, | ||
498 | .end = SIU_INT_SMC1, | ||
499 | .flags = IORESOURCE_IRQ, | ||
500 | }, | ||
501 | }, | ||
502 | }, | ||
503 | [MPC85xx_CPM_SMC2] = { | ||
504 | .name = "fsl-cpm-smc", | ||
505 | .id = 2, | ||
506 | .num_resources = 2, | ||
507 | .resource = (struct resource[]) { | ||
508 | { | ||
509 | .start = 0x91A90, | ||
510 | .end = 0x91A9F, | ||
511 | .flags = IORESOURCE_MEM, | ||
512 | }, | ||
513 | { | ||
514 | .start = SIU_INT_SMC2, | ||
515 | .end = SIU_INT_SMC2, | ||
516 | .flags = IORESOURCE_IRQ, | ||
517 | }, | ||
518 | }, | ||
519 | }, | ||
520 | [MPC85xx_CPM_USB] = { | ||
521 | .name = "fsl-cpm-usb", | ||
522 | .id = 2, | ||
523 | .num_resources = 2, | ||
524 | .resource = (struct resource[]) { | ||
525 | { | ||
526 | .start = 0x91B60, | ||
527 | .end = 0x91B7F, | ||
528 | .flags = IORESOURCE_MEM, | ||
529 | }, | ||
530 | { | ||
531 | .start = SIU_INT_USB, | ||
532 | .end = SIU_INT_USB, | ||
533 | .flags = IORESOURCE_IRQ, | ||
534 | }, | ||
535 | }, | ||
536 | }, | ||
537 | #endif /* CONFIG_CPM2 */ | ||
538 | }; | ||
539 | |||
540 | static int __init mach_mpc85xx_fixup(struct platform_device *pdev) | ||
541 | { | ||
542 | ppc_sys_fixup_mem_resource(pdev, CCSRBAR); | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | static int __init mach_mpc85xx_init(void) | ||
547 | { | ||
548 | ppc_sys_device_fixup = mach_mpc85xx_fixup; | ||
549 | return 0; | ||
550 | } | ||
551 | |||
552 | postcore_initcall(mach_mpc85xx_init); | ||
diff --git a/arch/ppc/syslib/mpc85xx_sys.c b/arch/ppc/syslib/mpc85xx_sys.c new file mode 100644 index 000000000000..d806a92a9401 --- /dev/null +++ b/arch/ppc/syslib/mpc85xx_sys.c | |||
@@ -0,0 +1,118 @@ | |||
1 | /* | ||
2 | * arch/ppc/platforms/85xx/mpc85xx_sys.c | ||
3 | * | ||
4 | * MPC85xx System descriptions | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2005 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/init.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <asm/ppc_sys.h> | ||
20 | |||
21 | struct ppc_sys_spec *cur_ppc_sys_spec; | ||
22 | struct ppc_sys_spec ppc_sys_specs[] = { | ||
23 | { | ||
24 | .ppc_sys_name = "8540", | ||
25 | .mask = 0xFFFF0000, | ||
26 | .value = 0x80300000, | ||
27 | .num_devices = 10, | ||
28 | .device_list = (enum ppc_sys_devices[]) | ||
29 | { | ||
30 | MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_FEC, MPC85xx_IIC1, | ||
31 | MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, | ||
32 | MPC85xx_PERFMON, MPC85xx_DUART, | ||
33 | }, | ||
34 | }, | ||
35 | { | ||
36 | .ppc_sys_name = "8560", | ||
37 | .mask = 0xFFFF0000, | ||
38 | .value = 0x80700000, | ||
39 | .num_devices = 19, | ||
40 | .device_list = (enum ppc_sys_devices[]) | ||
41 | { | ||
42 | MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, | ||
43 | MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, | ||
44 | MPC85xx_PERFMON, | ||
45 | MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1, | ||
46 | MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, MPC85xx_CPM_SCC4, | ||
47 | MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, MPC85xx_CPM_FCC3, | ||
48 | MPC85xx_CPM_MCC1, MPC85xx_CPM_MCC2, | ||
49 | }, | ||
50 | }, | ||
51 | { | ||
52 | .ppc_sys_name = "8541", | ||
53 | .mask = 0xFFFF0000, | ||
54 | .value = 0x80720000, | ||
55 | .num_devices = 13, | ||
56 | .device_list = (enum ppc_sys_devices[]) | ||
57 | { | ||
58 | MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, | ||
59 | MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, | ||
60 | MPC85xx_PERFMON, MPC85xx_DUART, | ||
61 | MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, | ||
62 | MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, | ||
63 | }, | ||
64 | }, | ||
65 | { | ||
66 | .ppc_sys_name = "8541E", | ||
67 | .mask = 0xFFFF0000, | ||
68 | .value = 0x807A0000, | ||
69 | .num_devices = 14, | ||
70 | .device_list = (enum ppc_sys_devices[]) | ||
71 | { | ||
72 | MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, | ||
73 | MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, | ||
74 | MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2, | ||
75 | MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, | ||
76 | MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, | ||
77 | }, | ||
78 | }, | ||
79 | { | ||
80 | .ppc_sys_name = "8555", | ||
81 | .mask = 0xFFFF0000, | ||
82 | .value = 0x80710000, | ||
83 | .num_devices = 19, | ||
84 | .device_list = (enum ppc_sys_devices[]) | ||
85 | { | ||
86 | MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, | ||
87 | MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, | ||
88 | MPC85xx_PERFMON, MPC85xx_DUART, | ||
89 | MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1, | ||
90 | MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, | ||
91 | MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, | ||
92 | MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2, | ||
93 | MPC85xx_CPM_USB, | ||
94 | }, | ||
95 | }, | ||
96 | { | ||
97 | .ppc_sys_name = "8555E", | ||
98 | .mask = 0xFFFF0000, | ||
99 | .value = 0x80790000, | ||
100 | .num_devices = 20, | ||
101 | .device_list = (enum ppc_sys_devices[]) | ||
102 | { | ||
103 | MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1, | ||
104 | MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3, | ||
105 | MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2, | ||
106 | MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1, | ||
107 | MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, | ||
108 | MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, | ||
109 | MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2, | ||
110 | MPC85xx_CPM_USB, | ||
111 | }, | ||
112 | }, | ||
113 | { /* default match */ | ||
114 | .ppc_sys_name = "", | ||
115 | .mask = 0x00000000, | ||
116 | .value = 0x00000000, | ||
117 | }, | ||
118 | }; | ||
diff --git a/arch/ppc/syslib/mv64360_pic.c b/arch/ppc/syslib/mv64360_pic.c new file mode 100644 index 000000000000..74d8996418e9 --- /dev/null +++ b/arch/ppc/syslib/mv64360_pic.c | |||
@@ -0,0 +1,426 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/mv64360_pic.c | ||
3 | * | ||
4 | * Interrupt controller support for Marvell's MV64360. | ||
5 | * | ||
6 | * Author: Rabeeh Khoury <rabeeh@galileo.co.il> | ||
7 | * Based on MV64360 PIC written by | ||
8 | * Chris Zankel <chris@mvista.com> | ||
9 | * Mark A. Greer <mgreer@mvista.com> | ||
10 | * | ||
11 | * Copyright 2004 MontaVista Software, Inc. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | */ | ||
18 | |||
19 | /* | ||
20 | * This file contains the specific functions to support the MV64360 | ||
21 | * interrupt controller. | ||
22 | * | ||
23 | * The MV64360 has two main interrupt registers (high and low) that | ||
24 | * summarizes the interrupts generated by the units of the MV64360. | ||
25 | * Each bit is assigned to an interrupt number, where the low register | ||
26 | * are assigned from IRQ0 to IRQ31 and the high cause register | ||
27 | * from IRQ32 to IRQ63 | ||
28 | * The GPP (General Purpose Pins) interrupts are assigned from IRQ64 (GPP0) | ||
29 | * to IRQ95 (GPP31). | ||
30 | * get_irq() returns the lowest interrupt number that is currently asserted. | ||
31 | * | ||
32 | * Note: | ||
33 | * - This driver does not initialize the GPP when used as an interrupt | ||
34 | * input. | ||
35 | */ | ||
36 | |||
37 | #include <linux/stddef.h> | ||
38 | #include <linux/init.h> | ||
39 | #include <linux/sched.h> | ||
40 | #include <linux/signal.h> | ||
41 | #include <linux/stddef.h> | ||
42 | #include <linux/delay.h> | ||
43 | #include <linux/irq.h> | ||
44 | #include <linux/interrupt.h> | ||
45 | |||
46 | #include <asm/io.h> | ||
47 | #include <asm/processor.h> | ||
48 | #include <asm/system.h> | ||
49 | #include <asm/irq.h> | ||
50 | #include <asm/mv64x60.h> | ||
51 | |||
52 | #ifdef CONFIG_IRQ_ALL_CPUS | ||
53 | #error "The mv64360 does not support distribution of IRQs on all CPUs" | ||
54 | #endif | ||
55 | /* ========================== forward declaration ========================== */ | ||
56 | |||
57 | static void mv64360_unmask_irq(unsigned int); | ||
58 | static void mv64360_mask_irq(unsigned int); | ||
59 | static irqreturn_t mv64360_cpu_error_int_handler(int, void *, struct pt_regs *); | ||
60 | static irqreturn_t mv64360_sram_error_int_handler(int, void *, | ||
61 | struct pt_regs *); | ||
62 | static irqreturn_t mv64360_pci_error_int_handler(int, void *, struct pt_regs *); | ||
63 | |||
64 | /* ========================== local declarations =========================== */ | ||
65 | |||
66 | struct hw_interrupt_type mv64360_pic = { | ||
67 | .typename = " mv64360 ", | ||
68 | .enable = mv64360_unmask_irq, | ||
69 | .disable = mv64360_mask_irq, | ||
70 | .ack = mv64360_mask_irq, | ||
71 | .end = mv64360_unmask_irq, | ||
72 | }; | ||
73 | |||
74 | #define CPU_INTR_STR "mv64360 cpu interface error" | ||
75 | #define SRAM_INTR_STR "mv64360 internal sram error" | ||
76 | #define PCI0_INTR_STR "mv64360 pci 0 error" | ||
77 | #define PCI1_INTR_STR "mv64360 pci 1 error" | ||
78 | |||
79 | static struct mv64x60_handle bh; | ||
80 | |||
81 | u32 mv64360_irq_base = 0; /* MV64360 handles the next 96 IRQs from here */ | ||
82 | |||
83 | /* mv64360_init_irq() | ||
84 | * | ||
85 | * This function initializes the interrupt controller. It assigns | ||
86 | * all interrupts from IRQ0 to IRQ95 to the mv64360 interrupt controller. | ||
87 | * | ||
88 | * Input Variable(s): | ||
89 | * None. | ||
90 | * | ||
91 | * Outpu. Variable(s): | ||
92 | * None. | ||
93 | * | ||
94 | * Returns: | ||
95 | * void | ||
96 | * | ||
97 | * Note: | ||
98 | * We register all GPP inputs as interrupt source, but disable them. | ||
99 | */ | ||
100 | void __init | ||
101 | mv64360_init_irq(void) | ||
102 | { | ||
103 | int i; | ||
104 | |||
105 | if (ppc_md.progress) | ||
106 | ppc_md.progress("mv64360_init_irq: enter", 0x0); | ||
107 | |||
108 | bh.v_base = mv64x60_get_bridge_vbase(); | ||
109 | |||
110 | ppc_cached_irq_mask[0] = 0; | ||
111 | ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */ | ||
112 | ppc_cached_irq_mask[2] = 0; | ||
113 | |||
114 | /* disable all interrupts and clear current interrupts */ | ||
115 | mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0); | ||
116 | mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]); | ||
117 | mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,ppc_cached_irq_mask[0]); | ||
118 | mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,ppc_cached_irq_mask[1]); | ||
119 | |||
120 | /* All interrupts are level interrupts */ | ||
121 | for (i = mv64360_irq_base; i < (mv64360_irq_base + 96); i++) { | ||
122 | irq_desc[i].status |= IRQ_LEVEL; | ||
123 | irq_desc[i].handler = &mv64360_pic; | ||
124 | } | ||
125 | |||
126 | if (ppc_md.progress) | ||
127 | ppc_md.progress("mv64360_init_irq: exit", 0x0); | ||
128 | } | ||
129 | |||
130 | /* mv64360_get_irq() | ||
131 | * | ||
132 | * This function returns the lowest interrupt number of all interrupts that | ||
133 | * are currently asserted. | ||
134 | * | ||
135 | * Input Variable(s): | ||
136 | * struct pt_regs* not used | ||
137 | * | ||
138 | * Output Variable(s): | ||
139 | * None. | ||
140 | * | ||
141 | * Returns: | ||
142 | * int <interrupt number> or -2 (bogus interrupt) | ||
143 | * | ||
144 | */ | ||
145 | int | ||
146 | mv64360_get_irq(struct pt_regs *regs) | ||
147 | { | ||
148 | int irq; | ||
149 | int irq_gpp; | ||
150 | |||
151 | #ifdef CONFIG_SMP | ||
152 | /* | ||
153 | * Second CPU gets only doorbell (message) interrupts. | ||
154 | * The doorbell interrupt is BIT28 in the main interrupt low cause reg. | ||
155 | */ | ||
156 | int cpu_nr = smp_processor_id(); | ||
157 | if (cpu_nr == 1) { | ||
158 | if (!(mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO) & | ||
159 | (1 << MV64x60_IRQ_DOORBELL))) | ||
160 | return -1; | ||
161 | return mv64360_irq_base + MV64x60_IRQ_DOORBELL; | ||
162 | } | ||
163 | #endif | ||
164 | |||
165 | irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO); | ||
166 | irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]); | ||
167 | |||
168 | if (irq == -1) { | ||
169 | irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_HI); | ||
170 | irq = __ilog2((irq & 0x1f0003f7) & ppc_cached_irq_mask[1]); | ||
171 | |||
172 | if (irq == -1) | ||
173 | irq = -2; /* bogus interrupt, should never happen */ | ||
174 | else { | ||
175 | if ((irq >= 24) && (irq < MV64x60_IRQ_DOORBELL)) { | ||
176 | irq_gpp = mv64x60_read(&bh, | ||
177 | MV64x60_GPP_INTR_CAUSE); | ||
178 | irq_gpp = __ilog2(irq_gpp & | ||
179 | ppc_cached_irq_mask[2]); | ||
180 | |||
181 | if (irq_gpp == -1) | ||
182 | irq = -2; | ||
183 | else { | ||
184 | irq = irq_gpp + 64; | ||
185 | mv64x60_write(&bh, | ||
186 | MV64x60_GPP_INTR_CAUSE, | ||
187 | ~(1 << (irq - 64))); | ||
188 | } | ||
189 | } | ||
190 | else | ||
191 | irq += 32; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | (void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE); | ||
196 | |||
197 | if (irq < 0) | ||
198 | return (irq); | ||
199 | else | ||
200 | return (mv64360_irq_base + irq); | ||
201 | } | ||
202 | |||
203 | /* mv64360_unmask_irq() | ||
204 | * | ||
205 | * This function enables an interrupt. | ||
206 | * | ||
207 | * Input Variable(s): | ||
208 | * unsigned int interrupt number (IRQ0...IRQ95). | ||
209 | * | ||
210 | * Output Variable(s): | ||
211 | * None. | ||
212 | * | ||
213 | * Returns: | ||
214 | * void | ||
215 | */ | ||
216 | static void | ||
217 | mv64360_unmask_irq(unsigned int irq) | ||
218 | { | ||
219 | #ifdef CONFIG_SMP | ||
220 | /* second CPU gets only doorbell interrupts */ | ||
221 | if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) { | ||
222 | mv64x60_set_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO, | ||
223 | (1 << MV64x60_IRQ_DOORBELL)); | ||
224 | return; | ||
225 | } | ||
226 | #endif | ||
227 | irq -= mv64360_irq_base; | ||
228 | |||
229 | if (irq > 31) { | ||
230 | if (irq > 63) /* unmask GPP irq */ | ||
231 | mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, | ||
232 | ppc_cached_irq_mask[2] |= (1 << (irq - 64))); | ||
233 | else /* mask high interrupt register */ | ||
234 | mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI, | ||
235 | ppc_cached_irq_mask[1] |= (1 << (irq - 32))); | ||
236 | } | ||
237 | else /* mask low interrupt register */ | ||
238 | mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO, | ||
239 | ppc_cached_irq_mask[0] |= (1 << irq)); | ||
240 | |||
241 | (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); | ||
242 | return; | ||
243 | } | ||
244 | |||
245 | /* mv64360_mask_irq() | ||
246 | * | ||
247 | * This function disables the requested interrupt. | ||
248 | * | ||
249 | * Input Variable(s): | ||
250 | * unsigned int interrupt number (IRQ0...IRQ95). | ||
251 | * | ||
252 | * Output Variable(s): | ||
253 | * None. | ||
254 | * | ||
255 | * Returns: | ||
256 | * void | ||
257 | */ | ||
258 | static void | ||
259 | mv64360_mask_irq(unsigned int irq) | ||
260 | { | ||
261 | #ifdef CONFIG_SMP | ||
262 | if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) { | ||
263 | mv64x60_clr_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO, | ||
264 | (1 << MV64x60_IRQ_DOORBELL)); | ||
265 | return; | ||
266 | } | ||
267 | #endif | ||
268 | irq -= mv64360_irq_base; | ||
269 | |||
270 | if (irq > 31) { | ||
271 | if (irq > 63) /* mask GPP irq */ | ||
272 | mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, | ||
273 | ppc_cached_irq_mask[2] &= ~(1 << (irq - 64))); | ||
274 | else /* mask high interrupt register */ | ||
275 | mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI, | ||
276 | ppc_cached_irq_mask[1] &= ~(1 << (irq - 32))); | ||
277 | } | ||
278 | else /* mask low interrupt register */ | ||
279 | mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO, | ||
280 | ppc_cached_irq_mask[0] &= ~(1 << irq)); | ||
281 | |||
282 | (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK); | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | static irqreturn_t | ||
287 | mv64360_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
288 | { | ||
289 | printk(KERN_ERR "mv64360_cpu_error_int_handler: %s 0x%08x\n", | ||
290 | "Error on CPU interface - Cause regiser", | ||
291 | mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE)); | ||
292 | printk(KERN_ERR "\tCPU error register dump:\n"); | ||
293 | printk(KERN_ERR "\tAddress low 0x%08x\n", | ||
294 | mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO)); | ||
295 | printk(KERN_ERR "\tAddress high 0x%08x\n", | ||
296 | mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI)); | ||
297 | printk(KERN_ERR "\tData low 0x%08x\n", | ||
298 | mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO)); | ||
299 | printk(KERN_ERR "\tData high 0x%08x\n", | ||
300 | mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI)); | ||
301 | printk(KERN_ERR "\tParity 0x%08x\n", | ||
302 | mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY)); | ||
303 | mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0); | ||
304 | return IRQ_HANDLED; | ||
305 | } | ||
306 | |||
307 | static irqreturn_t | ||
308 | mv64360_sram_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
309 | { | ||
310 | printk(KERN_ERR "mv64360_sram_error_int_handler: %s 0x%08x\n", | ||
311 | "Error in internal SRAM - Cause register", | ||
312 | mv64x60_read(&bh, MV64360_SRAM_ERR_CAUSE)); | ||
313 | printk(KERN_ERR "\tSRAM error register dump:\n"); | ||
314 | printk(KERN_ERR "\tAddress Low 0x%08x\n", | ||
315 | mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_LO)); | ||
316 | printk(KERN_ERR "\tAddress High 0x%08x\n", | ||
317 | mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_HI)); | ||
318 | printk(KERN_ERR "\tData Low 0x%08x\n", | ||
319 | mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_LO)); | ||
320 | printk(KERN_ERR "\tData High 0x%08x\n", | ||
321 | mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_HI)); | ||
322 | printk(KERN_ERR "\tParity 0x%08x\n", | ||
323 | mv64x60_read(&bh, MV64360_SRAM_ERR_PARITY)); | ||
324 | mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0); | ||
325 | return IRQ_HANDLED; | ||
326 | } | ||
327 | |||
328 | static irqreturn_t | ||
329 | mv64360_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs) | ||
330 | { | ||
331 | u32 val; | ||
332 | unsigned int pci_bus = (unsigned int)dev_id; | ||
333 | |||
334 | if (pci_bus == 0) { /* Error on PCI 0 */ | ||
335 | val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE); | ||
336 | printk(KERN_ERR "%s: Error in PCI %d Interface\n", | ||
337 | "mv64360_pci_error_int_handler", pci_bus); | ||
338 | printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); | ||
339 | printk(KERN_ERR "\tCause register 0x%08x\n", val); | ||
340 | printk(KERN_ERR "\tAddress Low 0x%08x\n", | ||
341 | mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO)); | ||
342 | printk(KERN_ERR "\tAddress High 0x%08x\n", | ||
343 | mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI)); | ||
344 | printk(KERN_ERR "\tAttribute 0x%08x\n", | ||
345 | mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO)); | ||
346 | printk(KERN_ERR "\tCommand 0x%08x\n", | ||
347 | mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD)); | ||
348 | mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val); | ||
349 | } | ||
350 | if (pci_bus == 1) { /* Error on PCI 1 */ | ||
351 | val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE); | ||
352 | printk(KERN_ERR "%s: Error in PCI %d Interface\n", | ||
353 | "mv64360_pci_error_int_handler", pci_bus); | ||
354 | printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus); | ||
355 | printk(KERN_ERR "\tCause register 0x%08x\n", val); | ||
356 | printk(KERN_ERR "\tAddress Low 0x%08x\n", | ||
357 | mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO)); | ||
358 | printk(KERN_ERR "\tAddress High 0x%08x\n", | ||
359 | mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI)); | ||
360 | printk(KERN_ERR "\tAttribute 0x%08x\n", | ||
361 | mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO)); | ||
362 | printk(KERN_ERR "\tCommand 0x%08x\n", | ||
363 | mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD)); | ||
364 | mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val); | ||
365 | } | ||
366 | return IRQ_HANDLED; | ||
367 | } | ||
368 | |||
369 | static int __init | ||
370 | mv64360_register_hdlrs(void) | ||
371 | { | ||
372 | u32 mask; | ||
373 | int rc; | ||
374 | |||
375 | /* Clear old errors and register CPU interface error intr handler */ | ||
376 | mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0); | ||
377 | if ((rc = request_irq(MV64x60_IRQ_CPU_ERR + mv64360_irq_base, | ||
378 | mv64360_cpu_error_int_handler, SA_INTERRUPT, CPU_INTR_STR, 0))) | ||
379 | printk(KERN_WARNING "Can't register cpu error handler: %d", rc); | ||
380 | |||
381 | mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0); | ||
382 | mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000ff); | ||
383 | |||
384 | /* Clear old errors and register internal SRAM error intr handler */ | ||
385 | mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0); | ||
386 | if ((rc = request_irq(MV64360_IRQ_SRAM_PAR_ERR + mv64360_irq_base, | ||
387 | mv64360_sram_error_int_handler,SA_INTERRUPT,SRAM_INTR_STR, 0))) | ||
388 | printk(KERN_WARNING "Can't register SRAM error handler: %d",rc); | ||
389 | |||
390 | /* | ||
391 | * Bit 0 reserved on 64360 and erratum FEr PCI-#11 (PCI internal | ||
392 | * data parity error set incorrectly) on rev 0 & 1 of 64460 requires | ||
393 | * bit 0 to be cleared. | ||
394 | */ | ||
395 | mask = 0x00a50c24; | ||
396 | |||
397 | if ((mv64x60_get_bridge_type() == MV64x60_TYPE_MV64460) && | ||
398 | (mv64x60_get_bridge_rev() > 1)) | ||
399 | mask |= 0x1; /* enable DPErr on 64460 */ | ||
400 | |||
401 | /* Clear old errors and register PCI 0 error intr handler */ | ||
402 | mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, 0); | ||
403 | if ((rc = request_irq(MV64360_IRQ_PCI0 + mv64360_irq_base, | ||
404 | mv64360_pci_error_int_handler, | ||
405 | SA_INTERRUPT, PCI0_INTR_STR, (void *)0))) | ||
406 | printk(KERN_WARNING "Can't register pci 0 error handler: %d", | ||
407 | rc); | ||
408 | |||
409 | mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0); | ||
410 | mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, mask); | ||
411 | |||
412 | /* Clear old errors and register PCI 1 error intr handler */ | ||
413 | mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, 0); | ||
414 | if ((rc = request_irq(MV64360_IRQ_PCI1 + mv64360_irq_base, | ||
415 | mv64360_pci_error_int_handler, | ||
416 | SA_INTERRUPT, PCI1_INTR_STR, (void *)1))) | ||
417 | printk(KERN_WARNING "Can't register pci 1 error handler: %d", | ||
418 | rc); | ||
419 | |||
420 | mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0); | ||
421 | mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, mask); | ||
422 | |||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | arch_initcall(mv64360_register_hdlrs); | ||
diff --git a/arch/ppc/syslib/mv64x60.c b/arch/ppc/syslib/mv64x60.c new file mode 100644 index 000000000000..7b241e7876bd --- /dev/null +++ b/arch/ppc/syslib/mv64x60.c | |||
@@ -0,0 +1,2392 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mv64x60.c | ||
3 | * | ||
4 | * Common routines for the Marvell/Galileo Discovery line of host bridges | ||
5 | * (gt64260, mv64360, mv64460, ...). | ||
6 | * | ||
7 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
8 | * | ||
9 | * 2004 (c) MontaVista, Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/bootmem.h> | ||
21 | #include <linux/spinlock.h> | ||
22 | #include <linux/mv643xx.h> | ||
23 | |||
24 | #include <asm/byteorder.h> | ||
25 | #include <asm/io.h> | ||
26 | #include <asm/irq.h> | ||
27 | #include <asm/uaccess.h> | ||
28 | #include <asm/machdep.h> | ||
29 | #include <asm/pci-bridge.h> | ||
30 | #include <asm/delay.h> | ||
31 | #include <asm/mv64x60.h> | ||
32 | |||
33 | |||
34 | u8 mv64x60_pci_exclude_bridge = 1; | ||
35 | spinlock_t mv64x60_lock = SPIN_LOCK_UNLOCKED; | ||
36 | |||
37 | static phys_addr_t mv64x60_bridge_pbase = 0; | ||
38 | static void *mv64x60_bridge_vbase = 0; | ||
39 | static u32 mv64x60_bridge_type = MV64x60_TYPE_INVALID; | ||
40 | static u32 mv64x60_bridge_rev = 0; | ||
41 | |||
42 | static u32 gt64260_translate_size(u32 base, u32 size, u32 num_bits); | ||
43 | static u32 gt64260_untranslate_size(u32 base, u32 size, u32 num_bits); | ||
44 | static void gt64260_set_pci2mem_window(struct pci_controller *hose, u32 bus, | ||
45 | u32 window, u32 base); | ||
46 | static void gt64260_set_pci2regs_window(struct mv64x60_handle *bh, | ||
47 | struct pci_controller *hose, u32 bus, u32 base); | ||
48 | static u32 gt64260_is_enabled_32bit(struct mv64x60_handle *bh, u32 window); | ||
49 | static void gt64260_enable_window_32bit(struct mv64x60_handle *bh, u32 window); | ||
50 | static void gt64260_disable_window_32bit(struct mv64x60_handle *bh, u32 window); | ||
51 | static void gt64260_enable_window_64bit(struct mv64x60_handle *bh, u32 window); | ||
52 | static void gt64260_disable_window_64bit(struct mv64x60_handle *bh, u32 window); | ||
53 | static void gt64260_disable_all_windows(struct mv64x60_handle *bh, | ||
54 | struct mv64x60_setup_info *si); | ||
55 | static void gt64260a_chip_specific_init(struct mv64x60_handle *bh, | ||
56 | struct mv64x60_setup_info *si); | ||
57 | static void gt64260b_chip_specific_init(struct mv64x60_handle *bh, | ||
58 | struct mv64x60_setup_info *si); | ||
59 | |||
60 | static u32 mv64360_translate_size(u32 base, u32 size, u32 num_bits); | ||
61 | static u32 mv64360_untranslate_size(u32 base, u32 size, u32 num_bits); | ||
62 | static void mv64360_set_pci2mem_window(struct pci_controller *hose, u32 bus, | ||
63 | u32 window, u32 base); | ||
64 | static void mv64360_set_pci2regs_window(struct mv64x60_handle *bh, | ||
65 | struct pci_controller *hose, u32 bus, u32 base); | ||
66 | static u32 mv64360_is_enabled_32bit(struct mv64x60_handle *bh, u32 window); | ||
67 | static void mv64360_enable_window_32bit(struct mv64x60_handle *bh, u32 window); | ||
68 | static void mv64360_disable_window_32bit(struct mv64x60_handle *bh, u32 window); | ||
69 | static void mv64360_enable_window_64bit(struct mv64x60_handle *bh, u32 window); | ||
70 | static void mv64360_disable_window_64bit(struct mv64x60_handle *bh, u32 window); | ||
71 | static void mv64360_disable_all_windows(struct mv64x60_handle *bh, | ||
72 | struct mv64x60_setup_info *si); | ||
73 | static void mv64360_config_io2mem_windows(struct mv64x60_handle *bh, | ||
74 | struct mv64x60_setup_info *si, | ||
75 | u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]); | ||
76 | static void mv64360_set_mpsc2regs_window(struct mv64x60_handle *bh, u32 base); | ||
77 | static void mv64360_chip_specific_init(struct mv64x60_handle *bh, | ||
78 | struct mv64x60_setup_info *si); | ||
79 | static void mv64460_chip_specific_init(struct mv64x60_handle *bh, | ||
80 | struct mv64x60_setup_info *si); | ||
81 | |||
82 | |||
83 | /* | ||
84 | * Define tables that have the chip-specific info for each type of | ||
85 | * Marvell bridge chip. | ||
86 | */ | ||
87 | static struct mv64x60_chip_info gt64260a_ci __initdata = { /* GT64260A */ | ||
88 | .translate_size = gt64260_translate_size, | ||
89 | .untranslate_size = gt64260_untranslate_size, | ||
90 | .set_pci2mem_window = gt64260_set_pci2mem_window, | ||
91 | .set_pci2regs_window = gt64260_set_pci2regs_window, | ||
92 | .is_enabled_32bit = gt64260_is_enabled_32bit, | ||
93 | .enable_window_32bit = gt64260_enable_window_32bit, | ||
94 | .disable_window_32bit = gt64260_disable_window_32bit, | ||
95 | .enable_window_64bit = gt64260_enable_window_64bit, | ||
96 | .disable_window_64bit = gt64260_disable_window_64bit, | ||
97 | .disable_all_windows = gt64260_disable_all_windows, | ||
98 | .chip_specific_init = gt64260a_chip_specific_init, | ||
99 | .window_tab_32bit = gt64260_32bit_windows, | ||
100 | .window_tab_64bit = gt64260_64bit_windows, | ||
101 | }; | ||
102 | |||
103 | static struct mv64x60_chip_info gt64260b_ci __initdata = { /* GT64260B */ | ||
104 | .translate_size = gt64260_translate_size, | ||
105 | .untranslate_size = gt64260_untranslate_size, | ||
106 | .set_pci2mem_window = gt64260_set_pci2mem_window, | ||
107 | .set_pci2regs_window = gt64260_set_pci2regs_window, | ||
108 | .is_enabled_32bit = gt64260_is_enabled_32bit, | ||
109 | .enable_window_32bit = gt64260_enable_window_32bit, | ||
110 | .disable_window_32bit = gt64260_disable_window_32bit, | ||
111 | .enable_window_64bit = gt64260_enable_window_64bit, | ||
112 | .disable_window_64bit = gt64260_disable_window_64bit, | ||
113 | .disable_all_windows = gt64260_disable_all_windows, | ||
114 | .chip_specific_init = gt64260b_chip_specific_init, | ||
115 | .window_tab_32bit = gt64260_32bit_windows, | ||
116 | .window_tab_64bit = gt64260_64bit_windows, | ||
117 | }; | ||
118 | |||
119 | static struct mv64x60_chip_info mv64360_ci __initdata = { /* MV64360 */ | ||
120 | .translate_size = mv64360_translate_size, | ||
121 | .untranslate_size = mv64360_untranslate_size, | ||
122 | .set_pci2mem_window = mv64360_set_pci2mem_window, | ||
123 | .set_pci2regs_window = mv64360_set_pci2regs_window, | ||
124 | .is_enabled_32bit = mv64360_is_enabled_32bit, | ||
125 | .enable_window_32bit = mv64360_enable_window_32bit, | ||
126 | .disable_window_32bit = mv64360_disable_window_32bit, | ||
127 | .enable_window_64bit = mv64360_enable_window_64bit, | ||
128 | .disable_window_64bit = mv64360_disable_window_64bit, | ||
129 | .disable_all_windows = mv64360_disable_all_windows, | ||
130 | .config_io2mem_windows = mv64360_config_io2mem_windows, | ||
131 | .set_mpsc2regs_window = mv64360_set_mpsc2regs_window, | ||
132 | .chip_specific_init = mv64360_chip_specific_init, | ||
133 | .window_tab_32bit = mv64360_32bit_windows, | ||
134 | .window_tab_64bit = mv64360_64bit_windows, | ||
135 | }; | ||
136 | |||
137 | static struct mv64x60_chip_info mv64460_ci __initdata = { /* MV64460 */ | ||
138 | .translate_size = mv64360_translate_size, | ||
139 | .untranslate_size = mv64360_untranslate_size, | ||
140 | .set_pci2mem_window = mv64360_set_pci2mem_window, | ||
141 | .set_pci2regs_window = mv64360_set_pci2regs_window, | ||
142 | .is_enabled_32bit = mv64360_is_enabled_32bit, | ||
143 | .enable_window_32bit = mv64360_enable_window_32bit, | ||
144 | .disable_window_32bit = mv64360_disable_window_32bit, | ||
145 | .enable_window_64bit = mv64360_enable_window_64bit, | ||
146 | .disable_window_64bit = mv64360_disable_window_64bit, | ||
147 | .disable_all_windows = mv64360_disable_all_windows, | ||
148 | .config_io2mem_windows = mv64360_config_io2mem_windows, | ||
149 | .set_mpsc2regs_window = mv64360_set_mpsc2regs_window, | ||
150 | .chip_specific_init = mv64460_chip_specific_init, | ||
151 | .window_tab_32bit = mv64360_32bit_windows, | ||
152 | .window_tab_64bit = mv64360_64bit_windows, | ||
153 | }; | ||
154 | |||
155 | /* | ||
156 | ***************************************************************************** | ||
157 | * | ||
158 | * Platform Device Definitions | ||
159 | * | ||
160 | ***************************************************************************** | ||
161 | */ | ||
162 | #ifdef CONFIG_SERIAL_MPSC | ||
163 | static struct mpsc_shared_pdata mv64x60_mpsc_shared_pdata = { | ||
164 | .mrr_val = 0x3ffffe38, | ||
165 | .rcrr_val = 0, | ||
166 | .tcrr_val = 0, | ||
167 | .intr_cause_val = 0, | ||
168 | .intr_mask_val = 0, | ||
169 | }; | ||
170 | |||
171 | static struct resource mv64x60_mpsc_shared_resources[] = { | ||
172 | /* Do not change the order of the IORESOURCE_MEM resources */ | ||
173 | [0] = { | ||
174 | .name = "mpsc routing base", | ||
175 | .start = MV64x60_MPSC_ROUTING_OFFSET, | ||
176 | .end = MV64x60_MPSC_ROUTING_OFFSET + | ||
177 | MPSC_ROUTING_REG_BLOCK_SIZE - 1, | ||
178 | .flags = IORESOURCE_MEM, | ||
179 | }, | ||
180 | [1] = { | ||
181 | .name = "sdma intr base", | ||
182 | .start = MV64x60_SDMA_INTR_OFFSET, | ||
183 | .end = MV64x60_SDMA_INTR_OFFSET + | ||
184 | MPSC_SDMA_INTR_REG_BLOCK_SIZE - 1, | ||
185 | .flags = IORESOURCE_MEM, | ||
186 | }, | ||
187 | }; | ||
188 | |||
189 | static struct platform_device mpsc_shared_device = { /* Shared device */ | ||
190 | .name = MPSC_SHARED_NAME, | ||
191 | .id = 0, | ||
192 | .num_resources = ARRAY_SIZE(mv64x60_mpsc_shared_resources), | ||
193 | .resource = mv64x60_mpsc_shared_resources, | ||
194 | .dev = { | ||
195 | .platform_data = &mv64x60_mpsc_shared_pdata, | ||
196 | }, | ||
197 | }; | ||
198 | |||
199 | static struct mpsc_pdata mv64x60_mpsc0_pdata = { | ||
200 | .mirror_regs = 0, | ||
201 | .cache_mgmt = 0, | ||
202 | .max_idle = 0, | ||
203 | .default_baud = 9600, | ||
204 | .default_bits = 8, | ||
205 | .default_parity = 'n', | ||
206 | .default_flow = 'n', | ||
207 | .chr_1_val = 0x00000000, | ||
208 | .chr_2_val = 0x00000000, | ||
209 | .chr_10_val = 0x00000003, | ||
210 | .mpcr_val = 0, | ||
211 | .bcr_val = 0, | ||
212 | .brg_can_tune = 0, | ||
213 | .brg_clk_src = 8, /* Default to TCLK */ | ||
214 | .brg_clk_freq = 100000000, /* Default to 100 MHz */ | ||
215 | }; | ||
216 | |||
217 | static struct resource mv64x60_mpsc0_resources[] = { | ||
218 | /* Do not change the order of the IORESOURCE_MEM resources */ | ||
219 | [0] = { | ||
220 | .name = "mpsc 0 base", | ||
221 | .start = MV64x60_MPSC_0_OFFSET, | ||
222 | .end = MV64x60_MPSC_0_OFFSET + MPSC_REG_BLOCK_SIZE - 1, | ||
223 | .flags = IORESOURCE_MEM, | ||
224 | }, | ||
225 | [1] = { | ||
226 | .name = "sdma 0 base", | ||
227 | .start = MV64x60_SDMA_0_OFFSET, | ||
228 | .end = MV64x60_SDMA_0_OFFSET + MPSC_SDMA_REG_BLOCK_SIZE - 1, | ||
229 | .flags = IORESOURCE_MEM, | ||
230 | }, | ||
231 | [2] = { | ||
232 | .name = "brg 0 base", | ||
233 | .start = MV64x60_BRG_0_OFFSET, | ||
234 | .end = MV64x60_BRG_0_OFFSET + MPSC_BRG_REG_BLOCK_SIZE - 1, | ||
235 | .flags = IORESOURCE_MEM, | ||
236 | }, | ||
237 | [3] = { | ||
238 | .name = "sdma 0 irq", | ||
239 | .start = MV64x60_IRQ_SDMA_0, | ||
240 | .end = MV64x60_IRQ_SDMA_0, | ||
241 | .flags = IORESOURCE_IRQ, | ||
242 | }, | ||
243 | }; | ||
244 | |||
245 | static struct platform_device mpsc0_device = { | ||
246 | .name = MPSC_CTLR_NAME, | ||
247 | .id = 0, | ||
248 | .num_resources = ARRAY_SIZE(mv64x60_mpsc0_resources), | ||
249 | .resource = mv64x60_mpsc0_resources, | ||
250 | .dev = { | ||
251 | .platform_data = &mv64x60_mpsc0_pdata, | ||
252 | }, | ||
253 | }; | ||
254 | |||
255 | static struct mpsc_pdata mv64x60_mpsc1_pdata = { | ||
256 | .mirror_regs = 0, | ||
257 | .cache_mgmt = 0, | ||
258 | .max_idle = 0, | ||
259 | .default_baud = 9600, | ||
260 | .default_bits = 8, | ||
261 | .default_parity = 'n', | ||
262 | .default_flow = 'n', | ||
263 | .chr_1_val = 0x00000000, | ||
264 | .chr_1_val = 0x00000000, | ||
265 | .chr_2_val = 0x00000000, | ||
266 | .chr_10_val = 0x00000003, | ||
267 | .mpcr_val = 0, | ||
268 | .bcr_val = 0, | ||
269 | .brg_can_tune = 0, | ||
270 | .brg_clk_src = 8, /* Default to TCLK */ | ||
271 | .brg_clk_freq = 100000000, /* Default to 100 MHz */ | ||
272 | }; | ||
273 | |||
274 | static struct resource mv64x60_mpsc1_resources[] = { | ||
275 | /* Do not change the order of the IORESOURCE_MEM resources */ | ||
276 | [0] = { | ||
277 | .name = "mpsc 1 base", | ||
278 | .start = MV64x60_MPSC_1_OFFSET, | ||
279 | .end = MV64x60_MPSC_1_OFFSET + MPSC_REG_BLOCK_SIZE - 1, | ||
280 | .flags = IORESOURCE_MEM, | ||
281 | }, | ||
282 | [1] = { | ||
283 | .name = "sdma 1 base", | ||
284 | .start = MV64x60_SDMA_1_OFFSET, | ||
285 | .end = MV64x60_SDMA_1_OFFSET + MPSC_SDMA_REG_BLOCK_SIZE - 1, | ||
286 | .flags = IORESOURCE_MEM, | ||
287 | }, | ||
288 | [2] = { | ||
289 | .name = "brg 1 base", | ||
290 | .start = MV64x60_BRG_1_OFFSET, | ||
291 | .end = MV64x60_BRG_1_OFFSET + MPSC_BRG_REG_BLOCK_SIZE - 1, | ||
292 | .flags = IORESOURCE_MEM, | ||
293 | }, | ||
294 | [3] = { | ||
295 | .name = "sdma 1 irq", | ||
296 | .start = MV64360_IRQ_SDMA_1, | ||
297 | .end = MV64360_IRQ_SDMA_1, | ||
298 | .flags = IORESOURCE_IRQ, | ||
299 | }, | ||
300 | }; | ||
301 | |||
302 | static struct platform_device mpsc1_device = { | ||
303 | .name = MPSC_CTLR_NAME, | ||
304 | .id = 1, | ||
305 | .num_resources = ARRAY_SIZE(mv64x60_mpsc1_resources), | ||
306 | .resource = mv64x60_mpsc1_resources, | ||
307 | .dev = { | ||
308 | .platform_data = &mv64x60_mpsc1_pdata, | ||
309 | }, | ||
310 | }; | ||
311 | #endif | ||
312 | |||
313 | #ifdef CONFIG_MV643XX_ETH | ||
314 | static struct resource mv64x60_eth_shared_resources[] = { | ||
315 | [0] = { | ||
316 | .name = "ethernet shared base", | ||
317 | .start = MV643XX_ETH_SHARED_REGS, | ||
318 | .end = MV643XX_ETH_SHARED_REGS + | ||
319 | MV643XX_ETH_SHARED_REGS_SIZE - 1, | ||
320 | .flags = IORESOURCE_MEM, | ||
321 | }, | ||
322 | }; | ||
323 | |||
324 | static struct platform_device mv64x60_eth_shared_device = { | ||
325 | .name = MV643XX_ETH_SHARED_NAME, | ||
326 | .id = 0, | ||
327 | .num_resources = ARRAY_SIZE(mv64x60_eth_shared_resources), | ||
328 | .resource = mv64x60_eth_shared_resources, | ||
329 | }; | ||
330 | |||
331 | #ifdef CONFIG_MV643XX_ETH_0 | ||
332 | static struct resource mv64x60_eth0_resources[] = { | ||
333 | [0] = { | ||
334 | .name = "eth0 irq", | ||
335 | .start = MV64x60_IRQ_ETH_0, | ||
336 | .end = MV64x60_IRQ_ETH_0, | ||
337 | .flags = IORESOURCE_IRQ, | ||
338 | }, | ||
339 | }; | ||
340 | |||
341 | static struct mv643xx_eth_platform_data eth0_pd; | ||
342 | |||
343 | static struct platform_device eth0_device = { | ||
344 | .name = MV643XX_ETH_NAME, | ||
345 | .id = 0, | ||
346 | .num_resources = ARRAY_SIZE(mv64x60_eth0_resources), | ||
347 | .resource = mv64x60_eth0_resources, | ||
348 | .dev = { | ||
349 | .platform_data = ð0_pd, | ||
350 | }, | ||
351 | }; | ||
352 | #endif | ||
353 | |||
354 | #ifdef CONFIG_MV643XX_ETH_1 | ||
355 | static struct resource mv64x60_eth1_resources[] = { | ||
356 | [0] = { | ||
357 | .name = "eth1 irq", | ||
358 | .start = MV64x60_IRQ_ETH_1, | ||
359 | .end = MV64x60_IRQ_ETH_1, | ||
360 | .flags = IORESOURCE_IRQ, | ||
361 | }, | ||
362 | }; | ||
363 | |||
364 | static struct mv643xx_eth_platform_data eth1_pd; | ||
365 | |||
366 | static struct platform_device eth1_device = { | ||
367 | .name = MV643XX_ETH_NAME, | ||
368 | .id = 1, | ||
369 | .num_resources = ARRAY_SIZE(mv64x60_eth1_resources), | ||
370 | .resource = mv64x60_eth1_resources, | ||
371 | .dev = { | ||
372 | .platform_data = ð1_pd, | ||
373 | }, | ||
374 | }; | ||
375 | #endif | ||
376 | |||
377 | #ifdef CONFIG_MV643XX_ETH_2 | ||
378 | static struct resource mv64x60_eth2_resources[] = { | ||
379 | [0] = { | ||
380 | .name = "eth2 irq", | ||
381 | .start = MV64x60_IRQ_ETH_2, | ||
382 | .end = MV64x60_IRQ_ETH_2, | ||
383 | .flags = IORESOURCE_IRQ, | ||
384 | }, | ||
385 | }; | ||
386 | |||
387 | static struct mv643xx_eth_platform_data eth2_pd; | ||
388 | |||
389 | static struct platform_device eth2_device = { | ||
390 | .name = MV643XX_ETH_NAME, | ||
391 | .id = 2, | ||
392 | .num_resources = ARRAY_SIZE(mv64x60_eth2_resources), | ||
393 | .resource = mv64x60_eth2_resources, | ||
394 | .dev = { | ||
395 | .platform_data = ð2_pd, | ||
396 | }, | ||
397 | }; | ||
398 | #endif | ||
399 | #endif | ||
400 | |||
401 | #ifdef CONFIG_I2C_MV64XXX | ||
402 | static struct mv64xxx_i2c_pdata mv64xxx_i2c_pdata = { | ||
403 | .freq_m = 8, | ||
404 | .freq_n = 3, | ||
405 | .timeout = 1000, /* Default timeout of 1 second */ | ||
406 | .retries = 1, | ||
407 | }; | ||
408 | |||
409 | static struct resource mv64xxx_i2c_resources[] = { | ||
410 | /* Do not change the order of the IORESOURCE_MEM resources */ | ||
411 | [0] = { | ||
412 | .name = "mv64xxx i2c base", | ||
413 | .start = MV64XXX_I2C_OFFSET, | ||
414 | .end = MV64XXX_I2C_OFFSET + MV64XXX_I2C_REG_BLOCK_SIZE - 1, | ||
415 | .flags = IORESOURCE_MEM, | ||
416 | }, | ||
417 | [1] = { | ||
418 | .name = "mv64xxx i2c irq", | ||
419 | .start = MV64x60_IRQ_I2C, | ||
420 | .end = MV64x60_IRQ_I2C, | ||
421 | .flags = IORESOURCE_IRQ, | ||
422 | }, | ||
423 | }; | ||
424 | |||
425 | static struct platform_device i2c_device = { | ||
426 | .name = MV64XXX_I2C_CTLR_NAME, | ||
427 | .id = 0, | ||
428 | .num_resources = ARRAY_SIZE(mv64xxx_i2c_resources), | ||
429 | .resource = mv64xxx_i2c_resources, | ||
430 | .dev = { | ||
431 | .platform_data = &mv64xxx_i2c_pdata, | ||
432 | }, | ||
433 | }; | ||
434 | #endif | ||
435 | |||
436 | static struct platform_device *mv64x60_pd_devs[] __initdata = { | ||
437 | #ifdef CONFIG_SERIAL_MPSC | ||
438 | &mpsc_shared_device, | ||
439 | &mpsc0_device, | ||
440 | &mpsc1_device, | ||
441 | #endif | ||
442 | #ifdef CONFIG_MV643XX_ETH | ||
443 | &mv64x60_eth_shared_device, | ||
444 | #endif | ||
445 | #ifdef CONFIG_MV643XX_ETH_0 | ||
446 | ð0_device, | ||
447 | #endif | ||
448 | #ifdef CONFIG_MV643XX_ETH_1 | ||
449 | ð1_device, | ||
450 | #endif | ||
451 | #ifdef CONFIG_MV643XX_ETH_2 | ||
452 | ð2_device, | ||
453 | #endif | ||
454 | #ifdef CONFIG_I2C_MV64XXX | ||
455 | &i2c_device, | ||
456 | #endif | ||
457 | }; | ||
458 | |||
459 | /* | ||
460 | ***************************************************************************** | ||
461 | * | ||
462 | * Bridge Initialization Routines | ||
463 | * | ||
464 | ***************************************************************************** | ||
465 | */ | ||
466 | /* | ||
467 | * mv64x60_init() | ||
468 | * | ||
469 | * Initialze the bridge based on setting passed in via 'si'. The bridge | ||
470 | * handle, 'bh', will be set so that it can be used to make subsequent | ||
471 | * calls to routines in this file. | ||
472 | */ | ||
473 | int __init | ||
474 | mv64x60_init(struct mv64x60_handle *bh, struct mv64x60_setup_info *si) | ||
475 | { | ||
476 | u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]; | ||
477 | |||
478 | if (ppc_md.progress) | ||
479 | ppc_md.progress("mv64x60 initialization", 0x0); | ||
480 | |||
481 | spin_lock_init(&mv64x60_lock); | ||
482 | mv64x60_early_init(bh, si); | ||
483 | |||
484 | if (mv64x60_get_type(bh) || mv64x60_setup_for_chip(bh)) { | ||
485 | iounmap(bh->v_base); | ||
486 | bh->v_base = 0; | ||
487 | if (ppc_md.progress) | ||
488 | ppc_md.progress("mv64x60_init: Can't determine chip",0); | ||
489 | return -1; | ||
490 | } | ||
491 | |||
492 | bh->ci->disable_all_windows(bh, si); | ||
493 | mv64x60_get_mem_windows(bh, mem_windows); | ||
494 | mv64x60_config_cpu2mem_windows(bh, si, mem_windows); | ||
495 | |||
496 | if (bh->ci->config_io2mem_windows) | ||
497 | bh->ci->config_io2mem_windows(bh, si, mem_windows); | ||
498 | if (bh->ci->set_mpsc2regs_window) | ||
499 | bh->ci->set_mpsc2regs_window(bh, si->phys_reg_base); | ||
500 | |||
501 | if (si->pci_1.enable_bus) { | ||
502 | bh->io_base_b = (u32)ioremap(si->pci_1.pci_io.cpu_base, | ||
503 | si->pci_1.pci_io.size); | ||
504 | isa_io_base = bh->io_base_b; | ||
505 | } | ||
506 | |||
507 | if (si->pci_0.enable_bus) { | ||
508 | bh->io_base_a = (u32)ioremap(si->pci_0.pci_io.cpu_base, | ||
509 | si->pci_0.pci_io.size); | ||
510 | isa_io_base = bh->io_base_a; | ||
511 | |||
512 | mv64x60_alloc_hose(bh, MV64x60_PCI0_CONFIG_ADDR, | ||
513 | MV64x60_PCI0_CONFIG_DATA, &bh->hose_a); | ||
514 | mv64x60_config_resources(bh->hose_a, &si->pci_0, bh->io_base_a); | ||
515 | mv64x60_config_pci_params(bh->hose_a, &si->pci_0); | ||
516 | |||
517 | mv64x60_config_cpu2pci_windows(bh, &si->pci_0, 0); | ||
518 | mv64x60_config_pci2mem_windows(bh, bh->hose_a, &si->pci_0, 0, | ||
519 | mem_windows); | ||
520 | bh->ci->set_pci2regs_window(bh, bh->hose_a, 0, | ||
521 | si->phys_reg_base); | ||
522 | } | ||
523 | |||
524 | if (si->pci_1.enable_bus) { | ||
525 | mv64x60_alloc_hose(bh, MV64x60_PCI1_CONFIG_ADDR, | ||
526 | MV64x60_PCI1_CONFIG_DATA, &bh->hose_b); | ||
527 | mv64x60_config_resources(bh->hose_b, &si->pci_1, bh->io_base_b); | ||
528 | mv64x60_config_pci_params(bh->hose_b, &si->pci_1); | ||
529 | |||
530 | mv64x60_config_cpu2pci_windows(bh, &si->pci_1, 1); | ||
531 | mv64x60_config_pci2mem_windows(bh, bh->hose_b, &si->pci_1, 1, | ||
532 | mem_windows); | ||
533 | bh->ci->set_pci2regs_window(bh, bh->hose_b, 1, | ||
534 | si->phys_reg_base); | ||
535 | } | ||
536 | |||
537 | bh->ci->chip_specific_init(bh, si); | ||
538 | mv64x60_pd_fixup(bh, mv64x60_pd_devs, ARRAY_SIZE(mv64x60_pd_devs)); | ||
539 | |||
540 | return 0; | ||
541 | } | ||
542 | |||
543 | /* | ||
544 | * mv64x60_early_init() | ||
545 | * | ||
546 | * Do some bridge work that must take place before we start messing with | ||
547 | * the bridge for real. | ||
548 | */ | ||
549 | void __init | ||
550 | mv64x60_early_init(struct mv64x60_handle *bh, struct mv64x60_setup_info *si) | ||
551 | { | ||
552 | struct pci_controller hose_a, hose_b; | ||
553 | |||
554 | memset(bh, 0, sizeof(*bh)); | ||
555 | |||
556 | bh->p_base = si->phys_reg_base; | ||
557 | bh->v_base = ioremap(bh->p_base, MV64x60_INTERNAL_SPACE_SIZE); | ||
558 | |||
559 | mv64x60_bridge_pbase = bh->p_base; | ||
560 | mv64x60_bridge_vbase = bh->v_base; | ||
561 | |||
562 | /* Assuming pci mode [reserved] bits 4:5 on 64260 are 0 */ | ||
563 | bh->pci_mode_a = mv64x60_read(bh, MV64x60_PCI0_MODE) & | ||
564 | MV64x60_PCIMODE_MASK; | ||
565 | bh->pci_mode_b = mv64x60_read(bh, MV64x60_PCI1_MODE) & | ||
566 | MV64x60_PCIMODE_MASK; | ||
567 | |||
568 | /* Need temporary hose structs to call mv64x60_set_bus() */ | ||
569 | memset(&hose_a, 0, sizeof(hose_a)); | ||
570 | memset(&hose_b, 0, sizeof(hose_b)); | ||
571 | setup_indirect_pci_nomap(&hose_a, bh->v_base + MV64x60_PCI0_CONFIG_ADDR, | ||
572 | bh->v_base + MV64x60_PCI0_CONFIG_DATA); | ||
573 | setup_indirect_pci_nomap(&hose_b, bh->v_base + MV64x60_PCI1_CONFIG_ADDR, | ||
574 | bh->v_base + MV64x60_PCI1_CONFIG_DATA); | ||
575 | bh->hose_a = &hose_a; | ||
576 | bh->hose_b = &hose_b; | ||
577 | |||
578 | mv64x60_set_bus(bh, 0, 0); | ||
579 | mv64x60_set_bus(bh, 1, 0); | ||
580 | |||
581 | bh->hose_a = NULL; | ||
582 | bh->hose_b = NULL; | ||
583 | |||
584 | /* Clear bit 0 of PCI addr decode control so PCI->CPU remap 1:1 */ | ||
585 | mv64x60_clr_bits(bh, MV64x60_PCI0_PCI_DECODE_CNTL, 0x00000001); | ||
586 | mv64x60_clr_bits(bh, MV64x60_PCI1_PCI_DECODE_CNTL, 0x00000001); | ||
587 | |||
588 | /* Bit 12 MUST be 0; set bit 27--don't auto-update cpu remap regs */ | ||
589 | mv64x60_clr_bits(bh, MV64x60_CPU_CONFIG, (1<<12)); | ||
590 | mv64x60_set_bits(bh, MV64x60_CPU_CONFIG, (1<<27)); | ||
591 | |||
592 | mv64x60_set_bits(bh, MV64x60_PCI0_TO_RETRY, 0xffff); | ||
593 | mv64x60_set_bits(bh, MV64x60_PCI1_TO_RETRY, 0xffff); | ||
594 | |||
595 | return; | ||
596 | } | ||
597 | |||
598 | /* | ||
599 | ***************************************************************************** | ||
600 | * | ||
601 | * Window Config Routines | ||
602 | * | ||
603 | ***************************************************************************** | ||
604 | */ | ||
605 | /* | ||
606 | * mv64x60_get_32bit_window() | ||
607 | * | ||
608 | * Determine the base address and size of a 32-bit window on the bridge. | ||
609 | */ | ||
610 | void __init | ||
611 | mv64x60_get_32bit_window(struct mv64x60_handle *bh, u32 window, | ||
612 | u32 *base, u32 *size) | ||
613 | { | ||
614 | u32 val, base_reg, size_reg, base_bits, size_bits; | ||
615 | u32 (*get_from_field)(u32 val, u32 num_bits); | ||
616 | |||
617 | base_reg = bh->ci->window_tab_32bit[window].base_reg; | ||
618 | |||
619 | if (base_reg != 0) { | ||
620 | size_reg = bh->ci->window_tab_32bit[window].size_reg; | ||
621 | base_bits = bh->ci->window_tab_32bit[window].base_bits; | ||
622 | size_bits = bh->ci->window_tab_32bit[window].size_bits; | ||
623 | get_from_field= bh->ci->window_tab_32bit[window].get_from_field; | ||
624 | |||
625 | val = mv64x60_read(bh, base_reg); | ||
626 | *base = get_from_field(val, base_bits); | ||
627 | |||
628 | if (size_reg != 0) { | ||
629 | val = mv64x60_read(bh, size_reg); | ||
630 | val = get_from_field(val, size_bits); | ||
631 | *size = bh->ci->untranslate_size(*base, val, size_bits); | ||
632 | } | ||
633 | else | ||
634 | *size = 0; | ||
635 | } | ||
636 | else { | ||
637 | *base = 0; | ||
638 | *size = 0; | ||
639 | } | ||
640 | |||
641 | pr_debug("get 32bit window: %d, base: 0x%x, size: 0x%x\n", | ||
642 | window, *base, *size); | ||
643 | |||
644 | return; | ||
645 | } | ||
646 | |||
647 | /* | ||
648 | * mv64x60_set_32bit_window() | ||
649 | * | ||
650 | * Set the base address and size of a 32-bit window on the bridge. | ||
651 | */ | ||
652 | void __init | ||
653 | mv64x60_set_32bit_window(struct mv64x60_handle *bh, u32 window, | ||
654 | u32 base, u32 size, u32 other_bits) | ||
655 | { | ||
656 | u32 val, base_reg, size_reg, base_bits, size_bits; | ||
657 | u32 (*map_to_field)(u32 val, u32 num_bits); | ||
658 | |||
659 | pr_debug("set 32bit window: %d, base: 0x%x, size: 0x%x, other: 0x%x\n", | ||
660 | window, base, size, other_bits); | ||
661 | |||
662 | base_reg = bh->ci->window_tab_32bit[window].base_reg; | ||
663 | |||
664 | if (base_reg != 0) { | ||
665 | size_reg = bh->ci->window_tab_32bit[window].size_reg; | ||
666 | base_bits = bh->ci->window_tab_32bit[window].base_bits; | ||
667 | size_bits = bh->ci->window_tab_32bit[window].size_bits; | ||
668 | map_to_field = bh->ci->window_tab_32bit[window].map_to_field; | ||
669 | |||
670 | val = map_to_field(base, base_bits) | other_bits; | ||
671 | mv64x60_write(bh, base_reg, val); | ||
672 | |||
673 | if (size_reg != 0) { | ||
674 | val = bh->ci->translate_size(base, size, size_bits); | ||
675 | val = map_to_field(val, size_bits); | ||
676 | mv64x60_write(bh, size_reg, val); | ||
677 | } | ||
678 | |||
679 | (void)mv64x60_read(bh, base_reg); /* Flush FIFO */ | ||
680 | } | ||
681 | |||
682 | return; | ||
683 | } | ||
684 | |||
685 | /* | ||
686 | * mv64x60_get_64bit_window() | ||
687 | * | ||
688 | * Determine the base address and size of a 64-bit window on the bridge. | ||
689 | */ | ||
690 | void __init | ||
691 | mv64x60_get_64bit_window(struct mv64x60_handle *bh, u32 window, | ||
692 | u32 *base_hi, u32 *base_lo, u32 *size) | ||
693 | { | ||
694 | u32 val, base_lo_reg, size_reg, base_lo_bits, size_bits; | ||
695 | u32 (*get_from_field)(u32 val, u32 num_bits); | ||
696 | |||
697 | base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg; | ||
698 | |||
699 | if (base_lo_reg != 0) { | ||
700 | size_reg = bh->ci->window_tab_64bit[window].size_reg; | ||
701 | base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits; | ||
702 | size_bits = bh->ci->window_tab_64bit[window].size_bits; | ||
703 | get_from_field= bh->ci->window_tab_64bit[window].get_from_field; | ||
704 | |||
705 | *base_hi = mv64x60_read(bh, | ||
706 | bh->ci->window_tab_64bit[window].base_hi_reg); | ||
707 | |||
708 | val = mv64x60_read(bh, base_lo_reg); | ||
709 | *base_lo = get_from_field(val, base_lo_bits); | ||
710 | |||
711 | if (size_reg != 0) { | ||
712 | val = mv64x60_read(bh, size_reg); | ||
713 | val = get_from_field(val, size_bits); | ||
714 | *size = bh->ci->untranslate_size(*base_lo, val, | ||
715 | size_bits); | ||
716 | } | ||
717 | else | ||
718 | *size = 0; | ||
719 | } | ||
720 | else { | ||
721 | *base_hi = 0; | ||
722 | *base_lo = 0; | ||
723 | *size = 0; | ||
724 | } | ||
725 | |||
726 | pr_debug("get 64bit window: %d, base hi: 0x%x, base lo: 0x%x, " | ||
727 | "size: 0x%x\n", window, *base_hi, *base_lo, *size); | ||
728 | |||
729 | return; | ||
730 | } | ||
731 | |||
732 | /* | ||
733 | * mv64x60_set_64bit_window() | ||
734 | * | ||
735 | * Set the base address and size of a 64-bit window on the bridge. | ||
736 | */ | ||
737 | void __init | ||
738 | mv64x60_set_64bit_window(struct mv64x60_handle *bh, u32 window, | ||
739 | u32 base_hi, u32 base_lo, u32 size, u32 other_bits) | ||
740 | { | ||
741 | u32 val, base_lo_reg, size_reg, base_lo_bits, size_bits; | ||
742 | u32 (*map_to_field)(u32 val, u32 num_bits); | ||
743 | |||
744 | pr_debug("set 64bit window: %d, base hi: 0x%x, base lo: 0x%x, " | ||
745 | "size: 0x%x, other: 0x%x\n", | ||
746 | window, base_hi, base_lo, size, other_bits); | ||
747 | |||
748 | base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg; | ||
749 | |||
750 | if (base_lo_reg != 0) { | ||
751 | size_reg = bh->ci->window_tab_64bit[window].size_reg; | ||
752 | base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits; | ||
753 | size_bits = bh->ci->window_tab_64bit[window].size_bits; | ||
754 | map_to_field = bh->ci->window_tab_64bit[window].map_to_field; | ||
755 | |||
756 | mv64x60_write(bh, bh->ci->window_tab_64bit[window].base_hi_reg, | ||
757 | base_hi); | ||
758 | |||
759 | val = map_to_field(base_lo, base_lo_bits) | other_bits; | ||
760 | mv64x60_write(bh, base_lo_reg, val); | ||
761 | |||
762 | if (size_reg != 0) { | ||
763 | val = bh->ci->translate_size(base_lo, size, size_bits); | ||
764 | val = map_to_field(val, size_bits); | ||
765 | mv64x60_write(bh, size_reg, val); | ||
766 | } | ||
767 | |||
768 | (void)mv64x60_read(bh, base_lo_reg); /* Flush FIFO */ | ||
769 | } | ||
770 | |||
771 | return; | ||
772 | } | ||
773 | |||
774 | /* | ||
775 | * mv64x60_mask() | ||
776 | * | ||
777 | * Take the high-order 'num_bits' of 'val' & mask off low bits. | ||
778 | */ | ||
779 | u32 __init | ||
780 | mv64x60_mask(u32 val, u32 num_bits) | ||
781 | { | ||
782 | return val & (0xffffffff << (32 - num_bits)); | ||
783 | } | ||
784 | |||
785 | /* | ||
786 | * mv64x60_shift_left() | ||
787 | * | ||
788 | * Take the low-order 'num_bits' of 'val', shift left to align at bit 31 (MSB). | ||
789 | */ | ||
790 | u32 __init | ||
791 | mv64x60_shift_left(u32 val, u32 num_bits) | ||
792 | { | ||
793 | return val << (32 - num_bits); | ||
794 | } | ||
795 | |||
796 | /* | ||
797 | * mv64x60_shift_right() | ||
798 | * | ||
799 | * Take the high-order 'num_bits' of 'val', shift right to align at bit 0 (LSB). | ||
800 | */ | ||
801 | u32 __init | ||
802 | mv64x60_shift_right(u32 val, u32 num_bits) | ||
803 | { | ||
804 | return val >> (32 - num_bits); | ||
805 | } | ||
806 | |||
807 | /* | ||
808 | ***************************************************************************** | ||
809 | * | ||
810 | * Chip Identification Routines | ||
811 | * | ||
812 | ***************************************************************************** | ||
813 | */ | ||
814 | /* | ||
815 | * mv64x60_get_type() | ||
816 | * | ||
817 | * Determine the type of bridge chip we have. | ||
818 | */ | ||
819 | int __init | ||
820 | mv64x60_get_type(struct mv64x60_handle *bh) | ||
821 | { | ||
822 | struct pci_controller hose; | ||
823 | u16 val; | ||
824 | u8 save_exclude; | ||
825 | |||
826 | memset(&hose, 0, sizeof(hose)); | ||
827 | setup_indirect_pci_nomap(&hose, bh->v_base + MV64x60_PCI0_CONFIG_ADDR, | ||
828 | bh->v_base + MV64x60_PCI0_CONFIG_DATA); | ||
829 | |||
830 | save_exclude = mv64x60_pci_exclude_bridge; | ||
831 | mv64x60_pci_exclude_bridge = 0; | ||
832 | /* Sanity check of bridge's Vendor ID */ | ||
833 | early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID, &val); | ||
834 | |||
835 | if (val != PCI_VENDOR_ID_MARVELL) { | ||
836 | mv64x60_pci_exclude_bridge = save_exclude; | ||
837 | return -1; | ||
838 | } | ||
839 | |||
840 | /* Get the revision of the chip */ | ||
841 | early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_CLASS_REVISION, | ||
842 | &val); | ||
843 | bh->rev = (u32)(val & 0xff); | ||
844 | |||
845 | /* Figure out the type of Marvell bridge it is */ | ||
846 | early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_DEVICE_ID, &val); | ||
847 | mv64x60_pci_exclude_bridge = save_exclude; | ||
848 | |||
849 | switch (val) { | ||
850 | case PCI_DEVICE_ID_MARVELL_GT64260: | ||
851 | switch (bh->rev) { | ||
852 | case GT64260_REV_A: | ||
853 | bh->type = MV64x60_TYPE_GT64260A; | ||
854 | break; | ||
855 | |||
856 | default: | ||
857 | printk(KERN_WARNING "Unsupported GT64260 rev %04x\n", | ||
858 | bh->rev); | ||
859 | /* Assume its similar to a 'B' rev and fallthru */ | ||
860 | case GT64260_REV_B: | ||
861 | bh->type = MV64x60_TYPE_GT64260B; | ||
862 | break; | ||
863 | } | ||
864 | break; | ||
865 | |||
866 | case PCI_DEVICE_ID_MARVELL_MV64360: | ||
867 | /* Marvell won't tell me how to distinguish a 64361 & 64362 */ | ||
868 | bh->type = MV64x60_TYPE_MV64360; | ||
869 | break; | ||
870 | |||
871 | case PCI_DEVICE_ID_MARVELL_MV64460: | ||
872 | bh->type = MV64x60_TYPE_MV64460; | ||
873 | break; | ||
874 | |||
875 | default: | ||
876 | printk(KERN_ERR "Unknown Marvell bridge type %04x\n", val); | ||
877 | return -1; | ||
878 | } | ||
879 | |||
880 | /* Hang onto bridge type & rev for PIC code */ | ||
881 | mv64x60_bridge_type = bh->type; | ||
882 | mv64x60_bridge_rev = bh->rev; | ||
883 | |||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | /* | ||
888 | * mv64x60_setup_for_chip() | ||
889 | * | ||
890 | * Set 'bh' to use the proper set of routine for the bridge chip that we have. | ||
891 | */ | ||
892 | int __init | ||
893 | mv64x60_setup_for_chip(struct mv64x60_handle *bh) | ||
894 | { | ||
895 | int rc = 0; | ||
896 | |||
897 | /* Set up chip-specific info based on the chip/bridge type */ | ||
898 | switch(bh->type) { | ||
899 | case MV64x60_TYPE_GT64260A: | ||
900 | bh->ci = >64260a_ci; | ||
901 | break; | ||
902 | |||
903 | case MV64x60_TYPE_GT64260B: | ||
904 | bh->ci = >64260b_ci; | ||
905 | break; | ||
906 | |||
907 | case MV64x60_TYPE_MV64360: | ||
908 | bh->ci = &mv64360_ci; | ||
909 | break; | ||
910 | |||
911 | case MV64x60_TYPE_MV64460: | ||
912 | bh->ci = &mv64460_ci; | ||
913 | break; | ||
914 | |||
915 | case MV64x60_TYPE_INVALID: | ||
916 | default: | ||
917 | if (ppc_md.progress) | ||
918 | ppc_md.progress("mv64x60: Unsupported bridge", 0x0); | ||
919 | printk(KERN_ERR "mv64x60: Unsupported bridge\n"); | ||
920 | rc = -1; | ||
921 | } | ||
922 | |||
923 | return rc; | ||
924 | } | ||
925 | |||
926 | /* | ||
927 | * mv64x60_get_bridge_vbase() | ||
928 | * | ||
929 | * Return the virtual address of the bridge's registers. | ||
930 | */ | ||
931 | void * | ||
932 | mv64x60_get_bridge_vbase(void) | ||
933 | { | ||
934 | return mv64x60_bridge_vbase; | ||
935 | } | ||
936 | |||
937 | /* | ||
938 | * mv64x60_get_bridge_type() | ||
939 | * | ||
940 | * Return the type of bridge on the platform. | ||
941 | */ | ||
942 | u32 | ||
943 | mv64x60_get_bridge_type(void) | ||
944 | { | ||
945 | return mv64x60_bridge_type; | ||
946 | } | ||
947 | |||
948 | /* | ||
949 | * mv64x60_get_bridge_rev() | ||
950 | * | ||
951 | * Return the revision of the bridge on the platform. | ||
952 | */ | ||
953 | u32 | ||
954 | mv64x60_get_bridge_rev(void) | ||
955 | { | ||
956 | return mv64x60_bridge_rev; | ||
957 | } | ||
958 | |||
959 | /* | ||
960 | ***************************************************************************** | ||
961 | * | ||
962 | * System Memory Window Related Routines | ||
963 | * | ||
964 | ***************************************************************************** | ||
965 | */ | ||
966 | /* | ||
967 | * mv64x60_get_mem_size() | ||
968 | * | ||
969 | * Calculate the amount of memory that the memory controller is set up for. | ||
970 | * This should only be used by board-specific code if there is no other | ||
971 | * way to determine the amount of memory in the system. | ||
972 | */ | ||
973 | u32 __init | ||
974 | mv64x60_get_mem_size(u32 bridge_base, u32 chip_type) | ||
975 | { | ||
976 | struct mv64x60_handle bh; | ||
977 | u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]; | ||
978 | u32 rc = 0; | ||
979 | |||
980 | memset(&bh, 0, sizeof(bh)); | ||
981 | |||
982 | bh.type = chip_type; | ||
983 | bh.v_base = (void *)bridge_base; | ||
984 | |||
985 | if (!mv64x60_setup_for_chip(&bh)) { | ||
986 | mv64x60_get_mem_windows(&bh, mem_windows); | ||
987 | rc = mv64x60_calc_mem_size(&bh, mem_windows); | ||
988 | } | ||
989 | |||
990 | return rc; | ||
991 | } | ||
992 | |||
993 | /* | ||
994 | * mv64x60_get_mem_windows() | ||
995 | * | ||
996 | * Get the values in the memory controller & return in the 'mem_windows' array. | ||
997 | */ | ||
998 | void __init | ||
999 | mv64x60_get_mem_windows(struct mv64x60_handle *bh, | ||
1000 | u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) | ||
1001 | { | ||
1002 | u32 i, win; | ||
1003 | |||
1004 | for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) | ||
1005 | if (bh->ci->is_enabled_32bit(bh, win)) | ||
1006 | mv64x60_get_32bit_window(bh, win, | ||
1007 | &mem_windows[i][0], &mem_windows[i][1]); | ||
1008 | else { | ||
1009 | mem_windows[i][0] = 0; | ||
1010 | mem_windows[i][1] = 0; | ||
1011 | } | ||
1012 | |||
1013 | return; | ||
1014 | } | ||
1015 | |||
1016 | /* | ||
1017 | * mv64x60_calc_mem_size() | ||
1018 | * | ||
1019 | * Using the memory controller register values in 'mem_windows', determine | ||
1020 | * how much memory it is set up for. | ||
1021 | */ | ||
1022 | u32 __init | ||
1023 | mv64x60_calc_mem_size(struct mv64x60_handle *bh, | ||
1024 | u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) | ||
1025 | { | ||
1026 | u32 i, total = 0; | ||
1027 | |||
1028 | for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++) | ||
1029 | total += mem_windows[i][1]; | ||
1030 | |||
1031 | return total; | ||
1032 | } | ||
1033 | |||
1034 | /* | ||
1035 | ***************************************************************************** | ||
1036 | * | ||
1037 | * CPU->System MEM, PCI Config Routines | ||
1038 | * | ||
1039 | ***************************************************************************** | ||
1040 | */ | ||
1041 | /* | ||
1042 | * mv64x60_config_cpu2mem_windows() | ||
1043 | * | ||
1044 | * Configure CPU->Memory windows on the bridge. | ||
1045 | */ | ||
1046 | static u32 prot_tab[] __initdata = { | ||
1047 | MV64x60_CPU_PROT_0_WIN, MV64x60_CPU_PROT_1_WIN, | ||
1048 | MV64x60_CPU_PROT_2_WIN, MV64x60_CPU_PROT_3_WIN | ||
1049 | }; | ||
1050 | |||
1051 | static u32 cpu_snoop_tab[] __initdata = { | ||
1052 | MV64x60_CPU_SNOOP_0_WIN, MV64x60_CPU_SNOOP_1_WIN, | ||
1053 | MV64x60_CPU_SNOOP_2_WIN, MV64x60_CPU_SNOOP_3_WIN | ||
1054 | }; | ||
1055 | |||
1056 | void __init | ||
1057 | mv64x60_config_cpu2mem_windows(struct mv64x60_handle *bh, | ||
1058 | struct mv64x60_setup_info *si, | ||
1059 | u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) | ||
1060 | { | ||
1061 | u32 i, win; | ||
1062 | |||
1063 | /* Set CPU protection & snoop windows */ | ||
1064 | for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) | ||
1065 | if (bh->ci->is_enabled_32bit(bh, win)) { | ||
1066 | mv64x60_set_32bit_window(bh, prot_tab[i], | ||
1067 | mem_windows[i][0], mem_windows[i][1], | ||
1068 | si->cpu_prot_options[i]); | ||
1069 | bh->ci->enable_window_32bit(bh, prot_tab[i]); | ||
1070 | |||
1071 | if (bh->ci->window_tab_32bit[cpu_snoop_tab[i]]. | ||
1072 | base_reg != 0) { | ||
1073 | mv64x60_set_32bit_window(bh, cpu_snoop_tab[i], | ||
1074 | mem_windows[i][0], mem_windows[i][1], | ||
1075 | si->cpu_snoop_options[i]); | ||
1076 | bh->ci->enable_window_32bit(bh, | ||
1077 | cpu_snoop_tab[i]); | ||
1078 | } | ||
1079 | |||
1080 | } | ||
1081 | |||
1082 | return; | ||
1083 | } | ||
1084 | |||
1085 | /* | ||
1086 | * mv64x60_config_cpu2pci_windows() | ||
1087 | * | ||
1088 | * Configure the CPU->PCI windows for one of the PCI buses. | ||
1089 | */ | ||
1090 | static u32 win_tab[2][4] __initdata = { | ||
1091 | { MV64x60_CPU2PCI0_IO_WIN, MV64x60_CPU2PCI0_MEM_0_WIN, | ||
1092 | MV64x60_CPU2PCI0_MEM_1_WIN, MV64x60_CPU2PCI0_MEM_2_WIN }, | ||
1093 | { MV64x60_CPU2PCI1_IO_WIN, MV64x60_CPU2PCI1_MEM_0_WIN, | ||
1094 | MV64x60_CPU2PCI1_MEM_1_WIN, MV64x60_CPU2PCI1_MEM_2_WIN }, | ||
1095 | }; | ||
1096 | |||
1097 | static u32 remap_tab[2][4] __initdata = { | ||
1098 | { MV64x60_CPU2PCI0_IO_REMAP_WIN, MV64x60_CPU2PCI0_MEM_0_REMAP_WIN, | ||
1099 | MV64x60_CPU2PCI0_MEM_1_REMAP_WIN, MV64x60_CPU2PCI0_MEM_2_REMAP_WIN }, | ||
1100 | { MV64x60_CPU2PCI1_IO_REMAP_WIN, MV64x60_CPU2PCI1_MEM_0_REMAP_WIN, | ||
1101 | MV64x60_CPU2PCI1_MEM_1_REMAP_WIN, MV64x60_CPU2PCI1_MEM_2_REMAP_WIN } | ||
1102 | }; | ||
1103 | |||
1104 | void __init | ||
1105 | mv64x60_config_cpu2pci_windows(struct mv64x60_handle *bh, | ||
1106 | struct mv64x60_pci_info *pi, u32 bus) | ||
1107 | { | ||
1108 | int i; | ||
1109 | |||
1110 | if (pi->pci_io.size > 0) { | ||
1111 | mv64x60_set_32bit_window(bh, win_tab[bus][0], | ||
1112 | pi->pci_io.cpu_base, pi->pci_io.size, pi->pci_io.swap); | ||
1113 | mv64x60_set_32bit_window(bh, remap_tab[bus][0], | ||
1114 | pi->pci_io.pci_base_lo, 0, 0); | ||
1115 | bh->ci->enable_window_32bit(bh, win_tab[bus][0]); | ||
1116 | } | ||
1117 | else /* Actually, the window should already be disabled */ | ||
1118 | bh->ci->disable_window_32bit(bh, win_tab[bus][0]); | ||
1119 | |||
1120 | for (i=0; i<3; i++) | ||
1121 | if (pi->pci_mem[i].size > 0) { | ||
1122 | mv64x60_set_32bit_window(bh, win_tab[bus][i+1], | ||
1123 | pi->pci_mem[i].cpu_base, pi->pci_mem[i].size, | ||
1124 | pi->pci_mem[i].swap); | ||
1125 | mv64x60_set_64bit_window(bh, remap_tab[bus][i+1], | ||
1126 | pi->pci_mem[i].pci_base_hi, | ||
1127 | pi->pci_mem[i].pci_base_lo, 0, 0); | ||
1128 | bh->ci->enable_window_32bit(bh, win_tab[bus][i+1]); | ||
1129 | } | ||
1130 | else /* Actually, the window should already be disabled */ | ||
1131 | bh->ci->disable_window_32bit(bh, win_tab[bus][i+1]); | ||
1132 | |||
1133 | return; | ||
1134 | } | ||
1135 | |||
1136 | /* | ||
1137 | ***************************************************************************** | ||
1138 | * | ||
1139 | * PCI->System MEM Config Routines | ||
1140 | * | ||
1141 | ***************************************************************************** | ||
1142 | */ | ||
1143 | /* | ||
1144 | * mv64x60_config_pci2mem_windows() | ||
1145 | * | ||
1146 | * Configure the PCI->Memory windows on the bridge. | ||
1147 | */ | ||
1148 | static u32 pci_acc_tab[2][4] __initdata = { | ||
1149 | { MV64x60_PCI02MEM_ACC_CNTL_0_WIN, MV64x60_PCI02MEM_ACC_CNTL_1_WIN, | ||
1150 | MV64x60_PCI02MEM_ACC_CNTL_2_WIN, MV64x60_PCI02MEM_ACC_CNTL_3_WIN }, | ||
1151 | { MV64x60_PCI12MEM_ACC_CNTL_0_WIN, MV64x60_PCI12MEM_ACC_CNTL_1_WIN, | ||
1152 | MV64x60_PCI12MEM_ACC_CNTL_2_WIN, MV64x60_PCI12MEM_ACC_CNTL_3_WIN } | ||
1153 | }; | ||
1154 | |||
1155 | static u32 pci_snoop_tab[2][4] __initdata = { | ||
1156 | { MV64x60_PCI02MEM_SNOOP_0_WIN, MV64x60_PCI02MEM_SNOOP_1_WIN, | ||
1157 | MV64x60_PCI02MEM_SNOOP_2_WIN, MV64x60_PCI02MEM_SNOOP_3_WIN }, | ||
1158 | { MV64x60_PCI12MEM_SNOOP_0_WIN, MV64x60_PCI12MEM_SNOOP_1_WIN, | ||
1159 | MV64x60_PCI12MEM_SNOOP_2_WIN, MV64x60_PCI12MEM_SNOOP_3_WIN } | ||
1160 | }; | ||
1161 | |||
1162 | static u32 pci_size_tab[2][4] __initdata = { | ||
1163 | { MV64x60_PCI0_MEM_0_SIZE, MV64x60_PCI0_MEM_1_SIZE, | ||
1164 | MV64x60_PCI0_MEM_2_SIZE, MV64x60_PCI0_MEM_3_SIZE }, | ||
1165 | { MV64x60_PCI1_MEM_0_SIZE, MV64x60_PCI1_MEM_1_SIZE, | ||
1166 | MV64x60_PCI1_MEM_2_SIZE, MV64x60_PCI1_MEM_3_SIZE } | ||
1167 | }; | ||
1168 | |||
1169 | void __init | ||
1170 | mv64x60_config_pci2mem_windows(struct mv64x60_handle *bh, | ||
1171 | struct pci_controller *hose, struct mv64x60_pci_info *pi, | ||
1172 | u32 bus, u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) | ||
1173 | { | ||
1174 | u32 i, win; | ||
1175 | |||
1176 | /* | ||
1177 | * Set the access control, snoop, BAR size, and window base addresses. | ||
1178 | * PCI->MEM windows base addresses will match exactly what the | ||
1179 | * CPU->MEM windows are. | ||
1180 | */ | ||
1181 | for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) | ||
1182 | if (bh->ci->is_enabled_32bit(bh, win)) { | ||
1183 | mv64x60_set_64bit_window(bh, | ||
1184 | pci_acc_tab[bus][i], 0, | ||
1185 | mem_windows[i][0], mem_windows[i][1], | ||
1186 | pi->acc_cntl_options[i]); | ||
1187 | bh->ci->enable_window_64bit(bh, pci_acc_tab[bus][i]); | ||
1188 | |||
1189 | if (bh->ci->window_tab_64bit[ | ||
1190 | pci_snoop_tab[bus][i]].base_lo_reg != 0) { | ||
1191 | |||
1192 | mv64x60_set_64bit_window(bh, | ||
1193 | pci_snoop_tab[bus][i], 0, | ||
1194 | mem_windows[i][0], mem_windows[i][1], | ||
1195 | pi->snoop_options[i]); | ||
1196 | bh->ci->enable_window_64bit(bh, | ||
1197 | pci_snoop_tab[bus][i]); | ||
1198 | } | ||
1199 | |||
1200 | bh->ci->set_pci2mem_window(hose, bus, i, | ||
1201 | mem_windows[i][0]); | ||
1202 | mv64x60_write(bh, pci_size_tab[bus][i], | ||
1203 | mv64x60_mask(mem_windows[i][1] - 1, 20)); | ||
1204 | |||
1205 | /* Enable the window */ | ||
1206 | mv64x60_clr_bits(bh, ((bus == 0) ? | ||
1207 | MV64x60_PCI0_BAR_ENABLE : | ||
1208 | MV64x60_PCI1_BAR_ENABLE), (1 << i)); | ||
1209 | } | ||
1210 | |||
1211 | return; | ||
1212 | } | ||
1213 | |||
1214 | /* | ||
1215 | ***************************************************************************** | ||
1216 | * | ||
1217 | * Hose & Resource Alloc/Init Routines | ||
1218 | * | ||
1219 | ***************************************************************************** | ||
1220 | */ | ||
1221 | /* | ||
1222 | * mv64x60_alloc_hoses() | ||
1223 | * | ||
1224 | * Allocate the PCI hose structures for the bridge's PCI buses. | ||
1225 | */ | ||
1226 | void __init | ||
1227 | mv64x60_alloc_hose(struct mv64x60_handle *bh, u32 cfg_addr, u32 cfg_data, | ||
1228 | struct pci_controller **hose) | ||
1229 | { | ||
1230 | *hose = pcibios_alloc_controller(); | ||
1231 | setup_indirect_pci_nomap(*hose, bh->v_base + cfg_addr, | ||
1232 | bh->v_base + cfg_data); | ||
1233 | return; | ||
1234 | } | ||
1235 | |||
1236 | /* | ||
1237 | * mv64x60_config_resources() | ||
1238 | * | ||
1239 | * Calculate the offsets, etc. for the hose structures to reflect all of | ||
1240 | * the address remapping that happens as you go from CPU->PCI and PCI->MEM. | ||
1241 | */ | ||
1242 | void __init | ||
1243 | mv64x60_config_resources(struct pci_controller *hose, | ||
1244 | struct mv64x60_pci_info *pi, u32 io_base) | ||
1245 | { | ||
1246 | int i; | ||
1247 | /* 2 hoses; 4 resources/hose; string <= 64 bytes */ | ||
1248 | static char s[2][4][64]; | ||
1249 | |||
1250 | if (pi->pci_io.size != 0) { | ||
1251 | sprintf(s[hose->index][0], "PCI hose %d I/O Space", | ||
1252 | hose->index); | ||
1253 | pci_init_resource(&hose->io_resource, io_base - isa_io_base, | ||
1254 | io_base - isa_io_base + pi->pci_io.size - 1, | ||
1255 | IORESOURCE_IO, s[hose->index][0]); | ||
1256 | hose->io_space.start = pi->pci_io.pci_base_lo; | ||
1257 | hose->io_space.end = pi->pci_io.pci_base_lo + pi->pci_io.size-1; | ||
1258 | hose->io_base_phys = pi->pci_io.cpu_base; | ||
1259 | hose->io_base_virt = (void *)isa_io_base; | ||
1260 | } | ||
1261 | |||
1262 | for (i=0; i<3; i++) | ||
1263 | if (pi->pci_mem[i].size != 0) { | ||
1264 | sprintf(s[hose->index][i+1], "PCI hose %d MEM Space %d", | ||
1265 | hose->index, i); | ||
1266 | pci_init_resource(&hose->mem_resources[i], | ||
1267 | pi->pci_mem[i].cpu_base, | ||
1268 | pi->pci_mem[i].cpu_base + pi->pci_mem[i].size-1, | ||
1269 | IORESOURCE_MEM, s[hose->index][i+1]); | ||
1270 | } | ||
1271 | |||
1272 | hose->mem_space.end = pi->pci_mem[0].pci_base_lo + | ||
1273 | pi->pci_mem[0].size - 1; | ||
1274 | hose->pci_mem_offset = pi->pci_mem[0].cpu_base - | ||
1275 | pi->pci_mem[0].pci_base_lo; | ||
1276 | return; | ||
1277 | } | ||
1278 | |||
1279 | /* | ||
1280 | * mv64x60_config_pci_params() | ||
1281 | * | ||
1282 | * Configure a hose's PCI config space parameters. | ||
1283 | */ | ||
1284 | void __init | ||
1285 | mv64x60_config_pci_params(struct pci_controller *hose, | ||
1286 | struct mv64x60_pci_info *pi) | ||
1287 | { | ||
1288 | u32 devfn; | ||
1289 | u16 u16_val; | ||
1290 | u8 save_exclude; | ||
1291 | |||
1292 | devfn = PCI_DEVFN(0,0); | ||
1293 | |||
1294 | save_exclude = mv64x60_pci_exclude_bridge; | ||
1295 | mv64x60_pci_exclude_bridge = 0; | ||
1296 | |||
1297 | /* Set class code to indicate host bridge */ | ||
1298 | u16_val = PCI_CLASS_BRIDGE_HOST; /* 0x0600 (host bridge) */ | ||
1299 | early_write_config_word(hose, 0, devfn, PCI_CLASS_DEVICE, u16_val); | ||
1300 | |||
1301 | /* Enable bridge to be PCI master & respond to PCI MEM cycles */ | ||
1302 | early_read_config_word(hose, 0, devfn, PCI_COMMAND, &u16_val); | ||
1303 | u16_val &= ~(PCI_COMMAND_IO | PCI_COMMAND_INVALIDATE | | ||
1304 | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK); | ||
1305 | u16_val |= pi->pci_cmd_bits | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; | ||
1306 | early_write_config_word(hose, 0, devfn, PCI_COMMAND, u16_val); | ||
1307 | |||
1308 | /* Set latency timer, cache line size, clear BIST */ | ||
1309 | u16_val = (pi->latency_timer << 8) | (L1_CACHE_LINE_SIZE >> 2); | ||
1310 | early_write_config_word(hose, 0, devfn, PCI_CACHE_LINE_SIZE, u16_val); | ||
1311 | |||
1312 | mv64x60_pci_exclude_bridge = save_exclude; | ||
1313 | return; | ||
1314 | } | ||
1315 | |||
1316 | /* | ||
1317 | ***************************************************************************** | ||
1318 | * | ||
1319 | * PCI Related Routine | ||
1320 | * | ||
1321 | ***************************************************************************** | ||
1322 | */ | ||
1323 | /* | ||
1324 | * mv64x60_set_bus() | ||
1325 | * | ||
1326 | * Set the bus number for the hose directly under the bridge. | ||
1327 | */ | ||
1328 | void __init | ||
1329 | mv64x60_set_bus(struct mv64x60_handle *bh, u32 bus, u32 child_bus) | ||
1330 | { | ||
1331 | struct pci_controller *hose; | ||
1332 | u32 pci_mode, p2p_cfg, pci_cfg_offset, val; | ||
1333 | u8 save_exclude; | ||
1334 | |||
1335 | if (bus == 0) { | ||
1336 | pci_mode = bh->pci_mode_a; | ||
1337 | p2p_cfg = MV64x60_PCI0_P2P_CONFIG; | ||
1338 | pci_cfg_offset = 0x64; | ||
1339 | hose = bh->hose_a; | ||
1340 | } | ||
1341 | else { | ||
1342 | pci_mode = bh->pci_mode_b; | ||
1343 | p2p_cfg = MV64x60_PCI1_P2P_CONFIG; | ||
1344 | pci_cfg_offset = 0xe4; | ||
1345 | hose = bh->hose_b; | ||
1346 | } | ||
1347 | |||
1348 | child_bus &= 0xff; | ||
1349 | val = mv64x60_read(bh, p2p_cfg); | ||
1350 | |||
1351 | if (pci_mode == MV64x60_PCIMODE_CONVENTIONAL) { | ||
1352 | val &= 0xe0000000; /* Force dev num to 0, turn off P2P bridge */ | ||
1353 | val |= (child_bus << 16) | 0xff; | ||
1354 | mv64x60_write(bh, p2p_cfg, val); | ||
1355 | (void)mv64x60_read(bh, p2p_cfg); /* Flush FIFO */ | ||
1356 | } | ||
1357 | else { /* PCI-X */ | ||
1358 | /* | ||
1359 | * Need to use the current bus/dev number (that's in the | ||
1360 | * P2P CONFIG reg) to access the bridge's pci config space. | ||
1361 | */ | ||
1362 | save_exclude = mv64x60_pci_exclude_bridge; | ||
1363 | mv64x60_pci_exclude_bridge = 0; | ||
1364 | early_write_config_dword(hose, (val & 0x00ff0000) >> 16, | ||
1365 | PCI_DEVFN(((val & 0x1f000000) >> 24), 0), | ||
1366 | pci_cfg_offset, child_bus << 8); | ||
1367 | mv64x60_pci_exclude_bridge = save_exclude; | ||
1368 | } | ||
1369 | |||
1370 | return; | ||
1371 | } | ||
1372 | |||
1373 | /* | ||
1374 | * mv64x60_pci_exclude_device() | ||
1375 | * | ||
1376 | * This routine is used to make the bridge not appear when the | ||
1377 | * PCI subsystem is accessing PCI devices (in PCI config space). | ||
1378 | */ | ||
1379 | int | ||
1380 | mv64x60_pci_exclude_device(u8 bus, u8 devfn) | ||
1381 | { | ||
1382 | struct pci_controller *hose; | ||
1383 | |||
1384 | hose = pci_bus_to_hose(bus); | ||
1385 | |||
1386 | /* Skip slot 0 on both hoses */ | ||
1387 | if ((mv64x60_pci_exclude_bridge == 1) && (PCI_SLOT(devfn) == 0) && | ||
1388 | (hose->first_busno == bus)) | ||
1389 | |||
1390 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
1391 | else | ||
1392 | return PCIBIOS_SUCCESSFUL; | ||
1393 | } /* mv64x60_pci_exclude_device() */ | ||
1394 | |||
1395 | /* | ||
1396 | ***************************************************************************** | ||
1397 | * | ||
1398 | * Platform Device Routines | ||
1399 | * | ||
1400 | ***************************************************************************** | ||
1401 | */ | ||
1402 | |||
1403 | /* | ||
1404 | * mv64x60_pd_fixup() | ||
1405 | * | ||
1406 | * Need to add the base addr of where the bridge's regs are mapped in the | ||
1407 | * physical addr space so drivers can ioremap() them. | ||
1408 | */ | ||
1409 | void __init | ||
1410 | mv64x60_pd_fixup(struct mv64x60_handle *bh, struct platform_device *pd_devs[], | ||
1411 | u32 entries) | ||
1412 | { | ||
1413 | struct resource *r; | ||
1414 | u32 i, j; | ||
1415 | |||
1416 | for (i=0; i<entries; i++) { | ||
1417 | j = 0; | ||
1418 | |||
1419 | while ((r = platform_get_resource(pd_devs[i],IORESOURCE_MEM,j)) | ||
1420 | != NULL) { | ||
1421 | |||
1422 | r->start += bh->p_base; | ||
1423 | r->end += bh->p_base; | ||
1424 | j++; | ||
1425 | } | ||
1426 | } | ||
1427 | |||
1428 | return; | ||
1429 | } | ||
1430 | |||
1431 | /* | ||
1432 | * mv64x60_add_pds() | ||
1433 | * | ||
1434 | * Add the mv64x60 platform devices to the list of platform devices. | ||
1435 | */ | ||
1436 | static int __init | ||
1437 | mv64x60_add_pds(void) | ||
1438 | { | ||
1439 | return platform_add_devices(mv64x60_pd_devs, | ||
1440 | ARRAY_SIZE(mv64x60_pd_devs)); | ||
1441 | } | ||
1442 | arch_initcall(mv64x60_add_pds); | ||
1443 | |||
1444 | /* | ||
1445 | ***************************************************************************** | ||
1446 | * | ||
1447 | * GT64260-Specific Routines | ||
1448 | * | ||
1449 | ***************************************************************************** | ||
1450 | */ | ||
1451 | /* | ||
1452 | * gt64260_translate_size() | ||
1453 | * | ||
1454 | * On the GT64260, the size register is really the "top" address of the window. | ||
1455 | */ | ||
1456 | static u32 __init | ||
1457 | gt64260_translate_size(u32 base, u32 size, u32 num_bits) | ||
1458 | { | ||
1459 | return base + mv64x60_mask(size - 1, num_bits); | ||
1460 | } | ||
1461 | |||
1462 | /* | ||
1463 | * gt64260_untranslate_size() | ||
1464 | * | ||
1465 | * Translate the top address of a window into a window size. | ||
1466 | */ | ||
1467 | static u32 __init | ||
1468 | gt64260_untranslate_size(u32 base, u32 size, u32 num_bits) | ||
1469 | { | ||
1470 | if (size >= base) | ||
1471 | size = size - base + (1 << (32 - num_bits)); | ||
1472 | else | ||
1473 | size = 0; | ||
1474 | |||
1475 | return size; | ||
1476 | } | ||
1477 | |||
1478 | /* | ||
1479 | * gt64260_set_pci2mem_window() | ||
1480 | * | ||
1481 | * The PCI->MEM window registers are actually in PCI config space so need | ||
1482 | * to set them by setting the correct config space BARs. | ||
1483 | */ | ||
1484 | static u32 gt64260_reg_addrs[2][4] __initdata = { | ||
1485 | { 0x10, 0x14, 0x18, 0x1c }, { 0x90, 0x94, 0x98, 0x9c } | ||
1486 | }; | ||
1487 | |||
1488 | static void __init | ||
1489 | gt64260_set_pci2mem_window(struct pci_controller *hose, u32 bus, u32 window, | ||
1490 | u32 base) | ||
1491 | { | ||
1492 | u8 save_exclude; | ||
1493 | |||
1494 | pr_debug("set pci->mem window: %d, hose: %d, base: 0x%x\n", window, | ||
1495 | hose->index, base); | ||
1496 | |||
1497 | save_exclude = mv64x60_pci_exclude_bridge; | ||
1498 | mv64x60_pci_exclude_bridge = 0; | ||
1499 | early_write_config_dword(hose, 0, PCI_DEVFN(0, 0), | ||
1500 | gt64260_reg_addrs[bus][window], mv64x60_mask(base, 20) | 0x8); | ||
1501 | mv64x60_pci_exclude_bridge = save_exclude; | ||
1502 | |||
1503 | return; | ||
1504 | } | ||
1505 | |||
1506 | /* | ||
1507 | * gt64260_set_pci2regs_window() | ||
1508 | * | ||
1509 | * Set where the bridge's registers appear in PCI MEM space. | ||
1510 | */ | ||
1511 | static u32 gt64260_offset[2] __initdata = {0x20, 0xa0}; | ||
1512 | |||
1513 | static void __init | ||
1514 | gt64260_set_pci2regs_window(struct mv64x60_handle *bh, | ||
1515 | struct pci_controller *hose, u32 bus, u32 base) | ||
1516 | { | ||
1517 | u8 save_exclude; | ||
1518 | |||
1519 | pr_debug("set pci->internal regs hose: %d, base: 0x%x\n", hose->index, | ||
1520 | base); | ||
1521 | |||
1522 | save_exclude = mv64x60_pci_exclude_bridge; | ||
1523 | mv64x60_pci_exclude_bridge = 0; | ||
1524 | early_write_config_dword(hose, 0, PCI_DEVFN(0,0), gt64260_offset[bus], | ||
1525 | (base << 16)); | ||
1526 | mv64x60_pci_exclude_bridge = save_exclude; | ||
1527 | |||
1528 | return; | ||
1529 | } | ||
1530 | |||
1531 | /* | ||
1532 | * gt64260_is_enabled_32bit() | ||
1533 | * | ||
1534 | * On a GT64260, a window is enabled iff its top address is >= to its base | ||
1535 | * address. | ||
1536 | */ | ||
1537 | static u32 __init | ||
1538 | gt64260_is_enabled_32bit(struct mv64x60_handle *bh, u32 window) | ||
1539 | { | ||
1540 | u32 rc = 0; | ||
1541 | |||
1542 | if ((gt64260_32bit_windows[window].base_reg != 0) && | ||
1543 | (gt64260_32bit_windows[window].size_reg != 0) && | ||
1544 | ((mv64x60_read(bh, gt64260_32bit_windows[window].size_reg) & | ||
1545 | ((1 << gt64260_32bit_windows[window].size_bits) - 1)) >= | ||
1546 | (mv64x60_read(bh, gt64260_32bit_windows[window].base_reg) & | ||
1547 | ((1 << gt64260_32bit_windows[window].base_bits) - 1)))) | ||
1548 | |||
1549 | rc = 1; | ||
1550 | |||
1551 | return rc; | ||
1552 | } | ||
1553 | |||
1554 | /* | ||
1555 | * gt64260_enable_window_32bit() | ||
1556 | * | ||
1557 | * On the GT64260, a window is enabled iff the top address is >= to the base | ||
1558 | * address of the window. Since the window has already been configured by | ||
1559 | * the time this routine is called, we have nothing to do here. | ||
1560 | */ | ||
1561 | static void __init | ||
1562 | gt64260_enable_window_32bit(struct mv64x60_handle *bh, u32 window) | ||
1563 | { | ||
1564 | pr_debug("enable 32bit window: %d\n", window); | ||
1565 | return; | ||
1566 | } | ||
1567 | |||
1568 | /* | ||
1569 | * gt64260_disable_window_32bit() | ||
1570 | * | ||
1571 | * On a GT64260, you disable a window by setting its top address to be less | ||
1572 | * than its base address. | ||
1573 | */ | ||
1574 | static void __init | ||
1575 | gt64260_disable_window_32bit(struct mv64x60_handle *bh, u32 window) | ||
1576 | { | ||
1577 | pr_debug("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", | ||
1578 | window, gt64260_32bit_windows[window].base_reg, | ||
1579 | gt64260_32bit_windows[window].size_reg); | ||
1580 | |||
1581 | if ((gt64260_32bit_windows[window].base_reg != 0) && | ||
1582 | (gt64260_32bit_windows[window].size_reg != 0)) { | ||
1583 | |||
1584 | /* To disable, make bottom reg higher than top reg */ | ||
1585 | mv64x60_write(bh, gt64260_32bit_windows[window].base_reg,0xfff); | ||
1586 | mv64x60_write(bh, gt64260_32bit_windows[window].size_reg, 0); | ||
1587 | } | ||
1588 | |||
1589 | return; | ||
1590 | } | ||
1591 | |||
1592 | /* | ||
1593 | * gt64260_enable_window_64bit() | ||
1594 | * | ||
1595 | * On the GT64260, a window is enabled iff the top address is >= to the base | ||
1596 | * address of the window. Since the window has already been configured by | ||
1597 | * the time this routine is called, we have nothing to do here. | ||
1598 | */ | ||
1599 | static void __init | ||
1600 | gt64260_enable_window_64bit(struct mv64x60_handle *bh, u32 window) | ||
1601 | { | ||
1602 | pr_debug("enable 64bit window: %d\n", window); | ||
1603 | return; /* Enabled when window configured (i.e., when top >= base) */ | ||
1604 | } | ||
1605 | |||
1606 | /* | ||
1607 | * gt64260_disable_window_64bit() | ||
1608 | * | ||
1609 | * On a GT64260, you disable a window by setting its top address to be less | ||
1610 | * than its base address. | ||
1611 | */ | ||
1612 | static void __init | ||
1613 | gt64260_disable_window_64bit(struct mv64x60_handle *bh, u32 window) | ||
1614 | { | ||
1615 | pr_debug("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", | ||
1616 | window, gt64260_64bit_windows[window].base_lo_reg, | ||
1617 | gt64260_64bit_windows[window].size_reg); | ||
1618 | |||
1619 | if ((gt64260_64bit_windows[window].base_lo_reg != 0) && | ||
1620 | (gt64260_64bit_windows[window].size_reg != 0)) { | ||
1621 | |||
1622 | /* To disable, make bottom reg higher than top reg */ | ||
1623 | mv64x60_write(bh, gt64260_64bit_windows[window].base_lo_reg, | ||
1624 | 0xfff); | ||
1625 | mv64x60_write(bh, gt64260_64bit_windows[window].base_hi_reg, 0); | ||
1626 | mv64x60_write(bh, gt64260_64bit_windows[window].size_reg, 0); | ||
1627 | } | ||
1628 | |||
1629 | return; | ||
1630 | } | ||
1631 | |||
1632 | /* | ||
1633 | * gt64260_disable_all_windows() | ||
1634 | * | ||
1635 | * The GT64260 has several windows that aren't represented in the table of | ||
1636 | * windows at the top of this file. This routine turns all of them off | ||
1637 | * except for the memory controller windows, of course. | ||
1638 | */ | ||
1639 | static void __init | ||
1640 | gt64260_disable_all_windows(struct mv64x60_handle *bh, | ||
1641 | struct mv64x60_setup_info *si) | ||
1642 | { | ||
1643 | u32 i, preserve; | ||
1644 | |||
1645 | /* Disable 32bit windows (don't disable cpu->mem windows) */ | ||
1646 | for (i=MV64x60_CPU2DEV_0_WIN; i<MV64x60_32BIT_WIN_COUNT; i++) { | ||
1647 | if (i < 32) | ||
1648 | preserve = si->window_preserve_mask_32_lo & (1 << i); | ||
1649 | else | ||
1650 | preserve = si->window_preserve_mask_32_hi & (1<<(i-32)); | ||
1651 | |||
1652 | if (!preserve) | ||
1653 | gt64260_disable_window_32bit(bh, i); | ||
1654 | } | ||
1655 | |||
1656 | /* Disable 64bit windows */ | ||
1657 | for (i=0; i<MV64x60_64BIT_WIN_COUNT; i++) | ||
1658 | if (!(si->window_preserve_mask_64 & (1<<i))) | ||
1659 | gt64260_disable_window_64bit(bh, i); | ||
1660 | |||
1661 | /* Turn off cpu protection windows not in gt64260_32bit_windows[] */ | ||
1662 | mv64x60_write(bh, GT64260_CPU_PROT_BASE_4, 0xfff); | ||
1663 | mv64x60_write(bh, GT64260_CPU_PROT_SIZE_4, 0); | ||
1664 | mv64x60_write(bh, GT64260_CPU_PROT_BASE_5, 0xfff); | ||
1665 | mv64x60_write(bh, GT64260_CPU_PROT_SIZE_5, 0); | ||
1666 | mv64x60_write(bh, GT64260_CPU_PROT_BASE_6, 0xfff); | ||
1667 | mv64x60_write(bh, GT64260_CPU_PROT_SIZE_6, 0); | ||
1668 | mv64x60_write(bh, GT64260_CPU_PROT_BASE_7, 0xfff); | ||
1669 | mv64x60_write(bh, GT64260_CPU_PROT_SIZE_7, 0); | ||
1670 | |||
1671 | /* Turn off PCI->MEM access cntl wins not in gt64260_64bit_windows[] */ | ||
1672 | mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0xfff); | ||
1673 | mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_HI, 0); | ||
1674 | mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_SIZE, 0); | ||
1675 | mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0xfff); | ||
1676 | mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_HI, 0); | ||
1677 | mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_SIZE, 0); | ||
1678 | mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_LO, 0xfff); | ||
1679 | mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_HI, 0); | ||
1680 | mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_SIZE, 0); | ||
1681 | mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_LO, 0xfff); | ||
1682 | mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_HI, 0); | ||
1683 | mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_SIZE, 0); | ||
1684 | |||
1685 | mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0xfff); | ||
1686 | mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_HI, 0); | ||
1687 | mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_SIZE, 0); | ||
1688 | mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0xfff); | ||
1689 | mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_HI, 0); | ||
1690 | mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_SIZE, 0); | ||
1691 | mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_LO, 0xfff); | ||
1692 | mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_HI, 0); | ||
1693 | mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_SIZE, 0); | ||
1694 | mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_LO, 0xfff); | ||
1695 | mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_HI, 0); | ||
1696 | mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_SIZE, 0); | ||
1697 | |||
1698 | /* Disable all PCI-><whatever> windows */ | ||
1699 | mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x07fffdff); | ||
1700 | mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x07fffdff); | ||
1701 | |||
1702 | /* | ||
1703 | * Some firmwares enable a bunch of intr sources | ||
1704 | * for the PCI INT output pins. | ||
1705 | */ | ||
1706 | mv64x60_write(bh, GT64260_IC_CPU_INTR_MASK_LO, 0); | ||
1707 | mv64x60_write(bh, GT64260_IC_CPU_INTR_MASK_HI, 0); | ||
1708 | mv64x60_write(bh, GT64260_IC_PCI0_INTR_MASK_LO, 0); | ||
1709 | mv64x60_write(bh, GT64260_IC_PCI0_INTR_MASK_HI, 0); | ||
1710 | mv64x60_write(bh, GT64260_IC_PCI1_INTR_MASK_LO, 0); | ||
1711 | mv64x60_write(bh, GT64260_IC_PCI1_INTR_MASK_HI, 0); | ||
1712 | mv64x60_write(bh, GT64260_IC_CPU_INT_0_MASK, 0); | ||
1713 | mv64x60_write(bh, GT64260_IC_CPU_INT_1_MASK, 0); | ||
1714 | mv64x60_write(bh, GT64260_IC_CPU_INT_2_MASK, 0); | ||
1715 | mv64x60_write(bh, GT64260_IC_CPU_INT_3_MASK, 0); | ||
1716 | |||
1717 | return; | ||
1718 | } | ||
1719 | |||
1720 | /* | ||
1721 | * gt64260a_chip_specific_init() | ||
1722 | * | ||
1723 | * Implement errata work arounds for the GT64260A. | ||
1724 | */ | ||
1725 | static void __init | ||
1726 | gt64260a_chip_specific_init(struct mv64x60_handle *bh, | ||
1727 | struct mv64x60_setup_info *si) | ||
1728 | { | ||
1729 | #ifdef CONFIG_SERIAL_MPSC | ||
1730 | struct resource *r; | ||
1731 | #endif | ||
1732 | #if !defined(CONFIG_NOT_COHERENT_CACHE) | ||
1733 | u32 val; | ||
1734 | u8 save_exclude; | ||
1735 | #endif | ||
1736 | |||
1737 | if (si->pci_0.enable_bus) | ||
1738 | mv64x60_set_bits(bh, MV64x60_PCI0_CMD, | ||
1739 | ((1<<4) | (1<<5) | (1<<9) | (1<<13))); | ||
1740 | |||
1741 | if (si->pci_1.enable_bus) | ||
1742 | mv64x60_set_bits(bh, MV64x60_PCI1_CMD, | ||
1743 | ((1<<4) | (1<<5) | (1<<9) | (1<<13))); | ||
1744 | |||
1745 | /* | ||
1746 | * Dave Wilhardt found that bit 4 in the PCI Command registers must | ||
1747 | * be set if you are using cache coherency. | ||
1748 | */ | ||
1749 | #if !defined(CONFIG_NOT_COHERENT_CACHE) | ||
1750 | /* Res #MEM-4 -- cpu read buffer to buffer 1 */ | ||
1751 | if ((mv64x60_read(bh, MV64x60_CPU_MODE) & 0xf0) == 0x40) | ||
1752 | mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26)); | ||
1753 | |||
1754 | save_exclude = mv64x60_pci_exclude_bridge; | ||
1755 | mv64x60_pci_exclude_bridge = 0; | ||
1756 | if (si->pci_0.enable_bus) { | ||
1757 | early_read_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), | ||
1758 | PCI_COMMAND, &val); | ||
1759 | val |= PCI_COMMAND_INVALIDATE; | ||
1760 | early_write_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), | ||
1761 | PCI_COMMAND, val); | ||
1762 | } | ||
1763 | |||
1764 | if (si->pci_1.enable_bus) { | ||
1765 | early_read_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), | ||
1766 | PCI_COMMAND, &val); | ||
1767 | val |= PCI_COMMAND_INVALIDATE; | ||
1768 | early_write_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), | ||
1769 | PCI_COMMAND, val); | ||
1770 | } | ||
1771 | mv64x60_pci_exclude_bridge = save_exclude; | ||
1772 | #endif | ||
1773 | |||
1774 | /* Disable buffer/descriptor snooping */ | ||
1775 | mv64x60_clr_bits(bh, 0xf280, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); | ||
1776 | mv64x60_clr_bits(bh, 0xf2c0, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); | ||
1777 | |||
1778 | #ifdef CONFIG_SERIAL_MPSC | ||
1779 | mv64x60_mpsc0_pdata.mirror_regs = 1; | ||
1780 | mv64x60_mpsc0_pdata.cache_mgmt = 1; | ||
1781 | mv64x60_mpsc1_pdata.mirror_regs = 1; | ||
1782 | mv64x60_mpsc1_pdata.cache_mgmt = 1; | ||
1783 | |||
1784 | if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0)) | ||
1785 | != NULL) { | ||
1786 | |||
1787 | r->start = MV64x60_IRQ_SDMA_0; | ||
1788 | r->end = MV64x60_IRQ_SDMA_0; | ||
1789 | } | ||
1790 | #endif | ||
1791 | |||
1792 | return; | ||
1793 | } | ||
1794 | |||
1795 | /* | ||
1796 | * gt64260b_chip_specific_init() | ||
1797 | * | ||
1798 | * Implement errata work arounds for the GT64260B. | ||
1799 | */ | ||
1800 | static void __init | ||
1801 | gt64260b_chip_specific_init(struct mv64x60_handle *bh, | ||
1802 | struct mv64x60_setup_info *si) | ||
1803 | { | ||
1804 | #ifdef CONFIG_SERIAL_MPSC | ||
1805 | struct resource *r; | ||
1806 | #endif | ||
1807 | #if !defined(CONFIG_NOT_COHERENT_CACHE) | ||
1808 | u32 val; | ||
1809 | u8 save_exclude; | ||
1810 | #endif | ||
1811 | |||
1812 | if (si->pci_0.enable_bus) | ||
1813 | mv64x60_set_bits(bh, MV64x60_PCI0_CMD, | ||
1814 | ((1<<4) | (1<<5) | (1<<9) | (1<<13))); | ||
1815 | |||
1816 | if (si->pci_1.enable_bus) | ||
1817 | mv64x60_set_bits(bh, MV64x60_PCI1_CMD, | ||
1818 | ((1<<4) | (1<<5) | (1<<9) | (1<<13))); | ||
1819 | |||
1820 | /* | ||
1821 | * Dave Wilhardt found that bit 4 in the PCI Command registers must | ||
1822 | * be set if you are using cache coherency. | ||
1823 | */ | ||
1824 | #if !defined(CONFIG_NOT_COHERENT_CACHE) | ||
1825 | mv64x60_set_bits(bh, GT64260_CPU_WB_PRIORITY_BUFFER_DEPTH, 0xf); | ||
1826 | |||
1827 | /* Res #MEM-4 -- cpu read buffer to buffer 1 */ | ||
1828 | if ((mv64x60_read(bh, MV64x60_CPU_MODE) & 0xf0) == 0x40) | ||
1829 | mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26)); | ||
1830 | |||
1831 | save_exclude = mv64x60_pci_exclude_bridge; | ||
1832 | mv64x60_pci_exclude_bridge = 0; | ||
1833 | if (si->pci_0.enable_bus) { | ||
1834 | early_read_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), | ||
1835 | PCI_COMMAND, &val); | ||
1836 | val |= PCI_COMMAND_INVALIDATE; | ||
1837 | early_write_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0), | ||
1838 | PCI_COMMAND, val); | ||
1839 | } | ||
1840 | |||
1841 | if (si->pci_1.enable_bus) { | ||
1842 | early_read_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), | ||
1843 | PCI_COMMAND, &val); | ||
1844 | val |= PCI_COMMAND_INVALIDATE; | ||
1845 | early_write_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0), | ||
1846 | PCI_COMMAND, val); | ||
1847 | } | ||
1848 | mv64x60_pci_exclude_bridge = save_exclude; | ||
1849 | #endif | ||
1850 | |||
1851 | /* Disable buffer/descriptor snooping */ | ||
1852 | mv64x60_clr_bits(bh, 0xf280, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); | ||
1853 | mv64x60_clr_bits(bh, 0xf2c0, (1<< 6) | (1<<14) | (1<<22) | (1<<30)); | ||
1854 | |||
1855 | #ifdef CONFIG_SERIAL_MPSC | ||
1856 | /* | ||
1857 | * The 64260B is not supposed to have the bug where the MPSC & ENET | ||
1858 | * can't access cache coherent regions. However, testing has shown | ||
1859 | * that the MPSC, at least, still has this bug. | ||
1860 | */ | ||
1861 | mv64x60_mpsc0_pdata.cache_mgmt = 1; | ||
1862 | mv64x60_mpsc1_pdata.cache_mgmt = 1; | ||
1863 | |||
1864 | if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0)) | ||
1865 | != NULL) { | ||
1866 | |||
1867 | r->start = MV64x60_IRQ_SDMA_0; | ||
1868 | r->end = MV64x60_IRQ_SDMA_0; | ||
1869 | } | ||
1870 | #endif | ||
1871 | |||
1872 | return; | ||
1873 | } | ||
1874 | |||
1875 | /* | ||
1876 | ***************************************************************************** | ||
1877 | * | ||
1878 | * MV64360-Specific Routines | ||
1879 | * | ||
1880 | ***************************************************************************** | ||
1881 | */ | ||
1882 | /* | ||
1883 | * mv64360_translate_size() | ||
1884 | * | ||
1885 | * On the MV64360, the size register is set similar to the size you get | ||
1886 | * from a pci config space BAR register. That is, programmed from LSB to MSB | ||
1887 | * as a sequence of 1's followed by a sequence of 0's. IOW, "size -1" with the | ||
1888 | * assumption that the size is a power of 2. | ||
1889 | */ | ||
1890 | static u32 __init | ||
1891 | mv64360_translate_size(u32 base_addr, u32 size, u32 num_bits) | ||
1892 | { | ||
1893 | return mv64x60_mask(size - 1, num_bits); | ||
1894 | } | ||
1895 | |||
1896 | /* | ||
1897 | * mv64360_untranslate_size() | ||
1898 | * | ||
1899 | * Translate the size register value of a window into a window size. | ||
1900 | */ | ||
1901 | static u32 __init | ||
1902 | mv64360_untranslate_size(u32 base_addr, u32 size, u32 num_bits) | ||
1903 | { | ||
1904 | if (size > 0) { | ||
1905 | size >>= (32 - num_bits); | ||
1906 | size++; | ||
1907 | size <<= (32 - num_bits); | ||
1908 | } | ||
1909 | |||
1910 | return size; | ||
1911 | } | ||
1912 | |||
1913 | /* | ||
1914 | * mv64360_set_pci2mem_window() | ||
1915 | * | ||
1916 | * The PCI->MEM window registers are actually in PCI config space so need | ||
1917 | * to set them by setting the correct config space BARs. | ||
1918 | */ | ||
1919 | struct { | ||
1920 | u32 fcn; | ||
1921 | u32 base_hi_bar; | ||
1922 | u32 base_lo_bar; | ||
1923 | } static mv64360_reg_addrs[2][4] __initdata = { | ||
1924 | {{ 0, 0x14, 0x10 }, { 0, 0x1c, 0x18 }, | ||
1925 | { 1, 0x14, 0x10 }, { 1, 0x1c, 0x18 }}, | ||
1926 | {{ 0, 0x94, 0x90 }, { 0, 0x9c, 0x98 }, | ||
1927 | { 1, 0x94, 0x90 }, { 1, 0x9c, 0x98 }} | ||
1928 | }; | ||
1929 | |||
1930 | static void __init | ||
1931 | mv64360_set_pci2mem_window(struct pci_controller *hose, u32 bus, u32 window, | ||
1932 | u32 base) | ||
1933 | { | ||
1934 | u8 save_exclude; | ||
1935 | |||
1936 | pr_debug("set pci->mem window: %d, hose: %d, base: 0x%x\n", window, | ||
1937 | hose->index, base); | ||
1938 | |||
1939 | save_exclude = mv64x60_pci_exclude_bridge; | ||
1940 | mv64x60_pci_exclude_bridge = 0; | ||
1941 | early_write_config_dword(hose, 0, | ||
1942 | PCI_DEVFN(0, mv64360_reg_addrs[bus][window].fcn), | ||
1943 | mv64360_reg_addrs[bus][window].base_hi_bar, 0); | ||
1944 | early_write_config_dword(hose, 0, | ||
1945 | PCI_DEVFN(0, mv64360_reg_addrs[bus][window].fcn), | ||
1946 | mv64360_reg_addrs[bus][window].base_lo_bar, | ||
1947 | mv64x60_mask(base,20) | 0xc); | ||
1948 | mv64x60_pci_exclude_bridge = save_exclude; | ||
1949 | |||
1950 | return; | ||
1951 | } | ||
1952 | |||
1953 | /* | ||
1954 | * mv64360_set_pci2regs_window() | ||
1955 | * | ||
1956 | * Set where the bridge's registers appear in PCI MEM space. | ||
1957 | */ | ||
1958 | static u32 mv64360_offset[2][2] __initdata = {{0x20, 0x24}, {0xa0, 0xa4}}; | ||
1959 | |||
1960 | static void __init | ||
1961 | mv64360_set_pci2regs_window(struct mv64x60_handle *bh, | ||
1962 | struct pci_controller *hose, u32 bus, u32 base) | ||
1963 | { | ||
1964 | u8 save_exclude; | ||
1965 | |||
1966 | pr_debug("set pci->internal regs hose: %d, base: 0x%x\n", hose->index, | ||
1967 | base); | ||
1968 | |||
1969 | save_exclude = mv64x60_pci_exclude_bridge; | ||
1970 | mv64x60_pci_exclude_bridge = 0; | ||
1971 | early_write_config_dword(hose, 0, PCI_DEVFN(0,0), | ||
1972 | mv64360_offset[bus][0], (base << 16)); | ||
1973 | early_write_config_dword(hose, 0, PCI_DEVFN(0,0), | ||
1974 | mv64360_offset[bus][1], 0); | ||
1975 | mv64x60_pci_exclude_bridge = save_exclude; | ||
1976 | |||
1977 | return; | ||
1978 | } | ||
1979 | |||
1980 | /* | ||
1981 | * mv64360_is_enabled_32bit() | ||
1982 | * | ||
1983 | * On a MV64360, a window is enabled by either clearing a bit in the | ||
1984 | * CPU BAR Enable reg or setting a bit in the window's base reg. | ||
1985 | * Note that this doesn't work for windows on the PCI slave side but we don't | ||
1986 | * check those so its okay. | ||
1987 | */ | ||
1988 | static u32 __init | ||
1989 | mv64360_is_enabled_32bit(struct mv64x60_handle *bh, u32 window) | ||
1990 | { | ||
1991 | u32 extra, rc = 0; | ||
1992 | |||
1993 | if (((mv64360_32bit_windows[window].base_reg != 0) && | ||
1994 | (mv64360_32bit_windows[window].size_reg != 0)) || | ||
1995 | (window == MV64x60_CPU2SRAM_WIN)) { | ||
1996 | |||
1997 | extra = mv64360_32bit_windows[window].extra; | ||
1998 | |||
1999 | switch (extra & MV64x60_EXTRA_MASK) { | ||
2000 | case MV64x60_EXTRA_CPUWIN_ENAB: | ||
2001 | rc = (mv64x60_read(bh, MV64360_CPU_BAR_ENABLE) & | ||
2002 | (1 << (extra & 0x1f))) == 0; | ||
2003 | break; | ||
2004 | |||
2005 | case MV64x60_EXTRA_CPUPROT_ENAB: | ||
2006 | rc = (mv64x60_read(bh, | ||
2007 | mv64360_32bit_windows[window].base_reg) & | ||
2008 | (1 << (extra & 0x1f))) != 0; | ||
2009 | break; | ||
2010 | |||
2011 | case MV64x60_EXTRA_ENET_ENAB: | ||
2012 | rc = (mv64x60_read(bh, MV64360_ENET2MEM_BAR_ENABLE) & | ||
2013 | (1 << (extra & 0x7))) == 0; | ||
2014 | break; | ||
2015 | |||
2016 | case MV64x60_EXTRA_MPSC_ENAB: | ||
2017 | rc = (mv64x60_read(bh, MV64360_MPSC2MEM_BAR_ENABLE) & | ||
2018 | (1 << (extra & 0x3))) == 0; | ||
2019 | break; | ||
2020 | |||
2021 | case MV64x60_EXTRA_IDMA_ENAB: | ||
2022 | rc = (mv64x60_read(bh, MV64360_IDMA2MEM_BAR_ENABLE) & | ||
2023 | (1 << (extra & 0x7))) == 0; | ||
2024 | break; | ||
2025 | |||
2026 | default: | ||
2027 | printk(KERN_ERR "mv64360_is_enabled: %s\n", | ||
2028 | "32bit table corrupted"); | ||
2029 | } | ||
2030 | } | ||
2031 | |||
2032 | return rc; | ||
2033 | } | ||
2034 | |||
2035 | /* | ||
2036 | * mv64360_enable_window_32bit() | ||
2037 | * | ||
2038 | * On a MV64360, a window is enabled by either clearing a bit in the | ||
2039 | * CPU BAR Enable reg or setting a bit in the window's base reg. | ||
2040 | */ | ||
2041 | static void __init | ||
2042 | mv64360_enable_window_32bit(struct mv64x60_handle *bh, u32 window) | ||
2043 | { | ||
2044 | u32 extra; | ||
2045 | |||
2046 | pr_debug("enable 32bit window: %d\n", window); | ||
2047 | |||
2048 | if (((mv64360_32bit_windows[window].base_reg != 0) && | ||
2049 | (mv64360_32bit_windows[window].size_reg != 0)) || | ||
2050 | (window == MV64x60_CPU2SRAM_WIN)) { | ||
2051 | |||
2052 | extra = mv64360_32bit_windows[window].extra; | ||
2053 | |||
2054 | switch (extra & MV64x60_EXTRA_MASK) { | ||
2055 | case MV64x60_EXTRA_CPUWIN_ENAB: | ||
2056 | mv64x60_clr_bits(bh, MV64360_CPU_BAR_ENABLE, | ||
2057 | (1 << (extra & 0x1f))); | ||
2058 | break; | ||
2059 | |||
2060 | case MV64x60_EXTRA_CPUPROT_ENAB: | ||
2061 | mv64x60_set_bits(bh, | ||
2062 | mv64360_32bit_windows[window].base_reg, | ||
2063 | (1 << (extra & 0x1f))); | ||
2064 | break; | ||
2065 | |||
2066 | case MV64x60_EXTRA_ENET_ENAB: | ||
2067 | mv64x60_clr_bits(bh, MV64360_ENET2MEM_BAR_ENABLE, | ||
2068 | (1 << (extra & 0x7))); | ||
2069 | break; | ||
2070 | |||
2071 | case MV64x60_EXTRA_MPSC_ENAB: | ||
2072 | mv64x60_clr_bits(bh, MV64360_MPSC2MEM_BAR_ENABLE, | ||
2073 | (1 << (extra & 0x3))); | ||
2074 | break; | ||
2075 | |||
2076 | case MV64x60_EXTRA_IDMA_ENAB: | ||
2077 | mv64x60_clr_bits(bh, MV64360_IDMA2MEM_BAR_ENABLE, | ||
2078 | (1 << (extra & 0x7))); | ||
2079 | break; | ||
2080 | |||
2081 | default: | ||
2082 | printk(KERN_ERR "mv64360_enable: %s\n", | ||
2083 | "32bit table corrupted"); | ||
2084 | } | ||
2085 | } | ||
2086 | |||
2087 | return; | ||
2088 | } | ||
2089 | |||
2090 | /* | ||
2091 | * mv64360_disable_window_32bit() | ||
2092 | * | ||
2093 | * On a MV64360, a window is disabled by either setting a bit in the | ||
2094 | * CPU BAR Enable reg or clearing a bit in the window's base reg. | ||
2095 | */ | ||
2096 | static void __init | ||
2097 | mv64360_disable_window_32bit(struct mv64x60_handle *bh, u32 window) | ||
2098 | { | ||
2099 | u32 extra; | ||
2100 | |||
2101 | pr_debug("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", | ||
2102 | window, mv64360_32bit_windows[window].base_reg, | ||
2103 | mv64360_32bit_windows[window].size_reg); | ||
2104 | |||
2105 | if (((mv64360_32bit_windows[window].base_reg != 0) && | ||
2106 | (mv64360_32bit_windows[window].size_reg != 0)) || | ||
2107 | (window == MV64x60_CPU2SRAM_WIN)) { | ||
2108 | |||
2109 | extra = mv64360_32bit_windows[window].extra; | ||
2110 | |||
2111 | switch (extra & MV64x60_EXTRA_MASK) { | ||
2112 | case MV64x60_EXTRA_CPUWIN_ENAB: | ||
2113 | mv64x60_set_bits(bh, MV64360_CPU_BAR_ENABLE, | ||
2114 | (1 << (extra & 0x1f))); | ||
2115 | break; | ||
2116 | |||
2117 | case MV64x60_EXTRA_CPUPROT_ENAB: | ||
2118 | mv64x60_clr_bits(bh, | ||
2119 | mv64360_32bit_windows[window].base_reg, | ||
2120 | (1 << (extra & 0x1f))); | ||
2121 | break; | ||
2122 | |||
2123 | case MV64x60_EXTRA_ENET_ENAB: | ||
2124 | mv64x60_set_bits(bh, MV64360_ENET2MEM_BAR_ENABLE, | ||
2125 | (1 << (extra & 0x7))); | ||
2126 | break; | ||
2127 | |||
2128 | case MV64x60_EXTRA_MPSC_ENAB: | ||
2129 | mv64x60_set_bits(bh, MV64360_MPSC2MEM_BAR_ENABLE, | ||
2130 | (1 << (extra & 0x3))); | ||
2131 | break; | ||
2132 | |||
2133 | case MV64x60_EXTRA_IDMA_ENAB: | ||
2134 | mv64x60_set_bits(bh, MV64360_IDMA2MEM_BAR_ENABLE, | ||
2135 | (1 << (extra & 0x7))); | ||
2136 | break; | ||
2137 | |||
2138 | default: | ||
2139 | printk(KERN_ERR "mv64360_disable: %s\n", | ||
2140 | "32bit table corrupted"); | ||
2141 | } | ||
2142 | } | ||
2143 | |||
2144 | return; | ||
2145 | } | ||
2146 | |||
2147 | /* | ||
2148 | * mv64360_enable_window_64bit() | ||
2149 | * | ||
2150 | * On the MV64360, a 64-bit window is enabled by setting a bit in the window's | ||
2151 | * base reg. | ||
2152 | */ | ||
2153 | static void __init | ||
2154 | mv64360_enable_window_64bit(struct mv64x60_handle *bh, u32 window) | ||
2155 | { | ||
2156 | pr_debug("enable 64bit window: %d\n", window); | ||
2157 | |||
2158 | if ((mv64360_64bit_windows[window].base_lo_reg!= 0) && | ||
2159 | (mv64360_64bit_windows[window].size_reg != 0)) { | ||
2160 | |||
2161 | if ((mv64360_64bit_windows[window].extra & MV64x60_EXTRA_MASK) | ||
2162 | == MV64x60_EXTRA_PCIACC_ENAB) | ||
2163 | |||
2164 | mv64x60_set_bits(bh, | ||
2165 | mv64360_64bit_windows[window].base_lo_reg, | ||
2166 | (1 << (mv64360_64bit_windows[window].extra & | ||
2167 | 0x1f))); | ||
2168 | else | ||
2169 | printk(KERN_ERR "mv64360_enable: %s\n", | ||
2170 | "64bit table corrupted"); | ||
2171 | } | ||
2172 | |||
2173 | return; | ||
2174 | } | ||
2175 | |||
2176 | /* | ||
2177 | * mv64360_disable_window_64bit() | ||
2178 | * | ||
2179 | * On a MV64360, a 64-bit window is disabled by clearing a bit in the window's | ||
2180 | * base reg. | ||
2181 | */ | ||
2182 | static void __init | ||
2183 | mv64360_disable_window_64bit(struct mv64x60_handle *bh, u32 window) | ||
2184 | { | ||
2185 | pr_debug("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n", | ||
2186 | window, mv64360_64bit_windows[window].base_lo_reg, | ||
2187 | mv64360_64bit_windows[window].size_reg); | ||
2188 | |||
2189 | if ((mv64360_64bit_windows[window].base_lo_reg != 0) && | ||
2190 | (mv64360_64bit_windows[window].size_reg != 0)) { | ||
2191 | |||
2192 | if ((mv64360_64bit_windows[window].extra & MV64x60_EXTRA_MASK) | ||
2193 | == MV64x60_EXTRA_PCIACC_ENAB) | ||
2194 | |||
2195 | mv64x60_clr_bits(bh, | ||
2196 | mv64360_64bit_windows[window].base_lo_reg, | ||
2197 | (1 << (mv64360_64bit_windows[window].extra & | ||
2198 | 0x1f))); | ||
2199 | else | ||
2200 | printk(KERN_ERR "mv64360_disable: %s\n", | ||
2201 | "64bit table corrupted"); | ||
2202 | } | ||
2203 | |||
2204 | return; | ||
2205 | } | ||
2206 | |||
2207 | /* | ||
2208 | * mv64360_disable_all_windows() | ||
2209 | * | ||
2210 | * The MV64360 has a few windows that aren't represented in the table of | ||
2211 | * windows at the top of this file. This routine turns all of them off | ||
2212 | * except for the memory controller windows, of course. | ||
2213 | */ | ||
2214 | static void __init | ||
2215 | mv64360_disable_all_windows(struct mv64x60_handle *bh, | ||
2216 | struct mv64x60_setup_info *si) | ||
2217 | { | ||
2218 | u32 preserve, i; | ||
2219 | |||
2220 | /* Disable 32bit windows (don't disable cpu->mem windows) */ | ||
2221 | for (i=MV64x60_CPU2DEV_0_WIN; i<MV64x60_32BIT_WIN_COUNT; i++) { | ||
2222 | if (i < 32) | ||
2223 | preserve = si->window_preserve_mask_32_lo & (1 << i); | ||
2224 | else | ||
2225 | preserve = si->window_preserve_mask_32_hi & (1<<(i-32)); | ||
2226 | |||
2227 | if (!preserve) | ||
2228 | mv64360_disable_window_32bit(bh, i); | ||
2229 | } | ||
2230 | |||
2231 | /* Disable 64bit windows */ | ||
2232 | for (i=0; i<MV64x60_64BIT_WIN_COUNT; i++) | ||
2233 | if (!(si->window_preserve_mask_64 & (1<<i))) | ||
2234 | mv64360_disable_window_64bit(bh, i); | ||
2235 | |||
2236 | /* Turn off PCI->MEM access cntl wins not in mv64360_64bit_windows[] */ | ||
2237 | mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0); | ||
2238 | mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0); | ||
2239 | mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0); | ||
2240 | mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0); | ||
2241 | |||
2242 | /* Disable all PCI-><whatever> windows */ | ||
2243 | mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x0000f9ff); | ||
2244 | mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x0000f9ff); | ||
2245 | |||
2246 | return; | ||
2247 | } | ||
2248 | |||
2249 | /* | ||
2250 | * mv64360_config_io2mem_windows() | ||
2251 | * | ||
2252 | * ENET, MPSC, and IDMA ctlrs on the MV64[34]60 have separate windows that | ||
2253 | * must be set up so that the respective ctlr can access system memory. | ||
2254 | */ | ||
2255 | static u32 enet_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { | ||
2256 | MV64x60_ENET2MEM_0_WIN, MV64x60_ENET2MEM_1_WIN, | ||
2257 | MV64x60_ENET2MEM_2_WIN, MV64x60_ENET2MEM_3_WIN, | ||
2258 | }; | ||
2259 | |||
2260 | static u32 mpsc_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { | ||
2261 | MV64x60_MPSC2MEM_0_WIN, MV64x60_MPSC2MEM_1_WIN, | ||
2262 | MV64x60_MPSC2MEM_2_WIN, MV64x60_MPSC2MEM_3_WIN, | ||
2263 | }; | ||
2264 | |||
2265 | static u32 idma_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = { | ||
2266 | MV64x60_IDMA2MEM_0_WIN, MV64x60_IDMA2MEM_1_WIN, | ||
2267 | MV64x60_IDMA2MEM_2_WIN, MV64x60_IDMA2MEM_3_WIN, | ||
2268 | }; | ||
2269 | |||
2270 | static u32 dram_selects[MV64x60_CPU2MEM_WINDOWS] __initdata = | ||
2271 | { 0xe, 0xd, 0xb, 0x7 }; | ||
2272 | |||
2273 | static void __init | ||
2274 | mv64360_config_io2mem_windows(struct mv64x60_handle *bh, | ||
2275 | struct mv64x60_setup_info *si, | ||
2276 | u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]) | ||
2277 | { | ||
2278 | u32 i, win; | ||
2279 | |||
2280 | pr_debug("config_io2regs_windows: enet, mpsc, idma -> bridge regs\n"); | ||
2281 | |||
2282 | mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_0, 0); | ||
2283 | mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_1, 0); | ||
2284 | mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_2, 0); | ||
2285 | |||
2286 | mv64x60_write(bh, MV64360_MPSC2MEM_ACC_PROT_0, 0); | ||
2287 | mv64x60_write(bh, MV64360_MPSC2MEM_ACC_PROT_1, 0); | ||
2288 | |||
2289 | mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_0, 0); | ||
2290 | mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_1, 0); | ||
2291 | mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_2, 0); | ||
2292 | mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_3, 0); | ||
2293 | |||
2294 | /* Assume that mem ctlr has no more windows than embedded I/O ctlr */ | ||
2295 | for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++) | ||
2296 | if (bh->ci->is_enabled_32bit(bh, win)) { | ||
2297 | mv64x60_set_32bit_window(bh, enet_tab[i], | ||
2298 | mem_windows[i][0], mem_windows[i][1], | ||
2299 | (dram_selects[i] << 8) | | ||
2300 | (si->enet_options[i] & 0x3000)); | ||
2301 | bh->ci->enable_window_32bit(bh, enet_tab[i]); | ||
2302 | |||
2303 | /* Give enet r/w access to memory region */ | ||
2304 | mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_0, | ||
2305 | (0x3 << (i << 1))); | ||
2306 | mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_1, | ||
2307 | (0x3 << (i << 1))); | ||
2308 | mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_2, | ||
2309 | (0x3 << (i << 1))); | ||
2310 | |||
2311 | mv64x60_set_32bit_window(bh, mpsc_tab[i], | ||
2312 | mem_windows[i][0], mem_windows[i][1], | ||
2313 | (dram_selects[i] << 8) | | ||
2314 | (si->mpsc_options[i] & 0x3000)); | ||
2315 | bh->ci->enable_window_32bit(bh, mpsc_tab[i]); | ||
2316 | |||
2317 | /* Give mpsc r/w access to memory region */ | ||
2318 | mv64x60_set_bits(bh, MV64360_MPSC2MEM_ACC_PROT_0, | ||
2319 | (0x3 << (i << 1))); | ||
2320 | mv64x60_set_bits(bh, MV64360_MPSC2MEM_ACC_PROT_1, | ||
2321 | (0x3 << (i << 1))); | ||
2322 | |||
2323 | mv64x60_set_32bit_window(bh, idma_tab[i], | ||
2324 | mem_windows[i][0], mem_windows[i][1], | ||
2325 | (dram_selects[i] << 8) | | ||
2326 | (si->idma_options[i] & 0x3000)); | ||
2327 | bh->ci->enable_window_32bit(bh, idma_tab[i]); | ||
2328 | |||
2329 | /* Give idma r/w access to memory region */ | ||
2330 | mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_0, | ||
2331 | (0x3 << (i << 1))); | ||
2332 | mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_1, | ||
2333 | (0x3 << (i << 1))); | ||
2334 | mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_2, | ||
2335 | (0x3 << (i << 1))); | ||
2336 | mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_3, | ||
2337 | (0x3 << (i << 1))); | ||
2338 | } | ||
2339 | |||
2340 | return; | ||
2341 | } | ||
2342 | |||
2343 | /* | ||
2344 | * mv64360_set_mpsc2regs_window() | ||
2345 | * | ||
2346 | * MPSC has a window to the bridge's internal registers. Call this routine | ||
2347 | * to change that window so it doesn't conflict with the windows mapping the | ||
2348 | * mpsc to system memory. | ||
2349 | */ | ||
2350 | static void __init | ||
2351 | mv64360_set_mpsc2regs_window(struct mv64x60_handle *bh, u32 base) | ||
2352 | { | ||
2353 | pr_debug("set mpsc->internal regs, base: 0x%x\n", base); | ||
2354 | |||
2355 | mv64x60_write(bh, MV64360_MPSC2REGS_BASE, base & 0xffff0000); | ||
2356 | return; | ||
2357 | } | ||
2358 | |||
2359 | /* | ||
2360 | * mv64360_chip_specific_init() | ||
2361 | * | ||
2362 | * No errata work arounds for the MV64360 implemented at this point. | ||
2363 | */ | ||
2364 | static void __init | ||
2365 | mv64360_chip_specific_init(struct mv64x60_handle *bh, | ||
2366 | struct mv64x60_setup_info *si) | ||
2367 | { | ||
2368 | #ifdef CONFIG_SERIAL_MPSC | ||
2369 | mv64x60_mpsc0_pdata.brg_can_tune = 1; | ||
2370 | mv64x60_mpsc0_pdata.cache_mgmt = 1; | ||
2371 | mv64x60_mpsc1_pdata.brg_can_tune = 1; | ||
2372 | mv64x60_mpsc1_pdata.cache_mgmt = 1; | ||
2373 | #endif | ||
2374 | |||
2375 | return; | ||
2376 | } | ||
2377 | |||
2378 | /* | ||
2379 | * mv64460_chip_specific_init() | ||
2380 | * | ||
2381 | * No errata work arounds for the MV64460 implemented at this point. | ||
2382 | */ | ||
2383 | static void __init | ||
2384 | mv64460_chip_specific_init(struct mv64x60_handle *bh, | ||
2385 | struct mv64x60_setup_info *si) | ||
2386 | { | ||
2387 | #ifdef CONFIG_SERIAL_MPSC | ||
2388 | mv64x60_mpsc0_pdata.brg_can_tune = 1; | ||
2389 | mv64x60_mpsc1_pdata.brg_can_tune = 1; | ||
2390 | #endif | ||
2391 | return; | ||
2392 | } | ||
diff --git a/arch/ppc/syslib/mv64x60_dbg.c b/arch/ppc/syslib/mv64x60_dbg.c new file mode 100644 index 000000000000..2927c7adf5e5 --- /dev/null +++ b/arch/ppc/syslib/mv64x60_dbg.c | |||
@@ -0,0 +1,123 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mv64x60_dbg.c | ||
3 | * | ||
4 | * KGDB and progress routines for the Marvell/Galileo MV64x60 (Discovery). | ||
5 | * | ||
6 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
7 | * | ||
8 | * 2003 (c) MontaVista Software, Inc. This file is licensed under | ||
9 | * the terms of the GNU General Public License version 2. This program | ||
10 | * is licensed "as is" without any warranty of any kind, whether express | ||
11 | * or implied. | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | ***************************************************************************** | ||
16 | * | ||
17 | * Low-level MPSC/UART I/O routines | ||
18 | * | ||
19 | ***************************************************************************** | ||
20 | */ | ||
21 | |||
22 | |||
23 | #include <linux/config.h> | ||
24 | #include <linux/irq.h> | ||
25 | #include <asm/delay.h> | ||
26 | #include <asm/mv64x60.h> | ||
27 | |||
28 | |||
29 | #if defined(CONFIG_SERIAL_TEXT_DEBUG) | ||
30 | |||
31 | #define MPSC_CHR_1 0x000c | ||
32 | #define MPSC_CHR_2 0x0010 | ||
33 | |||
34 | static struct mv64x60_handle mv64x60_dbg_bh; | ||
35 | |||
36 | void | ||
37 | mv64x60_progress_init(u32 base) | ||
38 | { | ||
39 | mv64x60_dbg_bh.v_base = base; | ||
40 | return; | ||
41 | } | ||
42 | |||
43 | static void | ||
44 | mv64x60_polled_putc(int chan, char c) | ||
45 | { | ||
46 | u32 offset; | ||
47 | |||
48 | if (chan == 0) | ||
49 | offset = 0x8000; | ||
50 | else | ||
51 | offset = 0x9000; | ||
52 | |||
53 | mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_1, (u32)c); | ||
54 | mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_2, 0x200); | ||
55 | udelay(2000); | ||
56 | } | ||
57 | |||
58 | void | ||
59 | mv64x60_mpsc_progress(char *s, unsigned short hex) | ||
60 | { | ||
61 | volatile char c; | ||
62 | |||
63 | mv64x60_polled_putc(0, '\r'); | ||
64 | |||
65 | while ((c = *s++) != 0) | ||
66 | mv64x60_polled_putc(0, c); | ||
67 | |||
68 | mv64x60_polled_putc(0, '\n'); | ||
69 | mv64x60_polled_putc(0, '\r'); | ||
70 | |||
71 | return; | ||
72 | } | ||
73 | #endif /* CONFIG_SERIAL_TEXT_DEBUG */ | ||
74 | |||
75 | |||
76 | #if defined(CONFIG_KGDB) | ||
77 | |||
78 | #if defined(CONFIG_KGDB_TTYS0) | ||
79 | #define KGDB_PORT 0 | ||
80 | #elif defined(CONFIG_KGDB_TTYS1) | ||
81 | #define KGDB_PORT 1 | ||
82 | #else | ||
83 | #error "Invalid kgdb_tty port" | ||
84 | #endif | ||
85 | |||
86 | void | ||
87 | putDebugChar(unsigned char c) | ||
88 | { | ||
89 | mv64x60_polled_putc(KGDB_PORT, (char)c); | ||
90 | } | ||
91 | |||
92 | int | ||
93 | getDebugChar(void) | ||
94 | { | ||
95 | unsigned char c; | ||
96 | |||
97 | while (!mv64x60_polled_getc(KGDB_PORT, &c)); | ||
98 | return (int)c; | ||
99 | } | ||
100 | |||
101 | void | ||
102 | putDebugString(char* str) | ||
103 | { | ||
104 | while (*str != '\0') { | ||
105 | putDebugChar(*str); | ||
106 | str++; | ||
107 | } | ||
108 | putDebugChar('\r'); | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | void | ||
113 | kgdb_interruptible(int enable) | ||
114 | { | ||
115 | } | ||
116 | |||
117 | void | ||
118 | kgdb_map_scc(void) | ||
119 | { | ||
120 | if (ppc_md.early_serial_map) | ||
121 | ppc_md.early_serial_map(); | ||
122 | } | ||
123 | #endif /* CONFIG_KGDB */ | ||
diff --git a/arch/ppc/syslib/mv64x60_win.c b/arch/ppc/syslib/mv64x60_win.c new file mode 100644 index 000000000000..b6f0f5dcf6ee --- /dev/null +++ b/arch/ppc/syslib/mv64x60_win.c | |||
@@ -0,0 +1,1168 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/mv64x60_win.c | ||
3 | * | ||
4 | * Tables with info on how to manipulate the 32 & 64 bit windows on the | ||
5 | * various types of Marvell bridge chips. | ||
6 | * | ||
7 | * Author: Mark A. Greer <mgreer@mvista.com> | ||
8 | * | ||
9 | * 2004 (c) MontaVista, Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/bootmem.h> | ||
21 | #include <linux/mv643xx.h> | ||
22 | |||
23 | #include <asm/byteorder.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/irq.h> | ||
26 | #include <asm/uaccess.h> | ||
27 | #include <asm/machdep.h> | ||
28 | #include <asm/pci-bridge.h> | ||
29 | #include <asm/delay.h> | ||
30 | #include <asm/mv64x60.h> | ||
31 | |||
32 | |||
33 | /* | ||
34 | ***************************************************************************** | ||
35 | * | ||
36 | * Tables describing how to set up windows on each type of bridge | ||
37 | * | ||
38 | ***************************************************************************** | ||
39 | */ | ||
40 | struct mv64x60_32bit_window | ||
41 | gt64260_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = { | ||
42 | /* CPU->MEM Windows */ | ||
43 | [MV64x60_CPU2MEM_0_WIN] = { | ||
44 | .base_reg = MV64x60_CPU2MEM_0_BASE, | ||
45 | .size_reg = MV64x60_CPU2MEM_0_SIZE, | ||
46 | .base_bits = 12, | ||
47 | .size_bits = 12, | ||
48 | .get_from_field = mv64x60_shift_left, | ||
49 | .map_to_field = mv64x60_shift_right, | ||
50 | .extra = 0 }, | ||
51 | [MV64x60_CPU2MEM_1_WIN] = { | ||
52 | .base_reg = MV64x60_CPU2MEM_1_BASE, | ||
53 | .size_reg = MV64x60_CPU2MEM_1_SIZE, | ||
54 | .base_bits = 12, | ||
55 | .size_bits = 12, | ||
56 | .get_from_field = mv64x60_shift_left, | ||
57 | .map_to_field = mv64x60_shift_right, | ||
58 | .extra = 0 }, | ||
59 | [MV64x60_CPU2MEM_2_WIN] = { | ||
60 | .base_reg = MV64x60_CPU2MEM_2_BASE, | ||
61 | .size_reg = MV64x60_CPU2MEM_2_SIZE, | ||
62 | .base_bits = 12, | ||
63 | .size_bits = 12, | ||
64 | .get_from_field = mv64x60_shift_left, | ||
65 | .map_to_field = mv64x60_shift_right, | ||
66 | .extra = 0 }, | ||
67 | [MV64x60_CPU2MEM_3_WIN] = { | ||
68 | .base_reg = MV64x60_CPU2MEM_3_BASE, | ||
69 | .size_reg = MV64x60_CPU2MEM_3_SIZE, | ||
70 | .base_bits = 12, | ||
71 | .size_bits = 12, | ||
72 | .get_from_field = mv64x60_shift_left, | ||
73 | .map_to_field = mv64x60_shift_right, | ||
74 | .extra = 0 }, | ||
75 | /* CPU->Device Windows */ | ||
76 | [MV64x60_CPU2DEV_0_WIN] = { | ||
77 | .base_reg = MV64x60_CPU2DEV_0_BASE, | ||
78 | .size_reg = MV64x60_CPU2DEV_0_SIZE, | ||
79 | .base_bits = 12, | ||
80 | .size_bits = 12, | ||
81 | .get_from_field = mv64x60_shift_left, | ||
82 | .map_to_field = mv64x60_shift_right, | ||
83 | .extra = 0 }, | ||
84 | [MV64x60_CPU2DEV_1_WIN] = { | ||
85 | .base_reg = MV64x60_CPU2DEV_1_BASE, | ||
86 | .size_reg = MV64x60_CPU2DEV_1_SIZE, | ||
87 | .base_bits = 12, | ||
88 | .size_bits = 12, | ||
89 | .get_from_field = mv64x60_shift_left, | ||
90 | .map_to_field = mv64x60_shift_right, | ||
91 | .extra = 0 }, | ||
92 | [MV64x60_CPU2DEV_2_WIN] = { | ||
93 | .base_reg = MV64x60_CPU2DEV_2_BASE, | ||
94 | .size_reg = MV64x60_CPU2DEV_2_SIZE, | ||
95 | .base_bits = 12, | ||
96 | .size_bits = 12, | ||
97 | .get_from_field = mv64x60_shift_left, | ||
98 | .map_to_field = mv64x60_shift_right, | ||
99 | .extra = 0 }, | ||
100 | [MV64x60_CPU2DEV_3_WIN] = { | ||
101 | .base_reg = MV64x60_CPU2DEV_3_BASE, | ||
102 | .size_reg = MV64x60_CPU2DEV_3_SIZE, | ||
103 | .base_bits = 12, | ||
104 | .size_bits = 12, | ||
105 | .get_from_field = mv64x60_shift_left, | ||
106 | .map_to_field = mv64x60_shift_right, | ||
107 | .extra = 0 }, | ||
108 | /* CPU->Boot Window */ | ||
109 | [MV64x60_CPU2BOOT_WIN] = { | ||
110 | .base_reg = MV64x60_CPU2BOOT_0_BASE, | ||
111 | .size_reg = MV64x60_CPU2BOOT_0_SIZE, | ||
112 | .base_bits = 12, | ||
113 | .size_bits = 12, | ||
114 | .get_from_field = mv64x60_shift_left, | ||
115 | .map_to_field = mv64x60_shift_right, | ||
116 | .extra = 0 }, | ||
117 | /* CPU->PCI 0 Windows */ | ||
118 | [MV64x60_CPU2PCI0_IO_WIN] = { | ||
119 | .base_reg = MV64x60_CPU2PCI0_IO_BASE, | ||
120 | .size_reg = MV64x60_CPU2PCI0_IO_SIZE, | ||
121 | .base_bits = 12, | ||
122 | .size_bits = 12, | ||
123 | .get_from_field = mv64x60_shift_left, | ||
124 | .map_to_field = mv64x60_shift_right, | ||
125 | .extra = 0 }, | ||
126 | [MV64x60_CPU2PCI0_MEM_0_WIN] = { | ||
127 | .base_reg = MV64x60_CPU2PCI0_MEM_0_BASE, | ||
128 | .size_reg = MV64x60_CPU2PCI0_MEM_0_SIZE, | ||
129 | .base_bits = 12, | ||
130 | .size_bits = 12, | ||
131 | .get_from_field = mv64x60_shift_left, | ||
132 | .map_to_field = mv64x60_shift_right, | ||
133 | .extra = 0 }, | ||
134 | [MV64x60_CPU2PCI0_MEM_1_WIN] = { | ||
135 | .base_reg = MV64x60_CPU2PCI0_MEM_1_BASE, | ||
136 | .size_reg = MV64x60_CPU2PCI0_MEM_1_SIZE, | ||
137 | .base_bits = 12, | ||
138 | .size_bits = 12, | ||
139 | .get_from_field = mv64x60_shift_left, | ||
140 | .map_to_field = mv64x60_shift_right, | ||
141 | .extra = 0 }, | ||
142 | [MV64x60_CPU2PCI0_MEM_2_WIN] = { | ||
143 | .base_reg = MV64x60_CPU2PCI0_MEM_2_BASE, | ||
144 | .size_reg = MV64x60_CPU2PCI0_MEM_2_SIZE, | ||
145 | .base_bits = 12, | ||
146 | .size_bits = 12, | ||
147 | .get_from_field = mv64x60_shift_left, | ||
148 | .map_to_field = mv64x60_shift_right, | ||
149 | .extra = 0 }, | ||
150 | [MV64x60_CPU2PCI0_MEM_3_WIN] = { | ||
151 | .base_reg = MV64x60_CPU2PCI0_MEM_3_BASE, | ||
152 | .size_reg = MV64x60_CPU2PCI0_MEM_3_SIZE, | ||
153 | .base_bits = 12, | ||
154 | .size_bits = 12, | ||
155 | .get_from_field = mv64x60_shift_left, | ||
156 | .map_to_field = mv64x60_shift_right, | ||
157 | .extra = 0 }, | ||
158 | /* CPU->PCI 1 Windows */ | ||
159 | [MV64x60_CPU2PCI1_IO_WIN] = { | ||
160 | .base_reg = MV64x60_CPU2PCI1_IO_BASE, | ||
161 | .size_reg = MV64x60_CPU2PCI1_IO_SIZE, | ||
162 | .base_bits = 12, | ||
163 | .size_bits = 12, | ||
164 | .get_from_field = mv64x60_shift_left, | ||
165 | .map_to_field = mv64x60_shift_right, | ||
166 | .extra = 0 }, | ||
167 | [MV64x60_CPU2PCI1_MEM_0_WIN] = { | ||
168 | .base_reg = MV64x60_CPU2PCI1_MEM_0_BASE, | ||
169 | .size_reg = MV64x60_CPU2PCI1_MEM_0_SIZE, | ||
170 | .base_bits = 12, | ||
171 | .size_bits = 12, | ||
172 | .get_from_field = mv64x60_shift_left, | ||
173 | .map_to_field = mv64x60_shift_right, | ||
174 | .extra = 0 }, | ||
175 | [MV64x60_CPU2PCI1_MEM_1_WIN] = { | ||
176 | .base_reg = MV64x60_CPU2PCI1_MEM_1_BASE, | ||
177 | .size_reg = MV64x60_CPU2PCI1_MEM_1_SIZE, | ||
178 | .base_bits = 12, | ||
179 | .size_bits = 12, | ||
180 | .get_from_field = mv64x60_shift_left, | ||
181 | .map_to_field = mv64x60_shift_right, | ||
182 | .extra = 0 }, | ||
183 | [MV64x60_CPU2PCI1_MEM_2_WIN] = { | ||
184 | .base_reg = MV64x60_CPU2PCI1_MEM_2_BASE, | ||
185 | .size_reg = MV64x60_CPU2PCI1_MEM_2_SIZE, | ||
186 | .base_bits = 12, | ||
187 | .size_bits = 12, | ||
188 | .get_from_field = mv64x60_shift_left, | ||
189 | .map_to_field = mv64x60_shift_right, | ||
190 | .extra = 0 }, | ||
191 | [MV64x60_CPU2PCI1_MEM_3_WIN] = { | ||
192 | .base_reg = MV64x60_CPU2PCI1_MEM_3_BASE, | ||
193 | .size_reg = MV64x60_CPU2PCI1_MEM_3_SIZE, | ||
194 | .base_bits = 12, | ||
195 | .size_bits = 12, | ||
196 | .get_from_field = mv64x60_shift_left, | ||
197 | .map_to_field = mv64x60_shift_right, | ||
198 | .extra = 0 }, | ||
199 | /* CPU->SRAM Window (64260 has no integrated SRAM) */ | ||
200 | /* CPU->PCI 0 Remap I/O Window */ | ||
201 | [MV64x60_CPU2PCI0_IO_REMAP_WIN] = { | ||
202 | .base_reg = MV64x60_CPU2PCI0_IO_REMAP, | ||
203 | .size_reg = 0, | ||
204 | .base_bits = 12, | ||
205 | .size_bits = 0, | ||
206 | .get_from_field = mv64x60_shift_left, | ||
207 | .map_to_field = mv64x60_shift_right, | ||
208 | .extra = 0 }, | ||
209 | /* CPU->PCI 1 Remap I/O Window */ | ||
210 | [MV64x60_CPU2PCI1_IO_REMAP_WIN] = { | ||
211 | .base_reg = MV64x60_CPU2PCI1_IO_REMAP, | ||
212 | .size_reg = 0, | ||
213 | .base_bits = 12, | ||
214 | .size_bits = 0, | ||
215 | .get_from_field = mv64x60_shift_left, | ||
216 | .map_to_field = mv64x60_shift_right, | ||
217 | .extra = 0 }, | ||
218 | /* CPU Memory Protection Windows */ | ||
219 | [MV64x60_CPU_PROT_0_WIN] = { | ||
220 | .base_reg = MV64x60_CPU_PROT_BASE_0, | ||
221 | .size_reg = MV64x60_CPU_PROT_SIZE_0, | ||
222 | .base_bits = 12, | ||
223 | .size_bits = 12, | ||
224 | .get_from_field = mv64x60_shift_left, | ||
225 | .map_to_field = mv64x60_shift_right, | ||
226 | .extra = 0 }, | ||
227 | [MV64x60_CPU_PROT_1_WIN] = { | ||
228 | .base_reg = MV64x60_CPU_PROT_BASE_1, | ||
229 | .size_reg = MV64x60_CPU_PROT_SIZE_1, | ||
230 | .base_bits = 12, | ||
231 | .size_bits = 12, | ||
232 | .get_from_field = mv64x60_shift_left, | ||
233 | .map_to_field = mv64x60_shift_right, | ||
234 | .extra = 0 }, | ||
235 | [MV64x60_CPU_PROT_2_WIN] = { | ||
236 | .base_reg = MV64x60_CPU_PROT_BASE_2, | ||
237 | .size_reg = MV64x60_CPU_PROT_SIZE_2, | ||
238 | .base_bits = 12, | ||
239 | .size_bits = 12, | ||
240 | .get_from_field = mv64x60_shift_left, | ||
241 | .map_to_field = mv64x60_shift_right, | ||
242 | .extra = 0 }, | ||
243 | [MV64x60_CPU_PROT_3_WIN] = { | ||
244 | .base_reg = MV64x60_CPU_PROT_BASE_3, | ||
245 | .size_reg = MV64x60_CPU_PROT_SIZE_3, | ||
246 | .base_bits = 12, | ||
247 | .size_bits = 12, | ||
248 | .get_from_field = mv64x60_shift_left, | ||
249 | .map_to_field = mv64x60_shift_right, | ||
250 | .extra = 0 }, | ||
251 | /* CPU Snoop Windows */ | ||
252 | [MV64x60_CPU_SNOOP_0_WIN] = { | ||
253 | .base_reg = GT64260_CPU_SNOOP_BASE_0, | ||
254 | .size_reg = GT64260_CPU_SNOOP_SIZE_0, | ||
255 | .base_bits = 12, | ||
256 | .size_bits = 12, | ||
257 | .get_from_field = mv64x60_shift_left, | ||
258 | .map_to_field = mv64x60_shift_right, | ||
259 | .extra = 0 }, | ||
260 | [MV64x60_CPU_SNOOP_1_WIN] = { | ||
261 | .base_reg = GT64260_CPU_SNOOP_BASE_1, | ||
262 | .size_reg = GT64260_CPU_SNOOP_SIZE_1, | ||
263 | .base_bits = 12, | ||
264 | .size_bits = 12, | ||
265 | .get_from_field = mv64x60_shift_left, | ||
266 | .map_to_field = mv64x60_shift_right, | ||
267 | .extra = 0 }, | ||
268 | [MV64x60_CPU_SNOOP_2_WIN] = { | ||
269 | .base_reg = GT64260_CPU_SNOOP_BASE_2, | ||
270 | .size_reg = GT64260_CPU_SNOOP_SIZE_2, | ||
271 | .base_bits = 12, | ||
272 | .size_bits = 12, | ||
273 | .get_from_field = mv64x60_shift_left, | ||
274 | .map_to_field = mv64x60_shift_right, | ||
275 | .extra = 0 }, | ||
276 | [MV64x60_CPU_SNOOP_3_WIN] = { | ||
277 | .base_reg = GT64260_CPU_SNOOP_BASE_3, | ||
278 | .size_reg = GT64260_CPU_SNOOP_SIZE_3, | ||
279 | .base_bits = 12, | ||
280 | .size_bits = 12, | ||
281 | .get_from_field = mv64x60_shift_left, | ||
282 | .map_to_field = mv64x60_shift_right, | ||
283 | .extra = 0 }, | ||
284 | /* PCI 0->System Memory Remap Windows */ | ||
285 | [MV64x60_PCI02MEM_REMAP_0_WIN] = { | ||
286 | .base_reg = MV64x60_PCI0_SLAVE_MEM_0_REMAP, | ||
287 | .size_reg = 0, | ||
288 | .base_bits = 20, | ||
289 | .size_bits = 0, | ||
290 | .get_from_field = mv64x60_mask, | ||
291 | .map_to_field = mv64x60_mask, | ||
292 | .extra = 0 }, | ||
293 | [MV64x60_PCI02MEM_REMAP_1_WIN] = { | ||
294 | .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, | ||
295 | .size_reg = 0, | ||
296 | .base_bits = 20, | ||
297 | .size_bits = 0, | ||
298 | .get_from_field = mv64x60_mask, | ||
299 | .map_to_field = mv64x60_mask, | ||
300 | .extra = 0 }, | ||
301 | [MV64x60_PCI02MEM_REMAP_2_WIN] = { | ||
302 | .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, | ||
303 | .size_reg = 0, | ||
304 | .base_bits = 20, | ||
305 | .size_bits = 0, | ||
306 | .get_from_field = mv64x60_mask, | ||
307 | .map_to_field = mv64x60_mask, | ||
308 | .extra = 0 }, | ||
309 | [MV64x60_PCI02MEM_REMAP_3_WIN] = { | ||
310 | .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, | ||
311 | .size_reg = 0, | ||
312 | .base_bits = 20, | ||
313 | .size_bits = 0, | ||
314 | .get_from_field = mv64x60_mask, | ||
315 | .map_to_field = mv64x60_mask, | ||
316 | .extra = 0 }, | ||
317 | /* PCI 1->System Memory Remap Windows */ | ||
318 | [MV64x60_PCI12MEM_REMAP_0_WIN] = { | ||
319 | .base_reg = MV64x60_PCI1_SLAVE_MEM_0_REMAP, | ||
320 | .size_reg = 0, | ||
321 | .base_bits = 20, | ||
322 | .size_bits = 0, | ||
323 | .get_from_field = mv64x60_mask, | ||
324 | .map_to_field = mv64x60_mask, | ||
325 | .extra = 0 }, | ||
326 | [MV64x60_PCI12MEM_REMAP_1_WIN] = { | ||
327 | .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, | ||
328 | .size_reg = 0, | ||
329 | .base_bits = 20, | ||
330 | .size_bits = 0, | ||
331 | .get_from_field = mv64x60_mask, | ||
332 | .map_to_field = mv64x60_mask, | ||
333 | .extra = 0 }, | ||
334 | [MV64x60_PCI12MEM_REMAP_2_WIN] = { | ||
335 | .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, | ||
336 | .size_reg = 0, | ||
337 | .base_bits = 20, | ||
338 | .size_bits = 0, | ||
339 | .get_from_field = mv64x60_mask, | ||
340 | .map_to_field = mv64x60_mask, | ||
341 | .extra = 0 }, | ||
342 | [MV64x60_PCI12MEM_REMAP_3_WIN] = { | ||
343 | .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, | ||
344 | .size_reg = 0, | ||
345 | .base_bits = 20, | ||
346 | .size_bits = 0, | ||
347 | .get_from_field = mv64x60_mask, | ||
348 | .map_to_field = mv64x60_mask, | ||
349 | .extra = 0 }, | ||
350 | /* ENET->SRAM Window (64260 doesn't have separate windows) */ | ||
351 | /* MPSC->SRAM Window (64260 doesn't have separate windows) */ | ||
352 | /* IDMA->SRAM Window (64260 doesn't have separate windows) */ | ||
353 | }; | ||
354 | |||
355 | struct mv64x60_64bit_window | ||
356 | gt64260_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = { | ||
357 | /* CPU->PCI 0 MEM Remap Windows */ | ||
358 | [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = { | ||
359 | .base_hi_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_HI, | ||
360 | .base_lo_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_LO, | ||
361 | .size_reg = 0, | ||
362 | .base_lo_bits = 12, | ||
363 | .size_bits = 0, | ||
364 | .get_from_field = mv64x60_shift_left, | ||
365 | .map_to_field = mv64x60_shift_right, | ||
366 | .extra = 0 }, | ||
367 | [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = { | ||
368 | .base_hi_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_HI, | ||
369 | .base_lo_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_LO, | ||
370 | .size_reg = 0, | ||
371 | .base_lo_bits = 12, | ||
372 | .size_bits = 0, | ||
373 | .get_from_field = mv64x60_shift_left, | ||
374 | .map_to_field = mv64x60_shift_right, | ||
375 | .extra = 0 }, | ||
376 | [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = { | ||
377 | .base_hi_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_HI, | ||
378 | .base_lo_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_LO, | ||
379 | .size_reg = 0, | ||
380 | .base_lo_bits = 12, | ||
381 | .size_bits = 0, | ||
382 | .get_from_field = mv64x60_shift_left, | ||
383 | .map_to_field = mv64x60_shift_right, | ||
384 | .extra = 0 }, | ||
385 | [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = { | ||
386 | .base_hi_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_HI, | ||
387 | .base_lo_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_LO, | ||
388 | .size_reg = 0, | ||
389 | .base_lo_bits = 12, | ||
390 | .size_bits = 0, | ||
391 | .get_from_field = mv64x60_shift_left, | ||
392 | .map_to_field = mv64x60_shift_right, | ||
393 | .extra = 0 }, | ||
394 | /* CPU->PCI 1 MEM Remap Windows */ | ||
395 | [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = { | ||
396 | .base_hi_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_HI, | ||
397 | .base_lo_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_LO, | ||
398 | .size_reg = 0, | ||
399 | .base_lo_bits = 12, | ||
400 | .size_bits = 0, | ||
401 | .get_from_field = mv64x60_shift_left, | ||
402 | .map_to_field = mv64x60_shift_right, | ||
403 | .extra = 0 }, | ||
404 | [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = { | ||
405 | .base_hi_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_HI, | ||
406 | .base_lo_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_LO, | ||
407 | .size_reg = 0, | ||
408 | .base_lo_bits = 12, | ||
409 | .size_bits = 0, | ||
410 | .get_from_field = mv64x60_shift_left, | ||
411 | .map_to_field = mv64x60_shift_right, | ||
412 | .extra = 0 }, | ||
413 | [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = { | ||
414 | .base_hi_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_HI, | ||
415 | .base_lo_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_LO, | ||
416 | .size_reg = 0, | ||
417 | .base_lo_bits = 12, | ||
418 | .size_bits = 0, | ||
419 | .get_from_field = mv64x60_shift_left, | ||
420 | .map_to_field = mv64x60_shift_right, | ||
421 | .extra = 0 }, | ||
422 | [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = { | ||
423 | .base_hi_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_HI, | ||
424 | .base_lo_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_LO, | ||
425 | .size_reg = 0, | ||
426 | .base_lo_bits = 12, | ||
427 | .size_bits = 0, | ||
428 | .get_from_field = mv64x60_shift_left, | ||
429 | .map_to_field = mv64x60_shift_right, | ||
430 | .extra = 0 }, | ||
431 | /* PCI 0->MEM Access Control Windows */ | ||
432 | [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = { | ||
433 | .base_hi_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_HI, | ||
434 | .base_lo_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_LO, | ||
435 | .size_reg = MV64x60_PCI0_ACC_CNTL_0_SIZE, | ||
436 | .base_lo_bits = 12, | ||
437 | .size_bits = 12, | ||
438 | .get_from_field = mv64x60_shift_left, | ||
439 | .map_to_field = mv64x60_shift_right, | ||
440 | .extra = 0 }, | ||
441 | [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = { | ||
442 | .base_hi_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_HI, | ||
443 | .base_lo_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_LO, | ||
444 | .size_reg = MV64x60_PCI0_ACC_CNTL_1_SIZE, | ||
445 | .base_lo_bits = 12, | ||
446 | .size_bits = 12, | ||
447 | .get_from_field = mv64x60_shift_left, | ||
448 | .map_to_field = mv64x60_shift_right, | ||
449 | .extra = 0 }, | ||
450 | [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = { | ||
451 | .base_hi_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_HI, | ||
452 | .base_lo_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_LO, | ||
453 | .size_reg = MV64x60_PCI0_ACC_CNTL_2_SIZE, | ||
454 | .base_lo_bits = 12, | ||
455 | .size_bits = 12, | ||
456 | .get_from_field = mv64x60_shift_left, | ||
457 | .map_to_field = mv64x60_shift_right, | ||
458 | .extra = 0 }, | ||
459 | [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = { | ||
460 | .base_hi_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_HI, | ||
461 | .base_lo_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_LO, | ||
462 | .size_reg = MV64x60_PCI0_ACC_CNTL_3_SIZE, | ||
463 | .base_lo_bits = 12, | ||
464 | .size_bits = 12, | ||
465 | .get_from_field = mv64x60_shift_left, | ||
466 | .map_to_field = mv64x60_shift_right, | ||
467 | .extra = 0 }, | ||
468 | /* PCI 1->MEM Access Control Windows */ | ||
469 | [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = { | ||
470 | .base_hi_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_HI, | ||
471 | .base_lo_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_LO, | ||
472 | .size_reg = MV64x60_PCI1_ACC_CNTL_0_SIZE, | ||
473 | .base_lo_bits = 12, | ||
474 | .size_bits = 12, | ||
475 | .get_from_field = mv64x60_shift_left, | ||
476 | .map_to_field = mv64x60_shift_right, | ||
477 | .extra = 0 }, | ||
478 | [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = { | ||
479 | .base_hi_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_HI, | ||
480 | .base_lo_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_LO, | ||
481 | .size_reg = MV64x60_PCI1_ACC_CNTL_1_SIZE, | ||
482 | .base_lo_bits = 12, | ||
483 | .size_bits = 12, | ||
484 | .get_from_field = mv64x60_shift_left, | ||
485 | .map_to_field = mv64x60_shift_right, | ||
486 | .extra = 0 }, | ||
487 | [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = { | ||
488 | .base_hi_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_HI, | ||
489 | .base_lo_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_LO, | ||
490 | .size_reg = MV64x60_PCI1_ACC_CNTL_2_SIZE, | ||
491 | .base_lo_bits = 12, | ||
492 | .size_bits = 12, | ||
493 | .get_from_field = mv64x60_shift_left, | ||
494 | .map_to_field = mv64x60_shift_right, | ||
495 | .extra = 0 }, | ||
496 | [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = { | ||
497 | .base_hi_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_HI, | ||
498 | .base_lo_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_LO, | ||
499 | .size_reg = MV64x60_PCI1_ACC_CNTL_3_SIZE, | ||
500 | .base_lo_bits = 12, | ||
501 | .size_bits = 12, | ||
502 | .get_from_field = mv64x60_shift_left, | ||
503 | .map_to_field = mv64x60_shift_right, | ||
504 | .extra = 0 }, | ||
505 | /* PCI 0->MEM Snoop Windows */ | ||
506 | [MV64x60_PCI02MEM_SNOOP_0_WIN] = { | ||
507 | .base_hi_reg = GT64260_PCI0_SNOOP_0_BASE_HI, | ||
508 | .base_lo_reg = GT64260_PCI0_SNOOP_0_BASE_LO, | ||
509 | .size_reg = GT64260_PCI0_SNOOP_0_SIZE, | ||
510 | .base_lo_bits = 12, | ||
511 | .size_bits = 12, | ||
512 | .get_from_field = mv64x60_shift_left, | ||
513 | .map_to_field = mv64x60_shift_right, | ||
514 | .extra = 0 }, | ||
515 | [MV64x60_PCI02MEM_SNOOP_1_WIN] = { | ||
516 | .base_hi_reg = GT64260_PCI0_SNOOP_1_BASE_HI, | ||
517 | .base_lo_reg = GT64260_PCI0_SNOOP_1_BASE_LO, | ||
518 | .size_reg = GT64260_PCI0_SNOOP_1_SIZE, | ||
519 | .base_lo_bits = 12, | ||
520 | .size_bits = 12, | ||
521 | .get_from_field = mv64x60_shift_left, | ||
522 | .map_to_field = mv64x60_shift_right, | ||
523 | .extra = 0 }, | ||
524 | [MV64x60_PCI02MEM_SNOOP_2_WIN] = { | ||
525 | .base_hi_reg = GT64260_PCI0_SNOOP_2_BASE_HI, | ||
526 | .base_lo_reg = GT64260_PCI0_SNOOP_2_BASE_LO, | ||
527 | .size_reg = GT64260_PCI0_SNOOP_2_SIZE, | ||
528 | .base_lo_bits = 12, | ||
529 | .size_bits = 12, | ||
530 | .get_from_field = mv64x60_shift_left, | ||
531 | .map_to_field = mv64x60_shift_right, | ||
532 | .extra = 0 }, | ||
533 | [MV64x60_PCI02MEM_SNOOP_3_WIN] = { | ||
534 | .base_hi_reg = GT64260_PCI0_SNOOP_3_BASE_HI, | ||
535 | .base_lo_reg = GT64260_PCI0_SNOOP_3_BASE_LO, | ||
536 | .size_reg = GT64260_PCI0_SNOOP_3_SIZE, | ||
537 | .base_lo_bits = 12, | ||
538 | .size_bits = 12, | ||
539 | .get_from_field = mv64x60_shift_left, | ||
540 | .map_to_field = mv64x60_shift_right, | ||
541 | .extra = 0 }, | ||
542 | /* PCI 1->MEM Snoop Windows */ | ||
543 | [MV64x60_PCI12MEM_SNOOP_0_WIN] = { | ||
544 | .base_hi_reg = GT64260_PCI1_SNOOP_0_BASE_HI, | ||
545 | .base_lo_reg = GT64260_PCI1_SNOOP_0_BASE_LO, | ||
546 | .size_reg = GT64260_PCI1_SNOOP_0_SIZE, | ||
547 | .base_lo_bits = 12, | ||
548 | .size_bits = 12, | ||
549 | .get_from_field = mv64x60_shift_left, | ||
550 | .map_to_field = mv64x60_shift_right, | ||
551 | .extra = 0 }, | ||
552 | [MV64x60_PCI12MEM_SNOOP_1_WIN] = { | ||
553 | .base_hi_reg = GT64260_PCI1_SNOOP_1_BASE_HI, | ||
554 | .base_lo_reg = GT64260_PCI1_SNOOP_1_BASE_LO, | ||
555 | .size_reg = GT64260_PCI1_SNOOP_1_SIZE, | ||
556 | .base_lo_bits = 12, | ||
557 | .size_bits = 12, | ||
558 | .get_from_field = mv64x60_shift_left, | ||
559 | .map_to_field = mv64x60_shift_right, | ||
560 | .extra = 0 }, | ||
561 | [MV64x60_PCI12MEM_SNOOP_2_WIN] = { | ||
562 | .base_hi_reg = GT64260_PCI1_SNOOP_2_BASE_HI, | ||
563 | .base_lo_reg = GT64260_PCI1_SNOOP_2_BASE_LO, | ||
564 | .size_reg = GT64260_PCI1_SNOOP_2_SIZE, | ||
565 | .base_lo_bits = 12, | ||
566 | .size_bits = 12, | ||
567 | .get_from_field = mv64x60_shift_left, | ||
568 | .map_to_field = mv64x60_shift_right, | ||
569 | .extra = 0 }, | ||
570 | [MV64x60_PCI12MEM_SNOOP_3_WIN] = { | ||
571 | .base_hi_reg = GT64260_PCI1_SNOOP_3_BASE_HI, | ||
572 | .base_lo_reg = GT64260_PCI1_SNOOP_3_BASE_LO, | ||
573 | .size_reg = GT64260_PCI1_SNOOP_3_SIZE, | ||
574 | .base_lo_bits = 12, | ||
575 | .size_bits = 12, | ||
576 | .get_from_field = mv64x60_shift_left, | ||
577 | .map_to_field = mv64x60_shift_right, | ||
578 | .extra = 0 }, | ||
579 | }; | ||
580 | |||
581 | struct mv64x60_32bit_window | ||
582 | mv64360_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = { | ||
583 | /* CPU->MEM Windows */ | ||
584 | [MV64x60_CPU2MEM_0_WIN] = { | ||
585 | .base_reg = MV64x60_CPU2MEM_0_BASE, | ||
586 | .size_reg = MV64x60_CPU2MEM_0_SIZE, | ||
587 | .base_bits = 16, | ||
588 | .size_bits = 16, | ||
589 | .get_from_field = mv64x60_shift_left, | ||
590 | .map_to_field = mv64x60_shift_right, | ||
591 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 0 }, | ||
592 | [MV64x60_CPU2MEM_1_WIN] = { | ||
593 | .base_reg = MV64x60_CPU2MEM_1_BASE, | ||
594 | .size_reg = MV64x60_CPU2MEM_1_SIZE, | ||
595 | .base_bits = 16, | ||
596 | .size_bits = 16, | ||
597 | .get_from_field = mv64x60_shift_left, | ||
598 | .map_to_field = mv64x60_shift_right, | ||
599 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 1 }, | ||
600 | [MV64x60_CPU2MEM_2_WIN] = { | ||
601 | .base_reg = MV64x60_CPU2MEM_2_BASE, | ||
602 | .size_reg = MV64x60_CPU2MEM_2_SIZE, | ||
603 | .base_bits = 16, | ||
604 | .size_bits = 16, | ||
605 | .get_from_field = mv64x60_shift_left, | ||
606 | .map_to_field = mv64x60_shift_right, | ||
607 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 2 }, | ||
608 | [MV64x60_CPU2MEM_3_WIN] = { | ||
609 | .base_reg = MV64x60_CPU2MEM_3_BASE, | ||
610 | .size_reg = MV64x60_CPU2MEM_3_SIZE, | ||
611 | .base_bits = 16, | ||
612 | .size_bits = 16, | ||
613 | .get_from_field = mv64x60_shift_left, | ||
614 | .map_to_field = mv64x60_shift_right, | ||
615 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 3 }, | ||
616 | /* CPU->Device Windows */ | ||
617 | [MV64x60_CPU2DEV_0_WIN] = { | ||
618 | .base_reg = MV64x60_CPU2DEV_0_BASE, | ||
619 | .size_reg = MV64x60_CPU2DEV_0_SIZE, | ||
620 | .base_bits = 16, | ||
621 | .size_bits = 16, | ||
622 | .get_from_field = mv64x60_shift_left, | ||
623 | .map_to_field = mv64x60_shift_right, | ||
624 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 4 }, | ||
625 | [MV64x60_CPU2DEV_1_WIN] = { | ||
626 | .base_reg = MV64x60_CPU2DEV_1_BASE, | ||
627 | .size_reg = MV64x60_CPU2DEV_1_SIZE, | ||
628 | .base_bits = 16, | ||
629 | .size_bits = 16, | ||
630 | .get_from_field = mv64x60_shift_left, | ||
631 | .map_to_field = mv64x60_shift_right, | ||
632 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 5 }, | ||
633 | [MV64x60_CPU2DEV_2_WIN] = { | ||
634 | .base_reg = MV64x60_CPU2DEV_2_BASE, | ||
635 | .size_reg = MV64x60_CPU2DEV_2_SIZE, | ||
636 | .base_bits = 16, | ||
637 | .size_bits = 16, | ||
638 | .get_from_field = mv64x60_shift_left, | ||
639 | .map_to_field = mv64x60_shift_right, | ||
640 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 6 }, | ||
641 | [MV64x60_CPU2DEV_3_WIN] = { | ||
642 | .base_reg = MV64x60_CPU2DEV_3_BASE, | ||
643 | .size_reg = MV64x60_CPU2DEV_3_SIZE, | ||
644 | .base_bits = 16, | ||
645 | .size_bits = 16, | ||
646 | .get_from_field = mv64x60_shift_left, | ||
647 | .map_to_field = mv64x60_shift_right, | ||
648 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 7 }, | ||
649 | /* CPU->Boot Window */ | ||
650 | [MV64x60_CPU2BOOT_WIN] = { | ||
651 | .base_reg = MV64x60_CPU2BOOT_0_BASE, | ||
652 | .size_reg = MV64x60_CPU2BOOT_0_SIZE, | ||
653 | .base_bits = 16, | ||
654 | .size_bits = 16, | ||
655 | .get_from_field = mv64x60_shift_left, | ||
656 | .map_to_field = mv64x60_shift_right, | ||
657 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 8 }, | ||
658 | /* CPU->PCI 0 Windows */ | ||
659 | [MV64x60_CPU2PCI0_IO_WIN] = { | ||
660 | .base_reg = MV64x60_CPU2PCI0_IO_BASE, | ||
661 | .size_reg = MV64x60_CPU2PCI0_IO_SIZE, | ||
662 | .base_bits = 16, | ||
663 | .size_bits = 16, | ||
664 | .get_from_field = mv64x60_shift_left, | ||
665 | .map_to_field = mv64x60_shift_right, | ||
666 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 9 }, | ||
667 | [MV64x60_CPU2PCI0_MEM_0_WIN] = { | ||
668 | .base_reg = MV64x60_CPU2PCI0_MEM_0_BASE, | ||
669 | .size_reg = MV64x60_CPU2PCI0_MEM_0_SIZE, | ||
670 | .base_bits = 16, | ||
671 | .size_bits = 16, | ||
672 | .get_from_field = mv64x60_shift_left, | ||
673 | .map_to_field = mv64x60_shift_right, | ||
674 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 10 }, | ||
675 | [MV64x60_CPU2PCI0_MEM_1_WIN] = { | ||
676 | .base_reg = MV64x60_CPU2PCI0_MEM_1_BASE, | ||
677 | .size_reg = MV64x60_CPU2PCI0_MEM_1_SIZE, | ||
678 | .base_bits = 16, | ||
679 | .size_bits = 16, | ||
680 | .get_from_field = mv64x60_shift_left, | ||
681 | .map_to_field = mv64x60_shift_right, | ||
682 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 11 }, | ||
683 | [MV64x60_CPU2PCI0_MEM_2_WIN] = { | ||
684 | .base_reg = MV64x60_CPU2PCI0_MEM_2_BASE, | ||
685 | .size_reg = MV64x60_CPU2PCI0_MEM_2_SIZE, | ||
686 | .base_bits = 16, | ||
687 | .size_bits = 16, | ||
688 | .get_from_field = mv64x60_shift_left, | ||
689 | .map_to_field = mv64x60_shift_right, | ||
690 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 12 }, | ||
691 | [MV64x60_CPU2PCI0_MEM_3_WIN] = { | ||
692 | .base_reg = MV64x60_CPU2PCI0_MEM_3_BASE, | ||
693 | .size_reg = MV64x60_CPU2PCI0_MEM_3_SIZE, | ||
694 | .base_bits = 16, | ||
695 | .size_bits = 16, | ||
696 | .get_from_field = mv64x60_shift_left, | ||
697 | .map_to_field = mv64x60_shift_right, | ||
698 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 13 }, | ||
699 | /* CPU->PCI 1 Windows */ | ||
700 | [MV64x60_CPU2PCI1_IO_WIN] = { | ||
701 | .base_reg = MV64x60_CPU2PCI1_IO_BASE, | ||
702 | .size_reg = MV64x60_CPU2PCI1_IO_SIZE, | ||
703 | .base_bits = 16, | ||
704 | .size_bits = 16, | ||
705 | .get_from_field = mv64x60_shift_left, | ||
706 | .map_to_field = mv64x60_shift_right, | ||
707 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 14 }, | ||
708 | [MV64x60_CPU2PCI1_MEM_0_WIN] = { | ||
709 | .base_reg = MV64x60_CPU2PCI1_MEM_0_BASE, | ||
710 | .size_reg = MV64x60_CPU2PCI1_MEM_0_SIZE, | ||
711 | .base_bits = 16, | ||
712 | .size_bits = 16, | ||
713 | .get_from_field = mv64x60_shift_left, | ||
714 | .map_to_field = mv64x60_shift_right, | ||
715 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 15 }, | ||
716 | [MV64x60_CPU2PCI1_MEM_1_WIN] = { | ||
717 | .base_reg = MV64x60_CPU2PCI1_MEM_1_BASE, | ||
718 | .size_reg = MV64x60_CPU2PCI1_MEM_1_SIZE, | ||
719 | .base_bits = 16, | ||
720 | .size_bits = 16, | ||
721 | .get_from_field = mv64x60_shift_left, | ||
722 | .map_to_field = mv64x60_shift_right, | ||
723 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 16 }, | ||
724 | [MV64x60_CPU2PCI1_MEM_2_WIN] = { | ||
725 | .base_reg = MV64x60_CPU2PCI1_MEM_2_BASE, | ||
726 | .size_reg = MV64x60_CPU2PCI1_MEM_2_SIZE, | ||
727 | .base_bits = 16, | ||
728 | .size_bits = 16, | ||
729 | .get_from_field = mv64x60_shift_left, | ||
730 | .map_to_field = mv64x60_shift_right, | ||
731 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 17 }, | ||
732 | [MV64x60_CPU2PCI1_MEM_3_WIN] = { | ||
733 | .base_reg = MV64x60_CPU2PCI1_MEM_3_BASE, | ||
734 | .size_reg = MV64x60_CPU2PCI1_MEM_3_SIZE, | ||
735 | .base_bits = 16, | ||
736 | .size_bits = 16, | ||
737 | .get_from_field = mv64x60_shift_left, | ||
738 | .map_to_field = mv64x60_shift_right, | ||
739 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 18 }, | ||
740 | /* CPU->SRAM Window */ | ||
741 | [MV64x60_CPU2SRAM_WIN] = { | ||
742 | .base_reg = MV64360_CPU2SRAM_BASE, | ||
743 | .size_reg = 0, | ||
744 | .base_bits = 16, | ||
745 | .size_bits = 0, | ||
746 | .get_from_field = mv64x60_shift_left, | ||
747 | .map_to_field = mv64x60_shift_right, | ||
748 | .extra = MV64x60_EXTRA_CPUWIN_ENAB | 19 }, | ||
749 | /* CPU->PCI 0 Remap I/O Window */ | ||
750 | [MV64x60_CPU2PCI0_IO_REMAP_WIN] = { | ||
751 | .base_reg = MV64x60_CPU2PCI0_IO_REMAP, | ||
752 | .size_reg = 0, | ||
753 | .base_bits = 16, | ||
754 | .size_bits = 0, | ||
755 | .get_from_field = mv64x60_shift_left, | ||
756 | .map_to_field = mv64x60_shift_right, | ||
757 | .extra = 0 }, | ||
758 | /* CPU->PCI 1 Remap I/O Window */ | ||
759 | [MV64x60_CPU2PCI1_IO_REMAP_WIN] = { | ||
760 | .base_reg = MV64x60_CPU2PCI1_IO_REMAP, | ||
761 | .size_reg = 0, | ||
762 | .base_bits = 16, | ||
763 | .size_bits = 0, | ||
764 | .get_from_field = mv64x60_shift_left, | ||
765 | .map_to_field = mv64x60_shift_right, | ||
766 | .extra = 0 }, | ||
767 | /* CPU Memory Protection Windows */ | ||
768 | [MV64x60_CPU_PROT_0_WIN] = { | ||
769 | .base_reg = MV64x60_CPU_PROT_BASE_0, | ||
770 | .size_reg = MV64x60_CPU_PROT_SIZE_0, | ||
771 | .base_bits = 16, | ||
772 | .size_bits = 16, | ||
773 | .get_from_field = mv64x60_shift_left, | ||
774 | .map_to_field = mv64x60_shift_right, | ||
775 | .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 }, | ||
776 | [MV64x60_CPU_PROT_1_WIN] = { | ||
777 | .base_reg = MV64x60_CPU_PROT_BASE_1, | ||
778 | .size_reg = MV64x60_CPU_PROT_SIZE_1, | ||
779 | .base_bits = 16, | ||
780 | .size_bits = 16, | ||
781 | .get_from_field = mv64x60_shift_left, | ||
782 | .map_to_field = mv64x60_shift_right, | ||
783 | .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 }, | ||
784 | [MV64x60_CPU_PROT_2_WIN] = { | ||
785 | .base_reg = MV64x60_CPU_PROT_BASE_2, | ||
786 | .size_reg = MV64x60_CPU_PROT_SIZE_2, | ||
787 | .base_bits = 16, | ||
788 | .size_bits = 16, | ||
789 | .get_from_field = mv64x60_shift_left, | ||
790 | .map_to_field = mv64x60_shift_right, | ||
791 | .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 }, | ||
792 | [MV64x60_CPU_PROT_3_WIN] = { | ||
793 | .base_reg = MV64x60_CPU_PROT_BASE_3, | ||
794 | .size_reg = MV64x60_CPU_PROT_SIZE_3, | ||
795 | .base_bits = 16, | ||
796 | .size_bits = 16, | ||
797 | .get_from_field = mv64x60_shift_left, | ||
798 | .map_to_field = mv64x60_shift_right, | ||
799 | .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 }, | ||
800 | /* CPU Snoop Windows -- don't exist on 64360 */ | ||
801 | /* PCI 0->System Memory Remap Windows */ | ||
802 | [MV64x60_PCI02MEM_REMAP_0_WIN] = { | ||
803 | .base_reg = MV64x60_PCI0_SLAVE_MEM_0_REMAP, | ||
804 | .size_reg = 0, | ||
805 | .base_bits = 20, | ||
806 | .size_bits = 0, | ||
807 | .get_from_field = mv64x60_mask, | ||
808 | .map_to_field = mv64x60_mask, | ||
809 | .extra = 0 }, | ||
810 | [MV64x60_PCI02MEM_REMAP_1_WIN] = { | ||
811 | .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, | ||
812 | .size_reg = 0, | ||
813 | .base_bits = 20, | ||
814 | .size_bits = 0, | ||
815 | .get_from_field = mv64x60_mask, | ||
816 | .map_to_field = mv64x60_mask, | ||
817 | .extra = 0 }, | ||
818 | [MV64x60_PCI02MEM_REMAP_2_WIN] = { | ||
819 | .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, | ||
820 | .size_reg = 0, | ||
821 | .base_bits = 20, | ||
822 | .size_bits = 0, | ||
823 | .get_from_field = mv64x60_mask, | ||
824 | .map_to_field = mv64x60_mask, | ||
825 | .extra = 0 }, | ||
826 | [MV64x60_PCI02MEM_REMAP_3_WIN] = { | ||
827 | .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP, | ||
828 | .size_reg = 0, | ||
829 | .base_bits = 20, | ||
830 | .size_bits = 0, | ||
831 | .get_from_field = mv64x60_mask, | ||
832 | .map_to_field = mv64x60_mask, | ||
833 | .extra = 0 }, | ||
834 | /* PCI 1->System Memory Remap Windows */ | ||
835 | [MV64x60_PCI12MEM_REMAP_0_WIN] = { | ||
836 | .base_reg = MV64x60_PCI1_SLAVE_MEM_0_REMAP, | ||
837 | .size_reg = 0, | ||
838 | .base_bits = 20, | ||
839 | .size_bits = 0, | ||
840 | .get_from_field = mv64x60_mask, | ||
841 | .map_to_field = mv64x60_mask, | ||
842 | .extra = 0 }, | ||
843 | [MV64x60_PCI12MEM_REMAP_1_WIN] = { | ||
844 | .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, | ||
845 | .size_reg = 0, | ||
846 | .base_bits = 20, | ||
847 | .size_bits = 0, | ||
848 | .get_from_field = mv64x60_mask, | ||
849 | .map_to_field = mv64x60_mask, | ||
850 | .extra = 0 }, | ||
851 | [MV64x60_PCI12MEM_REMAP_2_WIN] = { | ||
852 | .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, | ||
853 | .size_reg = 0, | ||
854 | .base_bits = 20, | ||
855 | .size_bits = 0, | ||
856 | .get_from_field = mv64x60_mask, | ||
857 | .map_to_field = mv64x60_mask, | ||
858 | .extra = 0 }, | ||
859 | [MV64x60_PCI12MEM_REMAP_3_WIN] = { | ||
860 | .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP, | ||
861 | .size_reg = 0, | ||
862 | .base_bits = 20, | ||
863 | .size_bits = 0, | ||
864 | .get_from_field = mv64x60_mask, | ||
865 | .map_to_field = mv64x60_mask, | ||
866 | .extra = 0 }, | ||
867 | /* ENET->System Memory Windows */ | ||
868 | [MV64x60_ENET2MEM_0_WIN] = { | ||
869 | .base_reg = MV64360_ENET2MEM_0_BASE, | ||
870 | .size_reg = MV64360_ENET2MEM_0_SIZE, | ||
871 | .base_bits = 16, | ||
872 | .size_bits = 16, | ||
873 | .get_from_field = mv64x60_mask, | ||
874 | .map_to_field = mv64x60_mask, | ||
875 | .extra = MV64x60_EXTRA_ENET_ENAB | 0 }, | ||
876 | [MV64x60_ENET2MEM_1_WIN] = { | ||
877 | .base_reg = MV64360_ENET2MEM_1_BASE, | ||
878 | .size_reg = MV64360_ENET2MEM_1_SIZE, | ||
879 | .base_bits = 16, | ||
880 | .size_bits = 16, | ||
881 | .get_from_field = mv64x60_mask, | ||
882 | .map_to_field = mv64x60_mask, | ||
883 | .extra = MV64x60_EXTRA_ENET_ENAB | 1 }, | ||
884 | [MV64x60_ENET2MEM_2_WIN] = { | ||
885 | .base_reg = MV64360_ENET2MEM_2_BASE, | ||
886 | .size_reg = MV64360_ENET2MEM_2_SIZE, | ||
887 | .base_bits = 16, | ||
888 | .size_bits = 16, | ||
889 | .get_from_field = mv64x60_mask, | ||
890 | .map_to_field = mv64x60_mask, | ||
891 | .extra = MV64x60_EXTRA_ENET_ENAB | 2 }, | ||
892 | [MV64x60_ENET2MEM_3_WIN] = { | ||
893 | .base_reg = MV64360_ENET2MEM_3_BASE, | ||
894 | .size_reg = MV64360_ENET2MEM_3_SIZE, | ||
895 | .base_bits = 16, | ||
896 | .size_bits = 16, | ||
897 | .get_from_field = mv64x60_mask, | ||
898 | .map_to_field = mv64x60_mask, | ||
899 | .extra = MV64x60_EXTRA_ENET_ENAB | 3 }, | ||
900 | [MV64x60_ENET2MEM_4_WIN] = { | ||
901 | .base_reg = MV64360_ENET2MEM_4_BASE, | ||
902 | .size_reg = MV64360_ENET2MEM_4_SIZE, | ||
903 | .base_bits = 16, | ||
904 | .size_bits = 16, | ||
905 | .get_from_field = mv64x60_mask, | ||
906 | .map_to_field = mv64x60_mask, | ||
907 | .extra = MV64x60_EXTRA_ENET_ENAB | 4 }, | ||
908 | [MV64x60_ENET2MEM_5_WIN] = { | ||
909 | .base_reg = MV64360_ENET2MEM_5_BASE, | ||
910 | .size_reg = MV64360_ENET2MEM_5_SIZE, | ||
911 | .base_bits = 16, | ||
912 | .size_bits = 16, | ||
913 | .get_from_field = mv64x60_mask, | ||
914 | .map_to_field = mv64x60_mask, | ||
915 | .extra = MV64x60_EXTRA_ENET_ENAB | 5 }, | ||
916 | /* MPSC->System Memory Windows */ | ||
917 | [MV64x60_MPSC2MEM_0_WIN] = { | ||
918 | .base_reg = MV64360_MPSC2MEM_0_BASE, | ||
919 | .size_reg = MV64360_MPSC2MEM_0_SIZE, | ||
920 | .base_bits = 16, | ||
921 | .size_bits = 16, | ||
922 | .get_from_field = mv64x60_mask, | ||
923 | .map_to_field = mv64x60_mask, | ||
924 | .extra = MV64x60_EXTRA_MPSC_ENAB | 0 }, | ||
925 | [MV64x60_MPSC2MEM_1_WIN] = { | ||
926 | .base_reg = MV64360_MPSC2MEM_1_BASE, | ||
927 | .size_reg = MV64360_MPSC2MEM_1_SIZE, | ||
928 | .base_bits = 16, | ||
929 | .size_bits = 16, | ||
930 | .get_from_field = mv64x60_mask, | ||
931 | .map_to_field = mv64x60_mask, | ||
932 | .extra = MV64x60_EXTRA_MPSC_ENAB | 1 }, | ||
933 | [MV64x60_MPSC2MEM_2_WIN] = { | ||
934 | .base_reg = MV64360_MPSC2MEM_2_BASE, | ||
935 | .size_reg = MV64360_MPSC2MEM_2_SIZE, | ||
936 | .base_bits = 16, | ||
937 | .size_bits = 16, | ||
938 | .get_from_field = mv64x60_mask, | ||
939 | .map_to_field = mv64x60_mask, | ||
940 | .extra = MV64x60_EXTRA_MPSC_ENAB | 2 }, | ||
941 | [MV64x60_MPSC2MEM_3_WIN] = { | ||
942 | .base_reg = MV64360_MPSC2MEM_3_BASE, | ||
943 | .size_reg = MV64360_MPSC2MEM_3_SIZE, | ||
944 | .base_bits = 16, | ||
945 | .size_bits = 16, | ||
946 | .get_from_field = mv64x60_mask, | ||
947 | .map_to_field = mv64x60_mask, | ||
948 | .extra = MV64x60_EXTRA_MPSC_ENAB | 3 }, | ||
949 | /* IDMA->System Memory Windows */ | ||
950 | [MV64x60_IDMA2MEM_0_WIN] = { | ||
951 | .base_reg = MV64360_IDMA2MEM_0_BASE, | ||
952 | .size_reg = MV64360_IDMA2MEM_0_SIZE, | ||
953 | .base_bits = 16, | ||
954 | .size_bits = 16, | ||
955 | .get_from_field = mv64x60_mask, | ||
956 | .map_to_field = mv64x60_mask, | ||
957 | .extra = MV64x60_EXTRA_IDMA_ENAB | 0 }, | ||
958 | [MV64x60_IDMA2MEM_1_WIN] = { | ||
959 | .base_reg = MV64360_IDMA2MEM_1_BASE, | ||
960 | .size_reg = MV64360_IDMA2MEM_1_SIZE, | ||
961 | .base_bits = 16, | ||
962 | .size_bits = 16, | ||
963 | .get_from_field = mv64x60_mask, | ||
964 | .map_to_field = mv64x60_mask, | ||
965 | .extra = MV64x60_EXTRA_IDMA_ENAB | 1 }, | ||
966 | [MV64x60_IDMA2MEM_2_WIN] = { | ||
967 | .base_reg = MV64360_IDMA2MEM_2_BASE, | ||
968 | .size_reg = MV64360_IDMA2MEM_2_SIZE, | ||
969 | .base_bits = 16, | ||
970 | .size_bits = 16, | ||
971 | .get_from_field = mv64x60_mask, | ||
972 | .map_to_field = mv64x60_mask, | ||
973 | .extra = MV64x60_EXTRA_IDMA_ENAB | 2 }, | ||
974 | [MV64x60_IDMA2MEM_3_WIN] = { | ||
975 | .base_reg = MV64360_IDMA2MEM_3_BASE, | ||
976 | .size_reg = MV64360_IDMA2MEM_3_SIZE, | ||
977 | .base_bits = 16, | ||
978 | .size_bits = 16, | ||
979 | .get_from_field = mv64x60_mask, | ||
980 | .map_to_field = mv64x60_mask, | ||
981 | .extra = MV64x60_EXTRA_IDMA_ENAB | 3 }, | ||
982 | [MV64x60_IDMA2MEM_4_WIN] = { | ||
983 | .base_reg = MV64360_IDMA2MEM_4_BASE, | ||
984 | .size_reg = MV64360_IDMA2MEM_4_SIZE, | ||
985 | .base_bits = 16, | ||
986 | .size_bits = 16, | ||
987 | .get_from_field = mv64x60_mask, | ||
988 | .map_to_field = mv64x60_mask, | ||
989 | .extra = MV64x60_EXTRA_IDMA_ENAB | 4 }, | ||
990 | [MV64x60_IDMA2MEM_5_WIN] = { | ||
991 | .base_reg = MV64360_IDMA2MEM_5_BASE, | ||
992 | .size_reg = MV64360_IDMA2MEM_5_SIZE, | ||
993 | .base_bits = 16, | ||
994 | .size_bits = 16, | ||
995 | .get_from_field = mv64x60_mask, | ||
996 | .map_to_field = mv64x60_mask, | ||
997 | .extra = MV64x60_EXTRA_IDMA_ENAB | 5 }, | ||
998 | [MV64x60_IDMA2MEM_6_WIN] = { | ||
999 | .base_reg = MV64360_IDMA2MEM_6_BASE, | ||
1000 | .size_reg = MV64360_IDMA2MEM_6_SIZE, | ||
1001 | .base_bits = 16, | ||
1002 | .size_bits = 16, | ||
1003 | .get_from_field = mv64x60_mask, | ||
1004 | .map_to_field = mv64x60_mask, | ||
1005 | .extra = MV64x60_EXTRA_IDMA_ENAB | 6 }, | ||
1006 | [MV64x60_IDMA2MEM_7_WIN] = { | ||
1007 | .base_reg = MV64360_IDMA2MEM_7_BASE, | ||
1008 | .size_reg = MV64360_IDMA2MEM_7_SIZE, | ||
1009 | .base_bits = 16, | ||
1010 | .size_bits = 16, | ||
1011 | .get_from_field = mv64x60_mask, | ||
1012 | .map_to_field = mv64x60_mask, | ||
1013 | .extra = MV64x60_EXTRA_IDMA_ENAB | 7 }, | ||
1014 | }; | ||
1015 | |||
1016 | struct mv64x60_64bit_window | ||
1017 | mv64360_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = { | ||
1018 | /* CPU->PCI 0 MEM Remap Windows */ | ||
1019 | [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = { | ||
1020 | .base_hi_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_HI, | ||
1021 | .base_lo_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_LO, | ||
1022 | .size_reg = 0, | ||
1023 | .base_lo_bits = 16, | ||
1024 | .size_bits = 0, | ||
1025 | .get_from_field = mv64x60_shift_left, | ||
1026 | .map_to_field = mv64x60_shift_right, | ||
1027 | .extra = 0 }, | ||
1028 | [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = { | ||
1029 | .base_hi_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_HI, | ||
1030 | .base_lo_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_LO, | ||
1031 | .size_reg = 0, | ||
1032 | .base_lo_bits = 16, | ||
1033 | .size_bits = 0, | ||
1034 | .get_from_field = mv64x60_shift_left, | ||
1035 | .map_to_field = mv64x60_shift_right, | ||
1036 | .extra = 0 }, | ||
1037 | [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = { | ||
1038 | .base_hi_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_HI, | ||
1039 | .base_lo_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_LO, | ||
1040 | .size_reg = 0, | ||
1041 | .base_lo_bits = 16, | ||
1042 | .size_bits = 0, | ||
1043 | .get_from_field = mv64x60_shift_left, | ||
1044 | .map_to_field = mv64x60_shift_right, | ||
1045 | .extra = 0 }, | ||
1046 | [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = { | ||
1047 | .base_hi_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_HI, | ||
1048 | .base_lo_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_LO, | ||
1049 | .size_reg = 0, | ||
1050 | .base_lo_bits = 16, | ||
1051 | .size_bits = 0, | ||
1052 | .get_from_field = mv64x60_shift_left, | ||
1053 | .map_to_field = mv64x60_shift_right, | ||
1054 | .extra = 0 }, | ||
1055 | /* CPU->PCI 1 MEM Remap Windows */ | ||
1056 | [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = { | ||
1057 | .base_hi_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_HI, | ||
1058 | .base_lo_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_LO, | ||
1059 | .size_reg = 0, | ||
1060 | .base_lo_bits = 16, | ||
1061 | .size_bits = 0, | ||
1062 | .get_from_field = mv64x60_shift_left, | ||
1063 | .map_to_field = mv64x60_shift_right, | ||
1064 | .extra = 0 }, | ||
1065 | [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = { | ||
1066 | .base_hi_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_HI, | ||
1067 | .base_lo_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_LO, | ||
1068 | .size_reg = 0, | ||
1069 | .base_lo_bits = 16, | ||
1070 | .size_bits = 0, | ||
1071 | .get_from_field = mv64x60_shift_left, | ||
1072 | .map_to_field = mv64x60_shift_right, | ||
1073 | .extra = 0 }, | ||
1074 | [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = { | ||
1075 | .base_hi_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_HI, | ||
1076 | .base_lo_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_LO, | ||
1077 | .size_reg = 0, | ||
1078 | .base_lo_bits = 16, | ||
1079 | .size_bits = 0, | ||
1080 | .get_from_field = mv64x60_shift_left, | ||
1081 | .map_to_field = mv64x60_shift_right, | ||
1082 | .extra = 0 }, | ||
1083 | [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = { | ||
1084 | .base_hi_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_HI, | ||
1085 | .base_lo_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_LO, | ||
1086 | .size_reg = 0, | ||
1087 | .base_lo_bits = 16, | ||
1088 | .size_bits = 0, | ||
1089 | .get_from_field = mv64x60_shift_left, | ||
1090 | .map_to_field = mv64x60_shift_right, | ||
1091 | .extra = 0 }, | ||
1092 | /* PCI 0->MEM Access Control Windows */ | ||
1093 | [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = { | ||
1094 | .base_hi_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_HI, | ||
1095 | .base_lo_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_LO, | ||
1096 | .size_reg = MV64x60_PCI0_ACC_CNTL_0_SIZE, | ||
1097 | .base_lo_bits = 20, | ||
1098 | .size_bits = 20, | ||
1099 | .get_from_field = mv64x60_mask, | ||
1100 | .map_to_field = mv64x60_mask, | ||
1101 | .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, | ||
1102 | [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = { | ||
1103 | .base_hi_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_HI, | ||
1104 | .base_lo_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_LO, | ||
1105 | .size_reg = MV64x60_PCI0_ACC_CNTL_1_SIZE, | ||
1106 | .base_lo_bits = 20, | ||
1107 | .size_bits = 20, | ||
1108 | .get_from_field = mv64x60_mask, | ||
1109 | .map_to_field = mv64x60_mask, | ||
1110 | .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, | ||
1111 | [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = { | ||
1112 | .base_hi_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_HI, | ||
1113 | .base_lo_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_LO, | ||
1114 | .size_reg = MV64x60_PCI0_ACC_CNTL_2_SIZE, | ||
1115 | .base_lo_bits = 20, | ||
1116 | .size_bits = 20, | ||
1117 | .get_from_field = mv64x60_mask, | ||
1118 | .map_to_field = mv64x60_mask, | ||
1119 | .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, | ||
1120 | [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = { | ||
1121 | .base_hi_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_HI, | ||
1122 | .base_lo_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_LO, | ||
1123 | .size_reg = MV64x60_PCI0_ACC_CNTL_3_SIZE, | ||
1124 | .base_lo_bits = 20, | ||
1125 | .size_bits = 20, | ||
1126 | .get_from_field = mv64x60_mask, | ||
1127 | .map_to_field = mv64x60_mask, | ||
1128 | .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, | ||
1129 | /* PCI 1->MEM Access Control Windows */ | ||
1130 | [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = { | ||
1131 | .base_hi_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_HI, | ||
1132 | .base_lo_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_LO, | ||
1133 | .size_reg = MV64x60_PCI1_ACC_CNTL_0_SIZE, | ||
1134 | .base_lo_bits = 20, | ||
1135 | .size_bits = 20, | ||
1136 | .get_from_field = mv64x60_mask, | ||
1137 | .map_to_field = mv64x60_mask, | ||
1138 | .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, | ||
1139 | [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = { | ||
1140 | .base_hi_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_HI, | ||
1141 | .base_lo_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_LO, | ||
1142 | .size_reg = MV64x60_PCI1_ACC_CNTL_1_SIZE, | ||
1143 | .base_lo_bits = 20, | ||
1144 | .size_bits = 20, | ||
1145 | .get_from_field = mv64x60_mask, | ||
1146 | .map_to_field = mv64x60_mask, | ||
1147 | .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, | ||
1148 | [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = { | ||
1149 | .base_hi_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_HI, | ||
1150 | .base_lo_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_LO, | ||
1151 | .size_reg = MV64x60_PCI1_ACC_CNTL_2_SIZE, | ||
1152 | .base_lo_bits = 20, | ||
1153 | .size_bits = 20, | ||
1154 | .get_from_field = mv64x60_mask, | ||
1155 | .map_to_field = mv64x60_mask, | ||
1156 | .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, | ||
1157 | [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = { | ||
1158 | .base_hi_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_HI, | ||
1159 | .base_lo_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_LO, | ||
1160 | .size_reg = MV64x60_PCI1_ACC_CNTL_3_SIZE, | ||
1161 | .base_lo_bits = 20, | ||
1162 | .size_bits = 20, | ||
1163 | .get_from_field = mv64x60_mask, | ||
1164 | .map_to_field = mv64x60_mask, | ||
1165 | .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 }, | ||
1166 | /* PCI 0->MEM Snoop Windows -- don't exist on 64360 */ | ||
1167 | /* PCI 1->MEM Snoop Windows -- don't exist on 64360 */ | ||
1168 | }; | ||
diff --git a/arch/ppc/syslib/ocp.c b/arch/ppc/syslib/ocp.c new file mode 100644 index 000000000000..a5156c5179a6 --- /dev/null +++ b/arch/ppc/syslib/ocp.c | |||
@@ -0,0 +1,485 @@ | |||
1 | /* | ||
2 | * ocp.c | ||
3 | * | ||
4 | * (c) Benjamin Herrenschmidt (benh@kernel.crashing.org) | ||
5 | * Mipsys - France | ||
6 | * | ||
7 | * Derived from work (c) Armin Kuster akuster@pacbell.net | ||
8 | * | ||
9 | * Additional support and port to 2.6 LDM/sysfs by | ||
10 | * Matt Porter <mporter@kernel.crashing.org> | ||
11 | * Copyright 2004 MontaVista Software, Inc. | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | * | ||
18 | * OCP (On Chip Peripheral) is a software emulated "bus" with a | ||
19 | * pseudo discovery method for dumb peripherals. Usually these type | ||
20 | * of peripherals are found on embedded SoC (System On a Chip) | ||
21 | * processors or highly integrated system controllers that have | ||
22 | * a host bridge and many peripherals. Common examples where | ||
23 | * this is already used include the PPC4xx, PPC85xx, MPC52xx, | ||
24 | * and MV64xxx parts. | ||
25 | * | ||
26 | * This subsystem creates a standard OCP bus type within the | ||
27 | * device model. The devices on the OCP bus are seeded by an | ||
28 | * an initial OCP device array created by the arch-specific | ||
29 | * Device entries can be added/removed/modified through OCP | ||
30 | * helper functions to accomodate system and board-specific | ||
31 | * parameters commonly found in embedded systems. OCP also | ||
32 | * provides a standard method for devices to describe extended | ||
33 | * attributes about themselves to the system. A standard access | ||
34 | * method allows OCP drivers to obtain the information, both | ||
35 | * SoC-specific and system/board-specific, needed for operation. | ||
36 | */ | ||
37 | |||
38 | #include <linux/module.h> | ||
39 | #include <linux/config.h> | ||
40 | #include <linux/list.h> | ||
41 | #include <linux/miscdevice.h> | ||
42 | #include <linux/slab.h> | ||
43 | #include <linux/types.h> | ||
44 | #include <linux/init.h> | ||
45 | #include <linux/pm.h> | ||
46 | #include <linux/bootmem.h> | ||
47 | #include <linux/device.h> | ||
48 | |||
49 | #include <asm/io.h> | ||
50 | #include <asm/ocp.h> | ||
51 | #include <asm/errno.h> | ||
52 | #include <asm/rwsem.h> | ||
53 | #include <asm/semaphore.h> | ||
54 | |||
55 | //#define DBG(x) printk x | ||
56 | #define DBG(x) | ||
57 | |||
58 | extern int mem_init_done; | ||
59 | |||
60 | extern struct ocp_def core_ocp[]; /* Static list of devices, provided by | ||
61 | CPU core */ | ||
62 | |||
63 | LIST_HEAD(ocp_devices); /* List of all OCP devices */ | ||
64 | DECLARE_RWSEM(ocp_devices_sem); /* Global semaphores for those lists */ | ||
65 | |||
66 | static int ocp_inited; | ||
67 | |||
68 | /* Sysfs support */ | ||
69 | #define OCP_DEF_ATTR(field, format_string) \ | ||
70 | static ssize_t \ | ||
71 | show_##field(struct device *dev, char *buf) \ | ||
72 | { \ | ||
73 | struct ocp_device *odev = to_ocp_dev(dev); \ | ||
74 | \ | ||
75 | return sprintf(buf, format_string, odev->def->field); \ | ||
76 | } \ | ||
77 | static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL); | ||
78 | |||
79 | OCP_DEF_ATTR(vendor, "0x%04x\n"); | ||
80 | OCP_DEF_ATTR(function, "0x%04x\n"); | ||
81 | OCP_DEF_ATTR(index, "0x%04x\n"); | ||
82 | #ifdef CONFIG_PTE_64BIT | ||
83 | OCP_DEF_ATTR(paddr, "0x%016Lx\n"); | ||
84 | #else | ||
85 | OCP_DEF_ATTR(paddr, "0x%08lx\n"); | ||
86 | #endif | ||
87 | OCP_DEF_ATTR(irq, "%d\n"); | ||
88 | OCP_DEF_ATTR(pm, "%lu\n"); | ||
89 | |||
90 | void ocp_create_sysfs_dev_files(struct ocp_device *odev) | ||
91 | { | ||
92 | struct device *dev = &odev->dev; | ||
93 | |||
94 | /* Current OCP device def attributes */ | ||
95 | device_create_file(dev, &dev_attr_vendor); | ||
96 | device_create_file(dev, &dev_attr_function); | ||
97 | device_create_file(dev, &dev_attr_index); | ||
98 | device_create_file(dev, &dev_attr_paddr); | ||
99 | device_create_file(dev, &dev_attr_irq); | ||
100 | device_create_file(dev, &dev_attr_pm); | ||
101 | /* Current OCP device additions attributes */ | ||
102 | if (odev->def->additions && odev->def->show) | ||
103 | odev->def->show(dev); | ||
104 | } | ||
105 | |||
106 | /** | ||
107 | * ocp_device_match - Match one driver to one device | ||
108 | * @drv: driver to match | ||
109 | * @dev: device to match | ||
110 | * | ||
111 | * This function returns 0 if the driver and device don't match | ||
112 | */ | ||
113 | static int | ||
114 | ocp_device_match(struct device *dev, struct device_driver *drv) | ||
115 | { | ||
116 | struct ocp_device *ocp_dev = to_ocp_dev(dev); | ||
117 | struct ocp_driver *ocp_drv = to_ocp_drv(drv); | ||
118 | const struct ocp_device_id *ids = ocp_drv->id_table; | ||
119 | |||
120 | if (!ids) | ||
121 | return 0; | ||
122 | |||
123 | while (ids->vendor || ids->function) { | ||
124 | if ((ids->vendor == OCP_ANY_ID | ||
125 | || ids->vendor == ocp_dev->def->vendor) | ||
126 | && (ids->function == OCP_ANY_ID | ||
127 | || ids->function == ocp_dev->def->function)) | ||
128 | return 1; | ||
129 | ids++; | ||
130 | } | ||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | static int | ||
135 | ocp_device_probe(struct device *dev) | ||
136 | { | ||
137 | int error = 0; | ||
138 | struct ocp_driver *drv; | ||
139 | struct ocp_device *ocp_dev; | ||
140 | |||
141 | drv = to_ocp_drv(dev->driver); | ||
142 | ocp_dev = to_ocp_dev(dev); | ||
143 | |||
144 | if (drv->probe) { | ||
145 | error = drv->probe(ocp_dev); | ||
146 | if (error >= 0) { | ||
147 | ocp_dev->driver = drv; | ||
148 | error = 0; | ||
149 | } | ||
150 | } | ||
151 | return error; | ||
152 | } | ||
153 | |||
154 | static int | ||
155 | ocp_device_remove(struct device *dev) | ||
156 | { | ||
157 | struct ocp_device *ocp_dev = to_ocp_dev(dev); | ||
158 | |||
159 | if (ocp_dev->driver) { | ||
160 | if (ocp_dev->driver->remove) | ||
161 | ocp_dev->driver->remove(ocp_dev); | ||
162 | ocp_dev->driver = NULL; | ||
163 | } | ||
164 | return 0; | ||
165 | } | ||
166 | |||
167 | static int | ||
168 | ocp_device_suspend(struct device *dev, u32 state) | ||
169 | { | ||
170 | struct ocp_device *ocp_dev = to_ocp_dev(dev); | ||
171 | struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver); | ||
172 | |||
173 | if (dev->driver && ocp_drv->suspend) | ||
174 | return ocp_drv->suspend(ocp_dev, state); | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static int | ||
179 | ocp_device_resume(struct device *dev) | ||
180 | { | ||
181 | struct ocp_device *ocp_dev = to_ocp_dev(dev); | ||
182 | struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver); | ||
183 | |||
184 | if (dev->driver && ocp_drv->resume) | ||
185 | return ocp_drv->resume(ocp_dev); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | struct bus_type ocp_bus_type = { | ||
190 | .name = "ocp", | ||
191 | .match = ocp_device_match, | ||
192 | .suspend = ocp_device_suspend, | ||
193 | .resume = ocp_device_resume, | ||
194 | }; | ||
195 | |||
196 | /** | ||
197 | * ocp_register_driver - Register an OCP driver | ||
198 | * @drv: pointer to statically defined ocp_driver structure | ||
199 | * | ||
200 | * The driver's probe() callback is called either recursively | ||
201 | * by this function or upon later call of ocp_driver_init | ||
202 | * | ||
203 | * NOTE: Detection of devices is a 2 pass step on this implementation, | ||
204 | * hotswap isn't supported. First, all OCP devices are put in the device | ||
205 | * list, _then_ all drivers are probed on each match. | ||
206 | */ | ||
207 | int | ||
208 | ocp_register_driver(struct ocp_driver *drv) | ||
209 | { | ||
210 | /* initialize common driver fields */ | ||
211 | drv->driver.name = drv->name; | ||
212 | drv->driver.bus = &ocp_bus_type; | ||
213 | drv->driver.probe = ocp_device_probe; | ||
214 | drv->driver.remove = ocp_device_remove; | ||
215 | |||
216 | /* register with core */ | ||
217 | return driver_register(&drv->driver); | ||
218 | } | ||
219 | |||
220 | /** | ||
221 | * ocp_unregister_driver - Unregister an OCP driver | ||
222 | * @drv: pointer to statically defined ocp_driver structure | ||
223 | * | ||
224 | * The driver's remove() callback is called recursively | ||
225 | * by this function for any device already registered | ||
226 | */ | ||
227 | void | ||
228 | ocp_unregister_driver(struct ocp_driver *drv) | ||
229 | { | ||
230 | DBG(("ocp: ocp_unregister_driver(%s)...\n", drv->name)); | ||
231 | |||
232 | driver_unregister(&drv->driver); | ||
233 | |||
234 | DBG(("ocp: ocp_unregister_driver(%s)... done.\n", drv->name)); | ||
235 | } | ||
236 | |||
237 | /* Core of ocp_find_device(). Caller must hold ocp_devices_sem */ | ||
238 | static struct ocp_device * | ||
239 | __ocp_find_device(unsigned int vendor, unsigned int function, int index) | ||
240 | { | ||
241 | struct list_head *entry; | ||
242 | struct ocp_device *dev, *found = NULL; | ||
243 | |||
244 | DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index)); | ||
245 | |||
246 | list_for_each(entry, &ocp_devices) { | ||
247 | dev = list_entry(entry, struct ocp_device, link); | ||
248 | if (vendor != OCP_ANY_ID && vendor != dev->def->vendor) | ||
249 | continue; | ||
250 | if (function != OCP_ANY_ID && function != dev->def->function) | ||
251 | continue; | ||
252 | if (index != OCP_ANY_INDEX && index != dev->def->index) | ||
253 | continue; | ||
254 | found = dev; | ||
255 | break; | ||
256 | } | ||
257 | |||
258 | DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)... done\n", vendor, function, index)); | ||
259 | |||
260 | return found; | ||
261 | } | ||
262 | |||
263 | /** | ||
264 | * ocp_find_device - Find a device by function & index | ||
265 | * @vendor: vendor ID of the device (or OCP_ANY_ID) | ||
266 | * @function: function code of the device (or OCP_ANY_ID) | ||
267 | * @idx: index of the device (or OCP_ANY_INDEX) | ||
268 | * | ||
269 | * This function allows a lookup of a given function by it's | ||
270 | * index, it's typically used to find the MAL or ZMII associated | ||
271 | * with an EMAC or similar horrors. | ||
272 | * You can pass vendor, though you usually want OCP_ANY_ID there... | ||
273 | */ | ||
274 | struct ocp_device * | ||
275 | ocp_find_device(unsigned int vendor, unsigned int function, int index) | ||
276 | { | ||
277 | struct ocp_device *dev; | ||
278 | |||
279 | down_read(&ocp_devices_sem); | ||
280 | dev = __ocp_find_device(vendor, function, index); | ||
281 | up_read(&ocp_devices_sem); | ||
282 | |||
283 | return dev; | ||
284 | } | ||
285 | |||
286 | /** | ||
287 | * ocp_get_one_device - Find a def by function & index | ||
288 | * @vendor: vendor ID of the device (or OCP_ANY_ID) | ||
289 | * @function: function code of the device (or OCP_ANY_ID) | ||
290 | * @idx: index of the device (or OCP_ANY_INDEX) | ||
291 | * | ||
292 | * This function allows a lookup of a given ocp_def by it's | ||
293 | * vendor, function, and index. The main purpose for is to | ||
294 | * allow modification of the def before binding to the driver | ||
295 | */ | ||
296 | struct ocp_def * | ||
297 | ocp_get_one_device(unsigned int vendor, unsigned int function, int index) | ||
298 | { | ||
299 | struct ocp_device *dev; | ||
300 | struct ocp_def *found = NULL; | ||
301 | |||
302 | DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)...\n", | ||
303 | vendor, function, index)); | ||
304 | |||
305 | dev = ocp_find_device(vendor, function, index); | ||
306 | |||
307 | if (dev) | ||
308 | found = dev->def; | ||
309 | |||
310 | DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)... done.\n", | ||
311 | vendor, function, index)); | ||
312 | |||
313 | return found; | ||
314 | } | ||
315 | |||
316 | /** | ||
317 | * ocp_add_one_device - Add a device | ||
318 | * @def: static device definition structure | ||
319 | * | ||
320 | * This function adds a device definition to the | ||
321 | * device list. It may only be called before | ||
322 | * ocp_driver_init() and will return an error | ||
323 | * otherwise. | ||
324 | */ | ||
325 | int | ||
326 | ocp_add_one_device(struct ocp_def *def) | ||
327 | { | ||
328 | struct ocp_device *dev; | ||
329 | |||
330 | DBG(("ocp: ocp_add_one_device()...\n")); | ||
331 | |||
332 | /* Can't be called after ocp driver init */ | ||
333 | if (ocp_inited) | ||
334 | return 1; | ||
335 | |||
336 | if (mem_init_done) | ||
337 | dev = kmalloc(sizeof(*dev), GFP_KERNEL); | ||
338 | else | ||
339 | dev = alloc_bootmem(sizeof(*dev)); | ||
340 | |||
341 | if (dev == NULL) | ||
342 | return 1; | ||
343 | memset(dev, 0, sizeof(*dev)); | ||
344 | dev->def = def; | ||
345 | dev->current_state = 4; | ||
346 | sprintf(dev->name, "OCP device %04x:%04x:%04x", | ||
347 | dev->def->vendor, dev->def->function, dev->def->index); | ||
348 | down_write(&ocp_devices_sem); | ||
349 | list_add_tail(&dev->link, &ocp_devices); | ||
350 | up_write(&ocp_devices_sem); | ||
351 | |||
352 | DBG(("ocp: ocp_add_one_device()...done\n")); | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | /** | ||
358 | * ocp_remove_one_device - Remove a device by function & index | ||
359 | * @vendor: vendor ID of the device (or OCP_ANY_ID) | ||
360 | * @function: function code of the device (or OCP_ANY_ID) | ||
361 | * @idx: index of the device (or OCP_ANY_INDEX) | ||
362 | * | ||
363 | * This function allows removal of a given function by its | ||
364 | * index. It may only be called before ocp_driver_init() | ||
365 | * and will return an error otherwise. | ||
366 | */ | ||
367 | int | ||
368 | ocp_remove_one_device(unsigned int vendor, unsigned int function, int index) | ||
369 | { | ||
370 | struct ocp_device *dev; | ||
371 | |||
372 | DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index)); | ||
373 | |||
374 | /* Can't be called after ocp driver init */ | ||
375 | if (ocp_inited) | ||
376 | return 1; | ||
377 | |||
378 | down_write(&ocp_devices_sem); | ||
379 | dev = __ocp_find_device(vendor, function, index); | ||
380 | list_del((struct list_head *)dev); | ||
381 | up_write(&ocp_devices_sem); | ||
382 | |||
383 | DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)... done.\n", vendor, function, index)); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | /** | ||
389 | * ocp_for_each_device - Iterate over OCP devices | ||
390 | * @callback: routine to execute for each ocp device. | ||
391 | * @arg: user data to be passed to callback routine. | ||
392 | * | ||
393 | * This routine holds the ocp_device semaphore, so the | ||
394 | * callback routine cannot modify the ocp_device list. | ||
395 | */ | ||
396 | void | ||
397 | ocp_for_each_device(void(*callback)(struct ocp_device *, void *arg), void *arg) | ||
398 | { | ||
399 | struct list_head *entry; | ||
400 | |||
401 | if (callback) { | ||
402 | down_read(&ocp_devices_sem); | ||
403 | list_for_each(entry, &ocp_devices) | ||
404 | callback(list_entry(entry, struct ocp_device, link), | ||
405 | arg); | ||
406 | up_read(&ocp_devices_sem); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | /** | ||
411 | * ocp_early_init - Init OCP device management | ||
412 | * | ||
413 | * This function builds the list of devices before setup_arch. | ||
414 | * This allows platform code to modify the device lists before | ||
415 | * they are bound to drivers (changes to paddr, removing devices | ||
416 | * etc) | ||
417 | */ | ||
418 | int __init | ||
419 | ocp_early_init(void) | ||
420 | { | ||
421 | struct ocp_def *def; | ||
422 | |||
423 | DBG(("ocp: ocp_early_init()...\n")); | ||
424 | |||
425 | /* Fill the devices list */ | ||
426 | for (def = core_ocp; def->vendor != OCP_VENDOR_INVALID; def++) | ||
427 | ocp_add_one_device(def); | ||
428 | |||
429 | DBG(("ocp: ocp_early_init()... done.\n")); | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | /** | ||
435 | * ocp_driver_init - Init OCP device management | ||
436 | * | ||
437 | * This function is meant to be called via OCP bus registration. | ||
438 | */ | ||
439 | static int __init | ||
440 | ocp_driver_init(void) | ||
441 | { | ||
442 | int ret = 0, index = 0; | ||
443 | struct device *ocp_bus; | ||
444 | struct list_head *entry; | ||
445 | struct ocp_device *dev; | ||
446 | |||
447 | if (ocp_inited) | ||
448 | return ret; | ||
449 | ocp_inited = 1; | ||
450 | |||
451 | DBG(("ocp: ocp_driver_init()...\n")); | ||
452 | |||
453 | /* Allocate/register primary OCP bus */ | ||
454 | ocp_bus = kmalloc(sizeof(struct device), GFP_KERNEL); | ||
455 | if (ocp_bus == NULL) | ||
456 | return 1; | ||
457 | memset(ocp_bus, 0, sizeof(struct device)); | ||
458 | strcpy(ocp_bus->bus_id, "ocp"); | ||
459 | |||
460 | bus_register(&ocp_bus_type); | ||
461 | |||
462 | device_register(ocp_bus); | ||
463 | |||
464 | /* Put each OCP device into global device list */ | ||
465 | list_for_each(entry, &ocp_devices) { | ||
466 | dev = list_entry(entry, struct ocp_device, link); | ||
467 | sprintf(dev->dev.bus_id, "%2.2x", index); | ||
468 | dev->dev.parent = ocp_bus; | ||
469 | dev->dev.bus = &ocp_bus_type; | ||
470 | device_register(&dev->dev); | ||
471 | ocp_create_sysfs_dev_files(dev); | ||
472 | index++; | ||
473 | } | ||
474 | |||
475 | DBG(("ocp: ocp_driver_init()... done.\n")); | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | postcore_initcall(ocp_driver_init); | ||
481 | |||
482 | EXPORT_SYMBOL(ocp_bus_type); | ||
483 | EXPORT_SYMBOL(ocp_find_device); | ||
484 | EXPORT_SYMBOL(ocp_register_driver); | ||
485 | EXPORT_SYMBOL(ocp_unregister_driver); | ||
diff --git a/arch/ppc/syslib/of_device.c b/arch/ppc/syslib/of_device.c new file mode 100644 index 000000000000..46269ed21aee --- /dev/null +++ b/arch/ppc/syslib/of_device.c | |||
@@ -0,0 +1,273 @@ | |||
1 | #include <linux/config.h> | ||
2 | #include <linux/string.h> | ||
3 | #include <linux/kernel.h> | ||
4 | #include <linux/init.h> | ||
5 | #include <linux/module.h> | ||
6 | #include <asm/errno.h> | ||
7 | #include <asm/of_device.h> | ||
8 | |||
9 | /** | ||
10 | * of_match_device - Tell if an of_device structure has a matching | ||
11 | * of_match structure | ||
12 | * @ids: array of of device match structures to search in | ||
13 | * @dev: the of device structure to match against | ||
14 | * | ||
15 | * Used by a driver to check whether an of_device present in the | ||
16 | * system is in its list of supported devices. | ||
17 | */ | ||
18 | const struct of_match * of_match_device(const struct of_match *matches, | ||
19 | const struct of_device *dev) | ||
20 | { | ||
21 | if (!dev->node) | ||
22 | return NULL; | ||
23 | while (matches->name || matches->type || matches->compatible) { | ||
24 | int match = 1; | ||
25 | if (matches->name && matches->name != OF_ANY_MATCH) | ||
26 | match &= dev->node->name | ||
27 | && !strcmp(matches->name, dev->node->name); | ||
28 | if (matches->type && matches->type != OF_ANY_MATCH) | ||
29 | match &= dev->node->type | ||
30 | && !strcmp(matches->type, dev->node->type); | ||
31 | if (matches->compatible && matches->compatible != OF_ANY_MATCH) | ||
32 | match &= device_is_compatible(dev->node, | ||
33 | matches->compatible); | ||
34 | if (match) | ||
35 | return matches; | ||
36 | matches++; | ||
37 | } | ||
38 | return NULL; | ||
39 | } | ||
40 | |||
41 | static int of_platform_bus_match(struct device *dev, struct device_driver *drv) | ||
42 | { | ||
43 | struct of_device * of_dev = to_of_device(dev); | ||
44 | struct of_platform_driver * of_drv = to_of_platform_driver(drv); | ||
45 | const struct of_match * matches = of_drv->match_table; | ||
46 | |||
47 | if (!matches) | ||
48 | return 0; | ||
49 | |||
50 | return of_match_device(matches, of_dev) != NULL; | ||
51 | } | ||
52 | |||
53 | struct of_device *of_dev_get(struct of_device *dev) | ||
54 | { | ||
55 | struct device *tmp; | ||
56 | |||
57 | if (!dev) | ||
58 | return NULL; | ||
59 | tmp = get_device(&dev->dev); | ||
60 | if (tmp) | ||
61 | return to_of_device(tmp); | ||
62 | else | ||
63 | return NULL; | ||
64 | } | ||
65 | |||
66 | void of_dev_put(struct of_device *dev) | ||
67 | { | ||
68 | if (dev) | ||
69 | put_device(&dev->dev); | ||
70 | } | ||
71 | |||
72 | |||
73 | static int of_device_probe(struct device *dev) | ||
74 | { | ||
75 | int error = -ENODEV; | ||
76 | struct of_platform_driver *drv; | ||
77 | struct of_device *of_dev; | ||
78 | const struct of_match *match; | ||
79 | |||
80 | drv = to_of_platform_driver(dev->driver); | ||
81 | of_dev = to_of_device(dev); | ||
82 | |||
83 | if (!drv->probe) | ||
84 | return error; | ||
85 | |||
86 | of_dev_get(of_dev); | ||
87 | |||
88 | match = of_match_device(drv->match_table, of_dev); | ||
89 | if (match) | ||
90 | error = drv->probe(of_dev, match); | ||
91 | if (error) | ||
92 | of_dev_put(of_dev); | ||
93 | |||
94 | return error; | ||
95 | } | ||
96 | |||
97 | static int of_device_remove(struct device *dev) | ||
98 | { | ||
99 | struct of_device * of_dev = to_of_device(dev); | ||
100 | struct of_platform_driver * drv = to_of_platform_driver(dev->driver); | ||
101 | |||
102 | if (dev->driver && drv->remove) | ||
103 | drv->remove(of_dev); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | static int of_device_suspend(struct device *dev, u32 state) | ||
108 | { | ||
109 | struct of_device * of_dev = to_of_device(dev); | ||
110 | struct of_platform_driver * drv = to_of_platform_driver(dev->driver); | ||
111 | int error = 0; | ||
112 | |||
113 | if (dev->driver && drv->suspend) | ||
114 | error = drv->suspend(of_dev, state); | ||
115 | return error; | ||
116 | } | ||
117 | |||
118 | static int of_device_resume(struct device * dev) | ||
119 | { | ||
120 | struct of_device * of_dev = to_of_device(dev); | ||
121 | struct of_platform_driver * drv = to_of_platform_driver(dev->driver); | ||
122 | int error = 0; | ||
123 | |||
124 | if (dev->driver && drv->resume) | ||
125 | error = drv->resume(of_dev); | ||
126 | return error; | ||
127 | } | ||
128 | |||
129 | struct bus_type of_platform_bus_type = { | ||
130 | .name = "of_platform", | ||
131 | .match = of_platform_bus_match, | ||
132 | .suspend = of_device_suspend, | ||
133 | .resume = of_device_resume, | ||
134 | }; | ||
135 | |||
136 | static int __init of_bus_driver_init(void) | ||
137 | { | ||
138 | return bus_register(&of_platform_bus_type); | ||
139 | } | ||
140 | |||
141 | postcore_initcall(of_bus_driver_init); | ||
142 | |||
143 | int of_register_driver(struct of_platform_driver *drv) | ||
144 | { | ||
145 | int count = 0; | ||
146 | |||
147 | /* initialize common driver fields */ | ||
148 | drv->driver.name = drv->name; | ||
149 | drv->driver.bus = &of_platform_bus_type; | ||
150 | drv->driver.probe = of_device_probe; | ||
151 | drv->driver.remove = of_device_remove; | ||
152 | |||
153 | /* register with core */ | ||
154 | count = driver_register(&drv->driver); | ||
155 | return count ? count : 1; | ||
156 | } | ||
157 | |||
158 | void of_unregister_driver(struct of_platform_driver *drv) | ||
159 | { | ||
160 | driver_unregister(&drv->driver); | ||
161 | } | ||
162 | |||
163 | |||
164 | static ssize_t dev_show_devspec(struct device *dev, char *buf) | ||
165 | { | ||
166 | struct of_device *ofdev; | ||
167 | |||
168 | ofdev = to_of_device(dev); | ||
169 | return sprintf(buf, "%s", ofdev->node->full_name); | ||
170 | } | ||
171 | |||
172 | static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL); | ||
173 | |||
174 | /** | ||
175 | * of_release_dev - free an of device structure when all users of it are finished. | ||
176 | * @dev: device that's been disconnected | ||
177 | * | ||
178 | * Will be called only by the device core when all users of this of device are | ||
179 | * done. | ||
180 | */ | ||
181 | void of_release_dev(struct device *dev) | ||
182 | { | ||
183 | struct of_device *ofdev; | ||
184 | |||
185 | ofdev = to_of_device(dev); | ||
186 | of_node_put(ofdev->node); | ||
187 | kfree(ofdev); | ||
188 | } | ||
189 | |||
190 | int of_device_register(struct of_device *ofdev) | ||
191 | { | ||
192 | int rc; | ||
193 | struct of_device **odprop; | ||
194 | |||
195 | BUG_ON(ofdev->node == NULL); | ||
196 | |||
197 | odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL); | ||
198 | if (!odprop) { | ||
199 | struct property *new_prop; | ||
200 | |||
201 | new_prop = kmalloc(sizeof(struct property) + sizeof(struct of_device *), | ||
202 | GFP_KERNEL); | ||
203 | if (new_prop == NULL) | ||
204 | return -ENOMEM; | ||
205 | new_prop->name = "linux,device"; | ||
206 | new_prop->length = sizeof(sizeof(struct of_device *)); | ||
207 | new_prop->value = (unsigned char *)&new_prop[1]; | ||
208 | odprop = (struct of_device **)new_prop->value; | ||
209 | *odprop = NULL; | ||
210 | prom_add_property(ofdev->node, new_prop); | ||
211 | } | ||
212 | *odprop = ofdev; | ||
213 | |||
214 | rc = device_register(&ofdev->dev); | ||
215 | if (rc) | ||
216 | return rc; | ||
217 | |||
218 | device_create_file(&ofdev->dev, &dev_attr_devspec); | ||
219 | |||
220 | return 0; | ||
221 | } | ||
222 | |||
223 | void of_device_unregister(struct of_device *ofdev) | ||
224 | { | ||
225 | struct of_device **odprop; | ||
226 | |||
227 | device_remove_file(&ofdev->dev, &dev_attr_devspec); | ||
228 | |||
229 | odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL); | ||
230 | if (odprop) | ||
231 | *odprop = NULL; | ||
232 | |||
233 | device_unregister(&ofdev->dev); | ||
234 | } | ||
235 | |||
236 | struct of_device* of_platform_device_create(struct device_node *np, const char *bus_id) | ||
237 | { | ||
238 | struct of_device *dev; | ||
239 | u32 *reg; | ||
240 | |||
241 | dev = kmalloc(sizeof(*dev), GFP_KERNEL); | ||
242 | if (!dev) | ||
243 | return NULL; | ||
244 | memset(dev, 0, sizeof(*dev)); | ||
245 | |||
246 | dev->node = of_node_get(np); | ||
247 | dev->dma_mask = 0xffffffffUL; | ||
248 | dev->dev.dma_mask = &dev->dma_mask; | ||
249 | dev->dev.parent = NULL; | ||
250 | dev->dev.bus = &of_platform_bus_type; | ||
251 | dev->dev.release = of_release_dev; | ||
252 | |||
253 | reg = (u32 *)get_property(np, "reg", NULL); | ||
254 | strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE); | ||
255 | |||
256 | if (of_device_register(dev) != 0) { | ||
257 | kfree(dev); | ||
258 | return NULL; | ||
259 | } | ||
260 | |||
261 | return dev; | ||
262 | } | ||
263 | |||
264 | EXPORT_SYMBOL(of_match_device); | ||
265 | EXPORT_SYMBOL(of_platform_bus_type); | ||
266 | EXPORT_SYMBOL(of_register_driver); | ||
267 | EXPORT_SYMBOL(of_unregister_driver); | ||
268 | EXPORT_SYMBOL(of_device_register); | ||
269 | EXPORT_SYMBOL(of_device_unregister); | ||
270 | EXPORT_SYMBOL(of_dev_get); | ||
271 | EXPORT_SYMBOL(of_dev_put); | ||
272 | EXPORT_SYMBOL(of_platform_device_create); | ||
273 | EXPORT_SYMBOL(of_release_dev); | ||
diff --git a/arch/ppc/syslib/open_pic.c b/arch/ppc/syslib/open_pic.c new file mode 100644 index 000000000000..406f36a8a681 --- /dev/null +++ b/arch/ppc/syslib/open_pic.c | |||
@@ -0,0 +1,1083 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling | ||
3 | * | ||
4 | * Copyright (C) 1997 Geert Uytterhoeven | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | |||
11 | #include <linux/config.h> | ||
12 | #include <linux/types.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/sysdev.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <asm/ptrace.h> | ||
21 | #include <asm/signal.h> | ||
22 | #include <asm/io.h> | ||
23 | #include <asm/irq.h> | ||
24 | #include <asm/prom.h> | ||
25 | #include <asm/sections.h> | ||
26 | #include <asm/open_pic.h> | ||
27 | #include <asm/i8259.h> | ||
28 | |||
29 | #include "open_pic_defs.h" | ||
30 | |||
31 | #if defined(CONFIG_PRPMC800) || defined(CONFIG_85xx) | ||
32 | #define OPENPIC_BIG_ENDIAN | ||
33 | #endif | ||
34 | |||
35 | void __iomem *OpenPIC_Addr; | ||
36 | static volatile struct OpenPIC __iomem *OpenPIC = NULL; | ||
37 | |||
38 | /* | ||
39 | * We define OpenPIC_InitSenses table thusly: | ||
40 | * bit 0x1: sense, 0 for edge and 1 for level. | ||
41 | * bit 0x2: polarity, 0 for negative, 1 for positive. | ||
42 | */ | ||
43 | u_int OpenPIC_NumInitSenses __initdata = 0; | ||
44 | u_char *OpenPIC_InitSenses __initdata = NULL; | ||
45 | extern int use_of_interrupt_tree; | ||
46 | |||
47 | static u_int NumProcessors; | ||
48 | static u_int NumSources; | ||
49 | static int open_pic_irq_offset; | ||
50 | static volatile OpenPIC_Source __iomem *ISR[NR_IRQS]; | ||
51 | static int openpic_cascade_irq = -1; | ||
52 | static int (*openpic_cascade_fn)(struct pt_regs *); | ||
53 | |||
54 | /* Global Operations */ | ||
55 | static void openpic_disable_8259_pass_through(void); | ||
56 | static void openpic_set_spurious(u_int vector); | ||
57 | |||
58 | #ifdef CONFIG_SMP | ||
59 | /* Interprocessor Interrupts */ | ||
60 | static void openpic_initipi(u_int ipi, u_int pri, u_int vector); | ||
61 | static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *); | ||
62 | #endif | ||
63 | |||
64 | /* Timer Interrupts */ | ||
65 | static void openpic_inittimer(u_int timer, u_int pri, u_int vector); | ||
66 | static void openpic_maptimer(u_int timer, cpumask_t cpumask); | ||
67 | |||
68 | /* Interrupt Sources */ | ||
69 | static void openpic_enable_irq(u_int irq); | ||
70 | static void openpic_disable_irq(u_int irq); | ||
71 | static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity, | ||
72 | int is_level); | ||
73 | static void openpic_mapirq(u_int irq, cpumask_t cpumask, cpumask_t keepmask); | ||
74 | |||
75 | /* | ||
76 | * These functions are not used but the code is kept here | ||
77 | * for completeness and future reference. | ||
78 | */ | ||
79 | #ifdef notused | ||
80 | static void openpic_enable_8259_pass_through(void); | ||
81 | static u_int openpic_get_priority(void); | ||
82 | static u_int openpic_get_spurious(void); | ||
83 | static void openpic_set_sense(u_int irq, int sense); | ||
84 | #endif /* notused */ | ||
85 | |||
86 | /* | ||
87 | * Description of the openpic for the higher-level irq code | ||
88 | */ | ||
89 | static void openpic_end_irq(unsigned int irq_nr); | ||
90 | static void openpic_ack_irq(unsigned int irq_nr); | ||
91 | static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask); | ||
92 | |||
93 | struct hw_interrupt_type open_pic = { | ||
94 | .typename = " OpenPIC ", | ||
95 | .enable = openpic_enable_irq, | ||
96 | .disable = openpic_disable_irq, | ||
97 | .ack = openpic_ack_irq, | ||
98 | .end = openpic_end_irq, | ||
99 | .set_affinity = openpic_set_affinity, | ||
100 | }; | ||
101 | |||
102 | #ifdef CONFIG_SMP | ||
103 | static void openpic_end_ipi(unsigned int irq_nr); | ||
104 | static void openpic_ack_ipi(unsigned int irq_nr); | ||
105 | static void openpic_enable_ipi(unsigned int irq_nr); | ||
106 | static void openpic_disable_ipi(unsigned int irq_nr); | ||
107 | |||
108 | struct hw_interrupt_type open_pic_ipi = { | ||
109 | .typename = " OpenPIC ", | ||
110 | .enable = openpic_enable_ipi, | ||
111 | .disable = openpic_disable_ipi, | ||
112 | .ack = openpic_ack_ipi, | ||
113 | .end = openpic_end_ipi, | ||
114 | }; | ||
115 | #endif /* CONFIG_SMP */ | ||
116 | |||
117 | /* | ||
118 | * Accesses to the current processor's openpic registers | ||
119 | */ | ||
120 | #ifdef CONFIG_SMP | ||
121 | #define THIS_CPU Processor[cpu] | ||
122 | #define DECL_THIS_CPU int cpu = smp_hw_index[smp_processor_id()] | ||
123 | #define CHECK_THIS_CPU check_arg_cpu(cpu) | ||
124 | #else | ||
125 | #define THIS_CPU Processor[0] | ||
126 | #define DECL_THIS_CPU | ||
127 | #define CHECK_THIS_CPU | ||
128 | #endif /* CONFIG_SMP */ | ||
129 | |||
130 | #if 1 | ||
131 | #define check_arg_ipi(ipi) \ | ||
132 | if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ | ||
133 | printk("open_pic.c:%d: invalid ipi %d\n", __LINE__, ipi); | ||
134 | #define check_arg_timer(timer) \ | ||
135 | if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ | ||
136 | printk("open_pic.c:%d: invalid timer %d\n", __LINE__, timer); | ||
137 | #define check_arg_vec(vec) \ | ||
138 | if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ | ||
139 | printk("open_pic.c:%d: invalid vector %d\n", __LINE__, vec); | ||
140 | #define check_arg_pri(pri) \ | ||
141 | if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ | ||
142 | printk("open_pic.c:%d: invalid priority %d\n", __LINE__, pri); | ||
143 | /* | ||
144 | * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's | ||
145 | * data has probably been corrupted and we're going to panic or deadlock later | ||
146 | * anyway --Troy | ||
147 | */ | ||
148 | #define check_arg_irq(irq) \ | ||
149 | if (irq < open_pic_irq_offset || irq >= NumSources+open_pic_irq_offset \ | ||
150 | || ISR[irq - open_pic_irq_offset] == 0) { \ | ||
151 | printk("open_pic.c:%d: invalid irq %d\n", __LINE__, irq); \ | ||
152 | dump_stack(); } | ||
153 | #define check_arg_cpu(cpu) \ | ||
154 | if (cpu < 0 || cpu >= NumProcessors){ \ | ||
155 | printk("open_pic.c:%d: invalid cpu %d\n", __LINE__, cpu); \ | ||
156 | dump_stack(); } | ||
157 | #else | ||
158 | #define check_arg_ipi(ipi) do {} while (0) | ||
159 | #define check_arg_timer(timer) do {} while (0) | ||
160 | #define check_arg_vec(vec) do {} while (0) | ||
161 | #define check_arg_pri(pri) do {} while (0) | ||
162 | #define check_arg_irq(irq) do {} while (0) | ||
163 | #define check_arg_cpu(cpu) do {} while (0) | ||
164 | #endif | ||
165 | |||
166 | u_int openpic_read(volatile u_int __iomem *addr) | ||
167 | { | ||
168 | u_int val; | ||
169 | |||
170 | #ifdef OPENPIC_BIG_ENDIAN | ||
171 | val = in_be32(addr); | ||
172 | #else | ||
173 | val = in_le32(addr); | ||
174 | #endif | ||
175 | return val; | ||
176 | } | ||
177 | |||
178 | static inline void openpic_write(volatile u_int __iomem *addr, u_int val) | ||
179 | { | ||
180 | #ifdef OPENPIC_BIG_ENDIAN | ||
181 | out_be32(addr, val); | ||
182 | #else | ||
183 | out_le32(addr, val); | ||
184 | #endif | ||
185 | } | ||
186 | |||
187 | static inline u_int openpic_readfield(volatile u_int __iomem *addr, u_int mask) | ||
188 | { | ||
189 | u_int val = openpic_read(addr); | ||
190 | return val & mask; | ||
191 | } | ||
192 | |||
193 | inline void openpic_writefield(volatile u_int __iomem *addr, u_int mask, | ||
194 | u_int field) | ||
195 | { | ||
196 | u_int val = openpic_read(addr); | ||
197 | openpic_write(addr, (val & ~mask) | (field & mask)); | ||
198 | } | ||
199 | |||
200 | static inline void openpic_clearfield(volatile u_int __iomem *addr, u_int mask) | ||
201 | { | ||
202 | openpic_writefield(addr, mask, 0); | ||
203 | } | ||
204 | |||
205 | static inline void openpic_setfield(volatile u_int __iomem *addr, u_int mask) | ||
206 | { | ||
207 | openpic_writefield(addr, mask, mask); | ||
208 | } | ||
209 | |||
210 | static void openpic_safe_writefield(volatile u_int __iomem *addr, u_int mask, | ||
211 | u_int field) | ||
212 | { | ||
213 | openpic_setfield(addr, OPENPIC_MASK); | ||
214 | while (openpic_read(addr) & OPENPIC_ACTIVITY); | ||
215 | openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); | ||
216 | } | ||
217 | |||
218 | #ifdef CONFIG_SMP | ||
219 | /* yes this is right ... bug, feature, you decide! -- tgall */ | ||
220 | u_int openpic_read_IPI(volatile u_int __iomem * addr) | ||
221 | { | ||
222 | u_int val = 0; | ||
223 | #if defined(OPENPIC_BIG_ENDIAN) || defined(CONFIG_POWER3) | ||
224 | val = in_be32(addr); | ||
225 | #else | ||
226 | val = in_le32(addr); | ||
227 | #endif | ||
228 | return val; | ||
229 | } | ||
230 | |||
231 | /* because of the power3 be / le above, this is needed */ | ||
232 | inline void openpic_writefield_IPI(volatile u_int __iomem * addr, u_int mask, u_int field) | ||
233 | { | ||
234 | u_int val = openpic_read_IPI(addr); | ||
235 | openpic_write(addr, (val & ~mask) | (field & mask)); | ||
236 | } | ||
237 | |||
238 | static inline void openpic_clearfield_IPI(volatile u_int __iomem *addr, u_int mask) | ||
239 | { | ||
240 | openpic_writefield_IPI(addr, mask, 0); | ||
241 | } | ||
242 | |||
243 | static inline void openpic_setfield_IPI(volatile u_int __iomem *addr, u_int mask) | ||
244 | { | ||
245 | openpic_writefield_IPI(addr, mask, mask); | ||
246 | } | ||
247 | |||
248 | static void openpic_safe_writefield_IPI(volatile u_int __iomem *addr, u_int mask, u_int field) | ||
249 | { | ||
250 | openpic_setfield_IPI(addr, OPENPIC_MASK); | ||
251 | |||
252 | /* wait until it's not in use */ | ||
253 | /* BenH: Is this code really enough ? I would rather check the result | ||
254 | * and eventually retry ... | ||
255 | */ | ||
256 | while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY); | ||
257 | |||
258 | openpic_writefield_IPI(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); | ||
259 | } | ||
260 | #endif /* CONFIG_SMP */ | ||
261 | |||
262 | #ifdef CONFIG_EPIC_SERIAL_MODE | ||
263 | /* On platforms that may use EPIC serial mode, the default is enabled. */ | ||
264 | int epic_serial_mode = 1; | ||
265 | |||
266 | static void __init openpic_eicr_set_clk(u_int clkval) | ||
267 | { | ||
268 | openpic_writefield(&OpenPIC->Global.Global_Configuration1, | ||
269 | OPENPIC_EICR_S_CLK_MASK, (clkval << 28)); | ||
270 | } | ||
271 | |||
272 | static void __init openpic_enable_sie(void) | ||
273 | { | ||
274 | openpic_setfield(&OpenPIC->Global.Global_Configuration1, | ||
275 | OPENPIC_EICR_SIE); | ||
276 | } | ||
277 | #endif | ||
278 | |||
279 | #if defined(CONFIG_EPIC_SERIAL_MODE) || defined(CONFIG_PM) | ||
280 | static void openpic_reset(void) | ||
281 | { | ||
282 | openpic_setfield(&OpenPIC->Global.Global_Configuration0, | ||
283 | OPENPIC_CONFIG_RESET); | ||
284 | while (openpic_readfield(&OpenPIC->Global.Global_Configuration0, | ||
285 | OPENPIC_CONFIG_RESET)) | ||
286 | mb(); | ||
287 | } | ||
288 | #endif | ||
289 | |||
290 | void __init openpic_set_sources(int first_irq, int num_irqs, void __iomem *first_ISR) | ||
291 | { | ||
292 | volatile OpenPIC_Source __iomem *src = first_ISR; | ||
293 | int i, last_irq; | ||
294 | |||
295 | last_irq = first_irq + num_irqs; | ||
296 | if (last_irq > NumSources) | ||
297 | NumSources = last_irq; | ||
298 | if (src == 0) | ||
299 | src = &((struct OpenPIC __iomem *)OpenPIC_Addr)->Source[first_irq]; | ||
300 | for (i = first_irq; i < last_irq; ++i, ++src) | ||
301 | ISR[i] = src; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * The `offset' parameter defines where the interrupts handled by the | ||
306 | * OpenPIC start in the space of interrupt numbers that the kernel knows | ||
307 | * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the | ||
308 | * kernel's interrupt numbering scheme. | ||
309 | * We assume there is only one OpenPIC. | ||
310 | */ | ||
311 | void __init openpic_init(int offset) | ||
312 | { | ||
313 | u_int t, i; | ||
314 | u_int timerfreq; | ||
315 | const char *version; | ||
316 | |||
317 | if (!OpenPIC_Addr) { | ||
318 | printk("No OpenPIC found !\n"); | ||
319 | return; | ||
320 | } | ||
321 | OpenPIC = (volatile struct OpenPIC __iomem *)OpenPIC_Addr; | ||
322 | |||
323 | #ifdef CONFIG_EPIC_SERIAL_MODE | ||
324 | /* Have to start from ground zero. | ||
325 | */ | ||
326 | openpic_reset(); | ||
327 | #endif | ||
328 | |||
329 | if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122); | ||
330 | |||
331 | t = openpic_read(&OpenPIC->Global.Feature_Reporting0); | ||
332 | switch (t & OPENPIC_FEATURE_VERSION_MASK) { | ||
333 | case 1: | ||
334 | version = "1.0"; | ||
335 | break; | ||
336 | case 2: | ||
337 | version = "1.2"; | ||
338 | break; | ||
339 | case 3: | ||
340 | version = "1.3"; | ||
341 | break; | ||
342 | default: | ||
343 | version = "?"; | ||
344 | break; | ||
345 | } | ||
346 | NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> | ||
347 | OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; | ||
348 | if (NumSources == 0) | ||
349 | openpic_set_sources(0, | ||
350 | ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> | ||
351 | OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1, | ||
352 | NULL); | ||
353 | printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n", | ||
354 | version, NumProcessors, NumSources, OpenPIC); | ||
355 | timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency); | ||
356 | if (timerfreq) | ||
357 | printk("OpenPIC timer frequency is %d.%06d MHz\n", | ||
358 | timerfreq / 1000000, timerfreq % 1000000); | ||
359 | |||
360 | open_pic_irq_offset = offset; | ||
361 | |||
362 | /* Initialize timer interrupts */ | ||
363 | if ( ppc_md.progress ) ppc_md.progress("openpic: timer",0x3ba); | ||
364 | for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { | ||
365 | /* Disabled, Priority 0 */ | ||
366 | openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i+offset); | ||
367 | /* No processor */ | ||
368 | openpic_maptimer(i, CPU_MASK_NONE); | ||
369 | } | ||
370 | |||
371 | #ifdef CONFIG_SMP | ||
372 | /* Initialize IPI interrupts */ | ||
373 | if ( ppc_md.progress ) ppc_md.progress("openpic: ipi",0x3bb); | ||
374 | for (i = 0; i < OPENPIC_NUM_IPI; i++) { | ||
375 | /* Disabled, Priority 10..13 */ | ||
376 | openpic_initipi(i, 10+i, OPENPIC_VEC_IPI+i+offset); | ||
377 | /* IPIs are per-CPU */ | ||
378 | irq_desc[OPENPIC_VEC_IPI+i+offset].status |= IRQ_PER_CPU; | ||
379 | irq_desc[OPENPIC_VEC_IPI+i+offset].handler = &open_pic_ipi; | ||
380 | } | ||
381 | #endif | ||
382 | |||
383 | /* Initialize external interrupts */ | ||
384 | if (ppc_md.progress) ppc_md.progress("openpic: external",0x3bc); | ||
385 | |||
386 | openpic_set_priority(0xf); | ||
387 | |||
388 | /* Init all external sources, including possibly the cascade. */ | ||
389 | for (i = 0; i < NumSources; i++) { | ||
390 | int sense; | ||
391 | |||
392 | if (ISR[i] == 0) | ||
393 | continue; | ||
394 | |||
395 | /* the bootloader may have left it enabled (bad !) */ | ||
396 | openpic_disable_irq(i+offset); | ||
397 | |||
398 | sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \ | ||
399 | (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE); | ||
400 | |||
401 | if (sense & IRQ_SENSE_MASK) | ||
402 | irq_desc[i+offset].status = IRQ_LEVEL; | ||
403 | |||
404 | /* Enabled, Priority 8 */ | ||
405 | openpic_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK), | ||
406 | (sense & IRQ_SENSE_MASK)); | ||
407 | /* Processor 0 */ | ||
408 | openpic_mapirq(i, CPU_MASK_CPU0, CPU_MASK_NONE); | ||
409 | } | ||
410 | |||
411 | /* Init descriptors */ | ||
412 | for (i = offset; i < NumSources + offset; i++) | ||
413 | irq_desc[i].handler = &open_pic; | ||
414 | |||
415 | /* Initialize the spurious interrupt */ | ||
416 | if (ppc_md.progress) ppc_md.progress("openpic: spurious",0x3bd); | ||
417 | openpic_set_spurious(OPENPIC_VEC_SPURIOUS); | ||
418 | openpic_disable_8259_pass_through(); | ||
419 | #ifdef CONFIG_EPIC_SERIAL_MODE | ||
420 | if (epic_serial_mode) { | ||
421 | openpic_eicr_set_clk(7); /* Slowest value until we know better */ | ||
422 | openpic_enable_sie(); | ||
423 | } | ||
424 | #endif | ||
425 | openpic_set_priority(0); | ||
426 | |||
427 | if (ppc_md.progress) ppc_md.progress("openpic: exit",0x222); | ||
428 | } | ||
429 | |||
430 | #ifdef notused | ||
431 | static void openpic_enable_8259_pass_through(void) | ||
432 | { | ||
433 | openpic_clearfield(&OpenPIC->Global.Global_Configuration0, | ||
434 | OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); | ||
435 | } | ||
436 | #endif /* notused */ | ||
437 | |||
438 | static void openpic_disable_8259_pass_through(void) | ||
439 | { | ||
440 | openpic_setfield(&OpenPIC->Global.Global_Configuration0, | ||
441 | OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); | ||
442 | } | ||
443 | |||
444 | /* | ||
445 | * Find out the current interrupt | ||
446 | */ | ||
447 | u_int openpic_irq(void) | ||
448 | { | ||
449 | u_int vec; | ||
450 | DECL_THIS_CPU; | ||
451 | |||
452 | CHECK_THIS_CPU; | ||
453 | vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge, | ||
454 | OPENPIC_VECTOR_MASK); | ||
455 | return vec; | ||
456 | } | ||
457 | |||
458 | void openpic_eoi(void) | ||
459 | { | ||
460 | DECL_THIS_CPU; | ||
461 | |||
462 | CHECK_THIS_CPU; | ||
463 | openpic_write(&OpenPIC->THIS_CPU.EOI, 0); | ||
464 | /* Handle PCI write posting */ | ||
465 | (void)openpic_read(&OpenPIC->THIS_CPU.EOI); | ||
466 | } | ||
467 | |||
468 | #ifdef notused | ||
469 | static u_int openpic_get_priority(void) | ||
470 | { | ||
471 | DECL_THIS_CPU; | ||
472 | |||
473 | CHECK_THIS_CPU; | ||
474 | return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority, | ||
475 | OPENPIC_CURRENT_TASK_PRIORITY_MASK); | ||
476 | } | ||
477 | #endif /* notused */ | ||
478 | |||
479 | void openpic_set_priority(u_int pri) | ||
480 | { | ||
481 | DECL_THIS_CPU; | ||
482 | |||
483 | CHECK_THIS_CPU; | ||
484 | check_arg_pri(pri); | ||
485 | openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority, | ||
486 | OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); | ||
487 | } | ||
488 | |||
489 | /* | ||
490 | * Get/set the spurious vector | ||
491 | */ | ||
492 | #ifdef notused | ||
493 | static u_int openpic_get_spurious(void) | ||
494 | { | ||
495 | return openpic_readfield(&OpenPIC->Global.Spurious_Vector, | ||
496 | OPENPIC_VECTOR_MASK); | ||
497 | } | ||
498 | #endif /* notused */ | ||
499 | |||
500 | static void openpic_set_spurious(u_int vec) | ||
501 | { | ||
502 | check_arg_vec(vec); | ||
503 | openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, | ||
504 | vec); | ||
505 | } | ||
506 | |||
507 | #ifdef CONFIG_SMP | ||
508 | /* | ||
509 | * Convert a cpu mask from logical to physical cpu numbers. | ||
510 | */ | ||
511 | static inline cpumask_t physmask(cpumask_t cpumask) | ||
512 | { | ||
513 | int i; | ||
514 | cpumask_t mask = CPU_MASK_NONE; | ||
515 | |||
516 | cpus_and(cpumask, cpu_online_map, cpumask); | ||
517 | |||
518 | for (i = 0; i < NR_CPUS; i++) | ||
519 | if (cpu_isset(i, cpumask)) | ||
520 | cpu_set(smp_hw_index[i], mask); | ||
521 | |||
522 | return mask; | ||
523 | } | ||
524 | #else | ||
525 | #define physmask(cpumask) (cpumask) | ||
526 | #endif | ||
527 | |||
528 | void openpic_reset_processor_phys(u_int mask) | ||
529 | { | ||
530 | openpic_write(&OpenPIC->Global.Processor_Initialization, mask); | ||
531 | } | ||
532 | |||
533 | #if defined(CONFIG_SMP) || defined(CONFIG_PM) | ||
534 | static DEFINE_SPINLOCK(openpic_setup_lock); | ||
535 | #endif | ||
536 | |||
537 | #ifdef CONFIG_SMP | ||
538 | /* | ||
539 | * Initialize an interprocessor interrupt (and disable it) | ||
540 | * | ||
541 | * ipi: OpenPIC interprocessor interrupt number | ||
542 | * pri: interrupt source priority | ||
543 | * vec: the vector it will produce | ||
544 | */ | ||
545 | static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec) | ||
546 | { | ||
547 | check_arg_ipi(ipi); | ||
548 | check_arg_pri(pri); | ||
549 | check_arg_vec(vec); | ||
550 | openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi), | ||
551 | OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, | ||
552 | (pri << OPENPIC_PRIORITY_SHIFT) | vec); | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * Send an IPI to one or more CPUs | ||
557 | * | ||
558 | * Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI) | ||
559 | * and not a system-wide interrupt number | ||
560 | */ | ||
561 | void openpic_cause_IPI(u_int ipi, cpumask_t cpumask) | ||
562 | { | ||
563 | cpumask_t phys; | ||
564 | DECL_THIS_CPU; | ||
565 | |||
566 | CHECK_THIS_CPU; | ||
567 | check_arg_ipi(ipi); | ||
568 | phys = physmask(cpumask); | ||
569 | openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi), | ||
570 | cpus_addr(physmask(cpumask))[0]); | ||
571 | } | ||
572 | |||
573 | void openpic_request_IPIs(void) | ||
574 | { | ||
575 | int i; | ||
576 | |||
577 | /* | ||
578 | * Make sure this matches what is defined in smp.c for | ||
579 | * smp_message_{pass|recv}() or what shows up in | ||
580 | * /proc/interrupts will be wrong!!! --Troy */ | ||
581 | |||
582 | if (OpenPIC == NULL) | ||
583 | return; | ||
584 | |||
585 | /* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */ | ||
586 | request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset, | ||
587 | openpic_ipi_action, SA_INTERRUPT, | ||
588 | "IPI0 (call function)", NULL); | ||
589 | request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+1, | ||
590 | openpic_ipi_action, SA_INTERRUPT, | ||
591 | "IPI1 (reschedule)", NULL); | ||
592 | request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+2, | ||
593 | openpic_ipi_action, SA_INTERRUPT, | ||
594 | "IPI2 (invalidate tlb)", NULL); | ||
595 | request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+3, | ||
596 | openpic_ipi_action, SA_INTERRUPT, | ||
597 | "IPI3 (xmon break)", NULL); | ||
598 | |||
599 | for ( i = 0; i < OPENPIC_NUM_IPI ; i++ ) | ||
600 | openpic_enable_ipi(OPENPIC_VEC_IPI+open_pic_irq_offset+i); | ||
601 | } | ||
602 | |||
603 | /* | ||
604 | * Do per-cpu setup for SMP systems. | ||
605 | * | ||
606 | * Get IPI's working and start taking interrupts. | ||
607 | * -- Cort | ||
608 | */ | ||
609 | |||
610 | void __devinit do_openpic_setup_cpu(void) | ||
611 | { | ||
612 | #ifdef CONFIG_IRQ_ALL_CPUS | ||
613 | int i; | ||
614 | cpumask_t msk = CPU_MASK_NONE; | ||
615 | #endif | ||
616 | spin_lock(&openpic_setup_lock); | ||
617 | |||
618 | #ifdef CONFIG_IRQ_ALL_CPUS | ||
619 | cpu_set(smp_hw_index[smp_processor_id()], msk); | ||
620 | |||
621 | /* let the openpic know we want intrs. default affinity | ||
622 | * is 0xffffffff until changed via /proc | ||
623 | * That's how it's done on x86. If we want it differently, then | ||
624 | * we should make sure we also change the default values of irq_affinity | ||
625 | * in irq.c. | ||
626 | */ | ||
627 | for (i = 0; i < NumSources; i++) | ||
628 | openpic_mapirq(i, msk, CPU_MASK_ALL); | ||
629 | #endif /* CONFIG_IRQ_ALL_CPUS */ | ||
630 | openpic_set_priority(0); | ||
631 | |||
632 | spin_unlock(&openpic_setup_lock); | ||
633 | } | ||
634 | #endif /* CONFIG_SMP */ | ||
635 | |||
636 | /* | ||
637 | * Initialize a timer interrupt (and disable it) | ||
638 | * | ||
639 | * timer: OpenPIC timer number | ||
640 | * pri: interrupt source priority | ||
641 | * vec: the vector it will produce | ||
642 | */ | ||
643 | static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec) | ||
644 | { | ||
645 | check_arg_timer(timer); | ||
646 | check_arg_pri(pri); | ||
647 | check_arg_vec(vec); | ||
648 | openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority, | ||
649 | OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, | ||
650 | (pri << OPENPIC_PRIORITY_SHIFT) | vec); | ||
651 | } | ||
652 | |||
653 | /* | ||
654 | * Map a timer interrupt to one or more CPUs | ||
655 | */ | ||
656 | static void __init openpic_maptimer(u_int timer, cpumask_t cpumask) | ||
657 | { | ||
658 | cpumask_t phys = physmask(cpumask); | ||
659 | check_arg_timer(timer); | ||
660 | openpic_write(&OpenPIC->Global.Timer[timer].Destination, | ||
661 | cpus_addr(phys)[0]); | ||
662 | } | ||
663 | |||
664 | /* | ||
665 | * Initalize the interrupt source which will generate an NMI. | ||
666 | * This raises the interrupt's priority from 8 to 9. | ||
667 | * | ||
668 | * irq: The logical IRQ which generates an NMI. | ||
669 | */ | ||
670 | void __init | ||
671 | openpic_init_nmi_irq(u_int irq) | ||
672 | { | ||
673 | check_arg_irq(irq); | ||
674 | openpic_safe_writefield(&ISR[irq - open_pic_irq_offset]->Vector_Priority, | ||
675 | OPENPIC_PRIORITY_MASK, | ||
676 | 9 << OPENPIC_PRIORITY_SHIFT); | ||
677 | } | ||
678 | |||
679 | /* | ||
680 | * | ||
681 | * All functions below take an offset'ed irq argument | ||
682 | * | ||
683 | */ | ||
684 | |||
685 | /* | ||
686 | * Hookup a cascade to the OpenPIC. | ||
687 | */ | ||
688 | |||
689 | static struct irqaction openpic_cascade_irqaction = { | ||
690 | .handler = no_action, | ||
691 | .flags = SA_INTERRUPT, | ||
692 | .mask = CPU_MASK_NONE, | ||
693 | }; | ||
694 | |||
695 | void __init | ||
696 | openpic_hookup_cascade(u_int irq, char *name, | ||
697 | int (*cascade_fn)(struct pt_regs *)) | ||
698 | { | ||
699 | openpic_cascade_irq = irq; | ||
700 | openpic_cascade_fn = cascade_fn; | ||
701 | |||
702 | if (setup_irq(irq, &openpic_cascade_irqaction)) | ||
703 | printk("Unable to get OpenPIC IRQ %d for cascade\n", | ||
704 | irq - open_pic_irq_offset); | ||
705 | } | ||
706 | |||
707 | /* | ||
708 | * Enable/disable an external interrupt source | ||
709 | * | ||
710 | * Externally called, irq is an offseted system-wide interrupt number | ||
711 | */ | ||
712 | static void openpic_enable_irq(u_int irq) | ||
713 | { | ||
714 | volatile u_int __iomem *vpp; | ||
715 | |||
716 | check_arg_irq(irq); | ||
717 | vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority; | ||
718 | openpic_clearfield(vpp, OPENPIC_MASK); | ||
719 | /* make sure mask gets to controller before we return to user */ | ||
720 | do { | ||
721 | mb(); /* sync is probably useless here */ | ||
722 | } while (openpic_readfield(vpp, OPENPIC_MASK)); | ||
723 | } | ||
724 | |||
725 | static void openpic_disable_irq(u_int irq) | ||
726 | { | ||
727 | volatile u_int __iomem *vpp; | ||
728 | u32 vp; | ||
729 | |||
730 | check_arg_irq(irq); | ||
731 | vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority; | ||
732 | openpic_setfield(vpp, OPENPIC_MASK); | ||
733 | /* make sure mask gets to controller before we return to user */ | ||
734 | do { | ||
735 | mb(); /* sync is probably useless here */ | ||
736 | vp = openpic_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY); | ||
737 | } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); | ||
738 | } | ||
739 | |||
740 | #ifdef CONFIG_SMP | ||
741 | /* | ||
742 | * Enable/disable an IPI interrupt source | ||
743 | * | ||
744 | * Externally called, irq is an offseted system-wide interrupt number | ||
745 | */ | ||
746 | void openpic_enable_ipi(u_int irq) | ||
747 | { | ||
748 | irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset); | ||
749 | check_arg_ipi(irq); | ||
750 | openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); | ||
751 | |||
752 | } | ||
753 | |||
754 | void openpic_disable_ipi(u_int irq) | ||
755 | { | ||
756 | irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset); | ||
757 | check_arg_ipi(irq); | ||
758 | openpic_setfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK); | ||
759 | } | ||
760 | #endif | ||
761 | |||
762 | /* | ||
763 | * Initialize an interrupt source (and disable it!) | ||
764 | * | ||
765 | * irq: OpenPIC interrupt number | ||
766 | * pri: interrupt source priority | ||
767 | * vec: the vector it will produce | ||
768 | * pol: polarity (1 for positive, 0 for negative) | ||
769 | * sense: 1 for level, 0 for edge | ||
770 | */ | ||
771 | static void __init | ||
772 | openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) | ||
773 | { | ||
774 | openpic_safe_writefield(&ISR[irq]->Vector_Priority, | ||
775 | OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | | ||
776 | OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, | ||
777 | (pri << OPENPIC_PRIORITY_SHIFT) | vec | | ||
778 | (pol ? OPENPIC_POLARITY_POSITIVE : | ||
779 | OPENPIC_POLARITY_NEGATIVE) | | ||
780 | (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); | ||
781 | } | ||
782 | |||
783 | /* | ||
784 | * Map an interrupt source to one or more CPUs | ||
785 | */ | ||
786 | static void openpic_mapirq(u_int irq, cpumask_t physmask, cpumask_t keepmask) | ||
787 | { | ||
788 | if (ISR[irq] == 0) | ||
789 | return; | ||
790 | if (!cpus_empty(keepmask)) { | ||
791 | cpumask_t irqdest = { .bits[0] = openpic_read(&ISR[irq]->Destination) }; | ||
792 | cpus_and(irqdest, irqdest, keepmask); | ||
793 | cpus_or(physmask, physmask, irqdest); | ||
794 | } | ||
795 | openpic_write(&ISR[irq]->Destination, cpus_addr(physmask)[0]); | ||
796 | } | ||
797 | |||
798 | #ifdef notused | ||
799 | /* | ||
800 | * Set the sense for an interrupt source (and disable it!) | ||
801 | * | ||
802 | * sense: 1 for level, 0 for edge | ||
803 | */ | ||
804 | static void openpic_set_sense(u_int irq, int sense) | ||
805 | { | ||
806 | if (ISR[irq] != 0) | ||
807 | openpic_safe_writefield(&ISR[irq]->Vector_Priority, | ||
808 | OPENPIC_SENSE_LEVEL, | ||
809 | (sense ? OPENPIC_SENSE_LEVEL : 0)); | ||
810 | } | ||
811 | #endif /* notused */ | ||
812 | |||
813 | /* No spinlocks, should not be necessary with the OpenPIC | ||
814 | * (1 register = 1 interrupt and we have the desc lock). | ||
815 | */ | ||
816 | static void openpic_ack_irq(unsigned int irq_nr) | ||
817 | { | ||
818 | #ifdef __SLOW_VERSION__ | ||
819 | openpic_disable_irq(irq_nr); | ||
820 | openpic_eoi(); | ||
821 | #else | ||
822 | if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0) | ||
823 | openpic_eoi(); | ||
824 | #endif | ||
825 | } | ||
826 | |||
827 | static void openpic_end_irq(unsigned int irq_nr) | ||
828 | { | ||
829 | #ifdef __SLOW_VERSION__ | ||
830 | if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) | ||
831 | && irq_desc[irq_nr].action) | ||
832 | openpic_enable_irq(irq_nr); | ||
833 | #else | ||
834 | if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0) | ||
835 | openpic_eoi(); | ||
836 | #endif | ||
837 | } | ||
838 | |||
839 | static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask) | ||
840 | { | ||
841 | openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), CPU_MASK_NONE); | ||
842 | } | ||
843 | |||
844 | #ifdef CONFIG_SMP | ||
845 | static void openpic_ack_ipi(unsigned int irq_nr) | ||
846 | { | ||
847 | openpic_eoi(); | ||
848 | } | ||
849 | |||
850 | static void openpic_end_ipi(unsigned int irq_nr) | ||
851 | { | ||
852 | } | ||
853 | |||
854 | static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs) | ||
855 | { | ||
856 | smp_message_recv(cpl-OPENPIC_VEC_IPI-open_pic_irq_offset, regs); | ||
857 | return IRQ_HANDLED; | ||
858 | } | ||
859 | |||
860 | #endif /* CONFIG_SMP */ | ||
861 | |||
862 | int | ||
863 | openpic_get_irq(struct pt_regs *regs) | ||
864 | { | ||
865 | int irq = openpic_irq(); | ||
866 | |||
867 | /* | ||
868 | * Check for the cascade interrupt and call the cascaded | ||
869 | * interrupt controller function (usually i8259_irq) if so. | ||
870 | * This should move to irq.c eventually. -- paulus | ||
871 | */ | ||
872 | if (irq == openpic_cascade_irq && openpic_cascade_fn != NULL) { | ||
873 | int cirq = openpic_cascade_fn(regs); | ||
874 | |||
875 | /* Allow for the cascade being shared with other devices */ | ||
876 | if (cirq != -1) { | ||
877 | irq = cirq; | ||
878 | openpic_eoi(); | ||
879 | } | ||
880 | } else if (irq == OPENPIC_VEC_SPURIOUS) | ||
881 | irq = -1; | ||
882 | return irq; | ||
883 | } | ||
884 | |||
885 | #ifdef CONFIG_SMP | ||
886 | void | ||
887 | smp_openpic_message_pass(int target, int msg, unsigned long data, int wait) | ||
888 | { | ||
889 | cpumask_t mask = CPU_MASK_ALL; | ||
890 | /* make sure we're sending something that translates to an IPI */ | ||
891 | if (msg > 0x3) { | ||
892 | printk("SMP %d: smp_message_pass: unknown msg %d\n", | ||
893 | smp_processor_id(), msg); | ||
894 | return; | ||
895 | } | ||
896 | switch (target) { | ||
897 | case MSG_ALL: | ||
898 | openpic_cause_IPI(msg, mask); | ||
899 | break; | ||
900 | case MSG_ALL_BUT_SELF: | ||
901 | cpu_clear(smp_processor_id(), mask); | ||
902 | openpic_cause_IPI(msg, mask); | ||
903 | break; | ||
904 | default: | ||
905 | openpic_cause_IPI(msg, cpumask_of_cpu(target)); | ||
906 | break; | ||
907 | } | ||
908 | } | ||
909 | #endif /* CONFIG_SMP */ | ||
910 | |||
911 | #ifdef CONFIG_PM | ||
912 | |||
913 | /* | ||
914 | * We implement the IRQ controller as a sysdev and put it | ||
915 | * to sleep at powerdown stage (the callback is named suspend, | ||
916 | * but it's old semantics, for the Device Model, it's really | ||
917 | * powerdown). The possible problem is that another sysdev that | ||
918 | * happens to be suspend after this one will have interrupts off, | ||
919 | * that may be an issue... For now, this isn't an issue on pmac | ||
920 | * though... | ||
921 | */ | ||
922 | |||
923 | static u32 save_ipi_vp[OPENPIC_NUM_IPI]; | ||
924 | static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES]; | ||
925 | static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES]; | ||
926 | static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS]; | ||
927 | static int openpic_suspend_count; | ||
928 | |||
929 | static void openpic_cached_enable_irq(u_int irq) | ||
930 | { | ||
931 | check_arg_irq(irq); | ||
932 | save_irq_src_vp[irq - open_pic_irq_offset] &= ~OPENPIC_MASK; | ||
933 | } | ||
934 | |||
935 | static void openpic_cached_disable_irq(u_int irq) | ||
936 | { | ||
937 | check_arg_irq(irq); | ||
938 | save_irq_src_vp[irq - open_pic_irq_offset] |= OPENPIC_MASK; | ||
939 | } | ||
940 | |||
941 | /* WARNING: Can be called directly by the cpufreq code with NULL parameter, | ||
942 | * we need something better to deal with that... Maybe switch to S1 for | ||
943 | * cpufreq changes | ||
944 | */ | ||
945 | int openpic_suspend(struct sys_device *sysdev, u32 state) | ||
946 | { | ||
947 | int i; | ||
948 | unsigned long flags; | ||
949 | |||
950 | spin_lock_irqsave(&openpic_setup_lock, flags); | ||
951 | |||
952 | if (openpic_suspend_count++ > 0) { | ||
953 | spin_unlock_irqrestore(&openpic_setup_lock, flags); | ||
954 | return 0; | ||
955 | } | ||
956 | |||
957 | openpic_set_priority(0xf); | ||
958 | |||
959 | open_pic.enable = openpic_cached_enable_irq; | ||
960 | open_pic.disable = openpic_cached_disable_irq; | ||
961 | |||
962 | for (i=0; i<NumProcessors; i++) { | ||
963 | save_cpu_task_pri[i] = openpic_read(&OpenPIC->Processor[i].Current_Task_Priority); | ||
964 | openpic_writefield(&OpenPIC->Processor[i].Current_Task_Priority, | ||
965 | OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf); | ||
966 | } | ||
967 | |||
968 | for (i=0; i<OPENPIC_NUM_IPI; i++) | ||
969 | save_ipi_vp[i] = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(i)); | ||
970 | for (i=0; i<NumSources; i++) { | ||
971 | if (ISR[i] == 0) | ||
972 | continue; | ||
973 | save_irq_src_vp[i] = openpic_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY; | ||
974 | save_irq_src_dest[i] = openpic_read(&ISR[i]->Destination); | ||
975 | } | ||
976 | |||
977 | spin_unlock_irqrestore(&openpic_setup_lock, flags); | ||
978 | |||
979 | return 0; | ||
980 | } | ||
981 | |||
982 | /* WARNING: Can be called directly by the cpufreq code with NULL parameter, | ||
983 | * we need something better to deal with that... Maybe switch to S1 for | ||
984 | * cpufreq changes | ||
985 | */ | ||
986 | int openpic_resume(struct sys_device *sysdev) | ||
987 | { | ||
988 | int i; | ||
989 | unsigned long flags; | ||
990 | u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | | ||
991 | OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK | | ||
992 | OPENPIC_MASK; | ||
993 | |||
994 | spin_lock_irqsave(&openpic_setup_lock, flags); | ||
995 | |||
996 | if ((--openpic_suspend_count) > 0) { | ||
997 | spin_unlock_irqrestore(&openpic_setup_lock, flags); | ||
998 | return 0; | ||
999 | } | ||
1000 | |||
1001 | openpic_reset(); | ||
1002 | |||
1003 | /* OpenPIC sometimes seem to need some time to be fully back up... */ | ||
1004 | do { | ||
1005 | openpic_set_spurious(OPENPIC_VEC_SPURIOUS); | ||
1006 | } while(openpic_readfield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK) | ||
1007 | != OPENPIC_VEC_SPURIOUS); | ||
1008 | |||
1009 | openpic_disable_8259_pass_through(); | ||
1010 | |||
1011 | for (i=0; i<OPENPIC_NUM_IPI; i++) | ||
1012 | openpic_write(&OpenPIC->Global.IPI_Vector_Priority(i), | ||
1013 | save_ipi_vp[i]); | ||
1014 | for (i=0; i<NumSources; i++) { | ||
1015 | if (ISR[i] == 0) | ||
1016 | continue; | ||
1017 | openpic_write(&ISR[i]->Destination, save_irq_src_dest[i]); | ||
1018 | openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); | ||
1019 | /* make sure mask gets to controller before we return to user */ | ||
1020 | do { | ||
1021 | openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); | ||
1022 | } while (openpic_readfield(&ISR[i]->Vector_Priority, vppmask) | ||
1023 | != (save_irq_src_vp[i] & vppmask)); | ||
1024 | } | ||
1025 | for (i=0; i<NumProcessors; i++) | ||
1026 | openpic_write(&OpenPIC->Processor[i].Current_Task_Priority, | ||
1027 | save_cpu_task_pri[i]); | ||
1028 | |||
1029 | open_pic.enable = openpic_enable_irq; | ||
1030 | open_pic.disable = openpic_disable_irq; | ||
1031 | |||
1032 | openpic_set_priority(0); | ||
1033 | |||
1034 | spin_unlock_irqrestore(&openpic_setup_lock, flags); | ||
1035 | |||
1036 | return 0; | ||
1037 | } | ||
1038 | |||
1039 | #endif /* CONFIG_PM */ | ||
1040 | |||
1041 | static struct sysdev_class openpic_sysclass = { | ||
1042 | set_kset_name("openpic"), | ||
1043 | }; | ||
1044 | |||
1045 | static struct sys_device device_openpic = { | ||
1046 | .id = 0, | ||
1047 | .cls = &openpic_sysclass, | ||
1048 | }; | ||
1049 | |||
1050 | static struct sysdev_driver driver_openpic = { | ||
1051 | #ifdef CONFIG_PM | ||
1052 | .suspend = &openpic_suspend, | ||
1053 | .resume = &openpic_resume, | ||
1054 | #endif /* CONFIG_PM */ | ||
1055 | }; | ||
1056 | |||
1057 | static int __init init_openpic_sysfs(void) | ||
1058 | { | ||
1059 | int rc; | ||
1060 | |||
1061 | if (!OpenPIC_Addr) | ||
1062 | return -ENODEV; | ||
1063 | printk(KERN_DEBUG "Registering openpic with sysfs...\n"); | ||
1064 | rc = sysdev_class_register(&openpic_sysclass); | ||
1065 | if (rc) { | ||
1066 | printk(KERN_ERR "Failed registering openpic sys class\n"); | ||
1067 | return -ENODEV; | ||
1068 | } | ||
1069 | rc = sysdev_register(&device_openpic); | ||
1070 | if (rc) { | ||
1071 | printk(KERN_ERR "Failed registering openpic sys device\n"); | ||
1072 | return -ENODEV; | ||
1073 | } | ||
1074 | rc = sysdev_driver_register(&openpic_sysclass, &driver_openpic); | ||
1075 | if (rc) { | ||
1076 | printk(KERN_ERR "Failed registering openpic sys driver\n"); | ||
1077 | return -ENODEV; | ||
1078 | } | ||
1079 | return 0; | ||
1080 | } | ||
1081 | |||
1082 | subsys_initcall(init_openpic_sysfs); | ||
1083 | |||
diff --git a/arch/ppc/syslib/open_pic2.c b/arch/ppc/syslib/open_pic2.c new file mode 100644 index 000000000000..ea26da0d8b6b --- /dev/null +++ b/arch/ppc/syslib/open_pic2.c | |||
@@ -0,0 +1,716 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling | ||
3 | * | ||
4 | * Copyright (C) 1997 Geert Uytterhoeven | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive | ||
8 | * for more details. | ||
9 | * | ||
10 | * This is a duplicate of open_pic.c that deals with U3s MPIC on | ||
11 | * G5 PowerMacs. It's the same file except it's using big endian | ||
12 | * register accesses | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/types.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/irq.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/sysdev.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <asm/ptrace.h> | ||
25 | #include <asm/signal.h> | ||
26 | #include <asm/io.h> | ||
27 | #include <asm/irq.h> | ||
28 | #include <asm/prom.h> | ||
29 | #include <asm/sections.h> | ||
30 | #include <asm/open_pic.h> | ||
31 | #include <asm/i8259.h> | ||
32 | |||
33 | #include "open_pic_defs.h" | ||
34 | |||
35 | void *OpenPIC2_Addr; | ||
36 | static volatile struct OpenPIC *OpenPIC2 = NULL; | ||
37 | /* | ||
38 | * We define OpenPIC_InitSenses table thusly: | ||
39 | * bit 0x1: sense, 0 for edge and 1 for level. | ||
40 | * bit 0x2: polarity, 0 for negative, 1 for positive. | ||
41 | */ | ||
42 | extern u_int OpenPIC_NumInitSenses; | ||
43 | extern u_char *OpenPIC_InitSenses; | ||
44 | extern int use_of_interrupt_tree; | ||
45 | |||
46 | static u_int NumProcessors; | ||
47 | static u_int NumSources; | ||
48 | static int open_pic2_irq_offset; | ||
49 | static volatile OpenPIC_Source *ISR[NR_IRQS]; | ||
50 | |||
51 | /* Global Operations */ | ||
52 | static void openpic2_disable_8259_pass_through(void); | ||
53 | static void openpic2_set_priority(u_int pri); | ||
54 | static void openpic2_set_spurious(u_int vector); | ||
55 | |||
56 | /* Timer Interrupts */ | ||
57 | static void openpic2_inittimer(u_int timer, u_int pri, u_int vector); | ||
58 | static void openpic2_maptimer(u_int timer, u_int cpumask); | ||
59 | |||
60 | /* Interrupt Sources */ | ||
61 | static void openpic2_enable_irq(u_int irq); | ||
62 | static void openpic2_disable_irq(u_int irq); | ||
63 | static void openpic2_initirq(u_int irq, u_int pri, u_int vector, int polarity, | ||
64 | int is_level); | ||
65 | static void openpic2_mapirq(u_int irq, u_int cpumask, u_int keepmask); | ||
66 | |||
67 | /* | ||
68 | * These functions are not used but the code is kept here | ||
69 | * for completeness and future reference. | ||
70 | */ | ||
71 | static void openpic2_reset(void); | ||
72 | #ifdef notused | ||
73 | static void openpic2_enable_8259_pass_through(void); | ||
74 | static u_int openpic2_get_priority(void); | ||
75 | static u_int openpic2_get_spurious(void); | ||
76 | static void openpic2_set_sense(u_int irq, int sense); | ||
77 | #endif /* notused */ | ||
78 | |||
79 | /* | ||
80 | * Description of the openpic for the higher-level irq code | ||
81 | */ | ||
82 | static void openpic2_end_irq(unsigned int irq_nr); | ||
83 | static void openpic2_ack_irq(unsigned int irq_nr); | ||
84 | |||
85 | struct hw_interrupt_type open_pic2 = { | ||
86 | " OpenPIC2 ", | ||
87 | NULL, | ||
88 | NULL, | ||
89 | openpic2_enable_irq, | ||
90 | openpic2_disable_irq, | ||
91 | openpic2_ack_irq, | ||
92 | openpic2_end_irq, | ||
93 | }; | ||
94 | |||
95 | /* | ||
96 | * Accesses to the current processor's openpic registers | ||
97 | * On cascaded controller, this is only CPU 0 | ||
98 | */ | ||
99 | #define THIS_CPU Processor[0] | ||
100 | #define DECL_THIS_CPU | ||
101 | #define CHECK_THIS_CPU | ||
102 | |||
103 | #if 1 | ||
104 | #define check_arg_ipi(ipi) \ | ||
105 | if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \ | ||
106 | printk("open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi); | ||
107 | #define check_arg_timer(timer) \ | ||
108 | if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \ | ||
109 | printk("open_pic.c:%d: illegal timer %d\n", __LINE__, timer); | ||
110 | #define check_arg_vec(vec) \ | ||
111 | if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \ | ||
112 | printk("open_pic.c:%d: illegal vector %d\n", __LINE__, vec); | ||
113 | #define check_arg_pri(pri) \ | ||
114 | if (pri < 0 || pri >= OPENPIC_NUM_PRI) \ | ||
115 | printk("open_pic.c:%d: illegal priority %d\n", __LINE__, pri); | ||
116 | /* | ||
117 | * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's | ||
118 | * data has probably been corrupted and we're going to panic or deadlock later | ||
119 | * anyway --Troy | ||
120 | */ | ||
121 | extern unsigned long* _get_SP(void); | ||
122 | #define check_arg_irq(irq) \ | ||
123 | if (irq < open_pic2_irq_offset || irq >= NumSources+open_pic2_irq_offset \ | ||
124 | || ISR[irq - open_pic2_irq_offset] == 0) { \ | ||
125 | printk("open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \ | ||
126 | /*print_backtrace(_get_SP());*/ } | ||
127 | #define check_arg_cpu(cpu) \ | ||
128 | if (cpu < 0 || cpu >= NumProcessors){ \ | ||
129 | printk("open_pic2.c:%d: illegal cpu %d\n", __LINE__, cpu); \ | ||
130 | /*print_backtrace(_get_SP());*/ } | ||
131 | #else | ||
132 | #define check_arg_ipi(ipi) do {} while (0) | ||
133 | #define check_arg_timer(timer) do {} while (0) | ||
134 | #define check_arg_vec(vec) do {} while (0) | ||
135 | #define check_arg_pri(pri) do {} while (0) | ||
136 | #define check_arg_irq(irq) do {} while (0) | ||
137 | #define check_arg_cpu(cpu) do {} while (0) | ||
138 | #endif | ||
139 | |||
140 | static u_int openpic2_read(volatile u_int *addr) | ||
141 | { | ||
142 | u_int val; | ||
143 | |||
144 | val = in_be32(addr); | ||
145 | return val; | ||
146 | } | ||
147 | |||
148 | static inline void openpic2_write(volatile u_int *addr, u_int val) | ||
149 | { | ||
150 | out_be32(addr, val); | ||
151 | } | ||
152 | |||
153 | static inline u_int openpic2_readfield(volatile u_int *addr, u_int mask) | ||
154 | { | ||
155 | u_int val = openpic2_read(addr); | ||
156 | return val & mask; | ||
157 | } | ||
158 | |||
159 | inline void openpic2_writefield(volatile u_int *addr, u_int mask, | ||
160 | u_int field) | ||
161 | { | ||
162 | u_int val = openpic2_read(addr); | ||
163 | openpic2_write(addr, (val & ~mask) | (field & mask)); | ||
164 | } | ||
165 | |||
166 | static inline void openpic2_clearfield(volatile u_int *addr, u_int mask) | ||
167 | { | ||
168 | openpic2_writefield(addr, mask, 0); | ||
169 | } | ||
170 | |||
171 | static inline void openpic2_setfield(volatile u_int *addr, u_int mask) | ||
172 | { | ||
173 | openpic2_writefield(addr, mask, mask); | ||
174 | } | ||
175 | |||
176 | static void openpic2_safe_writefield(volatile u_int *addr, u_int mask, | ||
177 | u_int field) | ||
178 | { | ||
179 | openpic2_setfield(addr, OPENPIC_MASK); | ||
180 | while (openpic2_read(addr) & OPENPIC_ACTIVITY); | ||
181 | openpic2_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK); | ||
182 | } | ||
183 | |||
184 | static void openpic2_reset(void) | ||
185 | { | ||
186 | openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, | ||
187 | OPENPIC_CONFIG_RESET); | ||
188 | while (openpic2_readfield(&OpenPIC2->Global.Global_Configuration0, | ||
189 | OPENPIC_CONFIG_RESET)) | ||
190 | mb(); | ||
191 | } | ||
192 | |||
193 | void __init openpic2_set_sources(int first_irq, int num_irqs, void *first_ISR) | ||
194 | { | ||
195 | volatile OpenPIC_Source *src = first_ISR; | ||
196 | int i, last_irq; | ||
197 | |||
198 | last_irq = first_irq + num_irqs; | ||
199 | if (last_irq > NumSources) | ||
200 | NumSources = last_irq; | ||
201 | if (src == 0) | ||
202 | src = &((struct OpenPIC *)OpenPIC2_Addr)->Source[first_irq]; | ||
203 | for (i = first_irq; i < last_irq; ++i, ++src) | ||
204 | ISR[i] = src; | ||
205 | } | ||
206 | |||
207 | /* | ||
208 | * The `offset' parameter defines where the interrupts handled by the | ||
209 | * OpenPIC start in the space of interrupt numbers that the kernel knows | ||
210 | * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the | ||
211 | * kernel's interrupt numbering scheme. | ||
212 | * We assume there is only one OpenPIC. | ||
213 | */ | ||
214 | void __init openpic2_init(int offset) | ||
215 | { | ||
216 | u_int t, i; | ||
217 | u_int timerfreq; | ||
218 | const char *version; | ||
219 | |||
220 | if (!OpenPIC2_Addr) { | ||
221 | printk("No OpenPIC2 found !\n"); | ||
222 | return; | ||
223 | } | ||
224 | OpenPIC2 = (volatile struct OpenPIC *)OpenPIC2_Addr; | ||
225 | |||
226 | if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122); | ||
227 | |||
228 | t = openpic2_read(&OpenPIC2->Global.Feature_Reporting0); | ||
229 | switch (t & OPENPIC_FEATURE_VERSION_MASK) { | ||
230 | case 1: | ||
231 | version = "1.0"; | ||
232 | break; | ||
233 | case 2: | ||
234 | version = "1.2"; | ||
235 | break; | ||
236 | case 3: | ||
237 | version = "1.3"; | ||
238 | break; | ||
239 | default: | ||
240 | version = "?"; | ||
241 | break; | ||
242 | } | ||
243 | NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >> | ||
244 | OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1; | ||
245 | if (NumSources == 0) | ||
246 | openpic2_set_sources(0, | ||
247 | ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >> | ||
248 | OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1, | ||
249 | NULL); | ||
250 | printk("OpenPIC (2) Version %s (%d CPUs and %d IRQ sources) at %p\n", | ||
251 | version, NumProcessors, NumSources, OpenPIC2); | ||
252 | timerfreq = openpic2_read(&OpenPIC2->Global.Timer_Frequency); | ||
253 | if (timerfreq) | ||
254 | printk("OpenPIC timer frequency is %d.%06d MHz\n", | ||
255 | timerfreq / 1000000, timerfreq % 1000000); | ||
256 | |||
257 | open_pic2_irq_offset = offset; | ||
258 | |||
259 | /* Initialize timer interrupts */ | ||
260 | if ( ppc_md.progress ) ppc_md.progress("openpic2: timer",0x3ba); | ||
261 | for (i = 0; i < OPENPIC_NUM_TIMERS; i++) { | ||
262 | /* Disabled, Priority 0 */ | ||
263 | openpic2_inittimer(i, 0, OPENPIC2_VEC_TIMER+i+offset); | ||
264 | /* No processor */ | ||
265 | openpic2_maptimer(i, 0); | ||
266 | } | ||
267 | |||
268 | /* Initialize external interrupts */ | ||
269 | if (ppc_md.progress) ppc_md.progress("openpic2: external",0x3bc); | ||
270 | |||
271 | openpic2_set_priority(0xf); | ||
272 | |||
273 | /* Init all external sources, including possibly the cascade. */ | ||
274 | for (i = 0; i < NumSources; i++) { | ||
275 | int sense; | ||
276 | |||
277 | if (ISR[i] == 0) | ||
278 | continue; | ||
279 | |||
280 | /* the bootloader may have left it enabled (bad !) */ | ||
281 | openpic2_disable_irq(i+offset); | ||
282 | |||
283 | sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \ | ||
284 | (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE); | ||
285 | |||
286 | if (sense & IRQ_SENSE_MASK) | ||
287 | irq_desc[i+offset].status = IRQ_LEVEL; | ||
288 | |||
289 | /* Enabled, Priority 8 */ | ||
290 | openpic2_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK), | ||
291 | (sense & IRQ_SENSE_MASK)); | ||
292 | /* Processor 0 */ | ||
293 | openpic2_mapirq(i, 1<<0, 0); | ||
294 | } | ||
295 | |||
296 | /* Init descriptors */ | ||
297 | for (i = offset; i < NumSources + offset; i++) | ||
298 | irq_desc[i].handler = &open_pic2; | ||
299 | |||
300 | /* Initialize the spurious interrupt */ | ||
301 | if (ppc_md.progress) ppc_md.progress("openpic2: spurious",0x3bd); | ||
302 | openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+offset); | ||
303 | |||
304 | openpic2_disable_8259_pass_through(); | ||
305 | openpic2_set_priority(0); | ||
306 | |||
307 | if (ppc_md.progress) ppc_md.progress("openpic2: exit",0x222); | ||
308 | } | ||
309 | |||
310 | #ifdef notused | ||
311 | static void openpic2_enable_8259_pass_through(void) | ||
312 | { | ||
313 | openpic2_clearfield(&OpenPIC2->Global.Global_Configuration0, | ||
314 | OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); | ||
315 | } | ||
316 | #endif /* notused */ | ||
317 | |||
318 | /* This can't be __init, it is used in openpic_sleep_restore_intrs */ | ||
319 | static void openpic2_disable_8259_pass_through(void) | ||
320 | { | ||
321 | openpic2_setfield(&OpenPIC2->Global.Global_Configuration0, | ||
322 | OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE); | ||
323 | } | ||
324 | |||
325 | /* | ||
326 | * Find out the current interrupt | ||
327 | */ | ||
328 | u_int openpic2_irq(void) | ||
329 | { | ||
330 | u_int vec; | ||
331 | DECL_THIS_CPU; | ||
332 | |||
333 | CHECK_THIS_CPU; | ||
334 | vec = openpic2_readfield(&OpenPIC2->THIS_CPU.Interrupt_Acknowledge, | ||
335 | OPENPIC_VECTOR_MASK); | ||
336 | return vec; | ||
337 | } | ||
338 | |||
339 | void openpic2_eoi(void) | ||
340 | { | ||
341 | DECL_THIS_CPU; | ||
342 | |||
343 | CHECK_THIS_CPU; | ||
344 | openpic2_write(&OpenPIC2->THIS_CPU.EOI, 0); | ||
345 | /* Handle PCI write posting */ | ||
346 | (void)openpic2_read(&OpenPIC2->THIS_CPU.EOI); | ||
347 | } | ||
348 | |||
349 | #ifdef notused | ||
350 | static u_int openpic2_get_priority(void) | ||
351 | { | ||
352 | DECL_THIS_CPU; | ||
353 | |||
354 | CHECK_THIS_CPU; | ||
355 | return openpic2_readfield(&OpenPIC2->THIS_CPU.Current_Task_Priority, | ||
356 | OPENPIC_CURRENT_TASK_PRIORITY_MASK); | ||
357 | } | ||
358 | #endif /* notused */ | ||
359 | |||
360 | static void __init openpic2_set_priority(u_int pri) | ||
361 | { | ||
362 | DECL_THIS_CPU; | ||
363 | |||
364 | CHECK_THIS_CPU; | ||
365 | check_arg_pri(pri); | ||
366 | openpic2_writefield(&OpenPIC2->THIS_CPU.Current_Task_Priority, | ||
367 | OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri); | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * Get/set the spurious vector | ||
372 | */ | ||
373 | #ifdef notused | ||
374 | static u_int openpic2_get_spurious(void) | ||
375 | { | ||
376 | return openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, | ||
377 | OPENPIC_VECTOR_MASK); | ||
378 | } | ||
379 | #endif /* notused */ | ||
380 | |||
381 | /* This can't be __init, it is used in openpic_sleep_restore_intrs */ | ||
382 | static void openpic2_set_spurious(u_int vec) | ||
383 | { | ||
384 | check_arg_vec(vec); | ||
385 | openpic2_writefield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK, | ||
386 | vec); | ||
387 | } | ||
388 | |||
389 | static DEFINE_SPINLOCK(openpic2_setup_lock); | ||
390 | |||
391 | /* | ||
392 | * Initialize a timer interrupt (and disable it) | ||
393 | * | ||
394 | * timer: OpenPIC timer number | ||
395 | * pri: interrupt source priority | ||
396 | * vec: the vector it will produce | ||
397 | */ | ||
398 | static void __init openpic2_inittimer(u_int timer, u_int pri, u_int vec) | ||
399 | { | ||
400 | check_arg_timer(timer); | ||
401 | check_arg_pri(pri); | ||
402 | check_arg_vec(vec); | ||
403 | openpic2_safe_writefield(&OpenPIC2->Global.Timer[timer].Vector_Priority, | ||
404 | OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK, | ||
405 | (pri << OPENPIC_PRIORITY_SHIFT) | vec); | ||
406 | } | ||
407 | |||
408 | /* | ||
409 | * Map a timer interrupt to one or more CPUs | ||
410 | */ | ||
411 | static void __init openpic2_maptimer(u_int timer, u_int cpumask) | ||
412 | { | ||
413 | check_arg_timer(timer); | ||
414 | openpic2_write(&OpenPIC2->Global.Timer[timer].Destination, | ||
415 | cpumask); | ||
416 | } | ||
417 | |||
418 | /* | ||
419 | * Initalize the interrupt source which will generate an NMI. | ||
420 | * This raises the interrupt's priority from 8 to 9. | ||
421 | * | ||
422 | * irq: The logical IRQ which generates an NMI. | ||
423 | */ | ||
424 | void __init | ||
425 | openpic2_init_nmi_irq(u_int irq) | ||
426 | { | ||
427 | check_arg_irq(irq); | ||
428 | openpic2_safe_writefield(&ISR[irq - open_pic2_irq_offset]->Vector_Priority, | ||
429 | OPENPIC_PRIORITY_MASK, | ||
430 | 9 << OPENPIC_PRIORITY_SHIFT); | ||
431 | } | ||
432 | |||
433 | /* | ||
434 | * | ||
435 | * All functions below take an offset'ed irq argument | ||
436 | * | ||
437 | */ | ||
438 | |||
439 | |||
440 | /* | ||
441 | * Enable/disable an external interrupt source | ||
442 | * | ||
443 | * Externally called, irq is an offseted system-wide interrupt number | ||
444 | */ | ||
445 | static void openpic2_enable_irq(u_int irq) | ||
446 | { | ||
447 | volatile u_int *vpp; | ||
448 | |||
449 | check_arg_irq(irq); | ||
450 | vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority; | ||
451 | openpic2_clearfield(vpp, OPENPIC_MASK); | ||
452 | /* make sure mask gets to controller before we return to user */ | ||
453 | do { | ||
454 | mb(); /* sync is probably useless here */ | ||
455 | } while (openpic2_readfield(vpp, OPENPIC_MASK)); | ||
456 | } | ||
457 | |||
458 | static void openpic2_disable_irq(u_int irq) | ||
459 | { | ||
460 | volatile u_int *vpp; | ||
461 | u32 vp; | ||
462 | |||
463 | check_arg_irq(irq); | ||
464 | vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority; | ||
465 | openpic2_setfield(vpp, OPENPIC_MASK); | ||
466 | /* make sure mask gets to controller before we return to user */ | ||
467 | do { | ||
468 | mb(); /* sync is probably useless here */ | ||
469 | vp = openpic2_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY); | ||
470 | } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK)); | ||
471 | } | ||
472 | |||
473 | |||
474 | /* | ||
475 | * Initialize an interrupt source (and disable it!) | ||
476 | * | ||
477 | * irq: OpenPIC interrupt number | ||
478 | * pri: interrupt source priority | ||
479 | * vec: the vector it will produce | ||
480 | * pol: polarity (1 for positive, 0 for negative) | ||
481 | * sense: 1 for level, 0 for edge | ||
482 | */ | ||
483 | static void __init | ||
484 | openpic2_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense) | ||
485 | { | ||
486 | openpic2_safe_writefield(&ISR[irq]->Vector_Priority, | ||
487 | OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | | ||
488 | OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK, | ||
489 | (pri << OPENPIC_PRIORITY_SHIFT) | vec | | ||
490 | (pol ? OPENPIC_POLARITY_POSITIVE : | ||
491 | OPENPIC_POLARITY_NEGATIVE) | | ||
492 | (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE)); | ||
493 | } | ||
494 | |||
495 | /* | ||
496 | * Map an interrupt source to one or more CPUs | ||
497 | */ | ||
498 | static void openpic2_mapirq(u_int irq, u_int physmask, u_int keepmask) | ||
499 | { | ||
500 | if (ISR[irq] == 0) | ||
501 | return; | ||
502 | if (keepmask != 0) | ||
503 | physmask |= openpic2_read(&ISR[irq]->Destination) & keepmask; | ||
504 | openpic2_write(&ISR[irq]->Destination, physmask); | ||
505 | } | ||
506 | |||
507 | #ifdef notused | ||
508 | /* | ||
509 | * Set the sense for an interrupt source (and disable it!) | ||
510 | * | ||
511 | * sense: 1 for level, 0 for edge | ||
512 | */ | ||
513 | static void openpic2_set_sense(u_int irq, int sense) | ||
514 | { | ||
515 | if (ISR[irq] != 0) | ||
516 | openpic2_safe_writefield(&ISR[irq]->Vector_Priority, | ||
517 | OPENPIC_SENSE_LEVEL, | ||
518 | (sense ? OPENPIC_SENSE_LEVEL : 0)); | ||
519 | } | ||
520 | #endif /* notused */ | ||
521 | |||
522 | /* No spinlocks, should not be necessary with the OpenPIC | ||
523 | * (1 register = 1 interrupt and we have the desc lock). | ||
524 | */ | ||
525 | static void openpic2_ack_irq(unsigned int irq_nr) | ||
526 | { | ||
527 | openpic2_disable_irq(irq_nr); | ||
528 | openpic2_eoi(); | ||
529 | } | ||
530 | |||
531 | static void openpic2_end_irq(unsigned int irq_nr) | ||
532 | { | ||
533 | if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) | ||
534 | openpic2_enable_irq(irq_nr); | ||
535 | } | ||
536 | |||
537 | int | ||
538 | openpic2_get_irq(struct pt_regs *regs) | ||
539 | { | ||
540 | int irq = openpic2_irq(); | ||
541 | |||
542 | if (irq == (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset)) | ||
543 | irq = -1; | ||
544 | return irq; | ||
545 | } | ||
546 | |||
547 | #ifdef CONFIG_PM | ||
548 | |||
549 | /* | ||
550 | * We implement the IRQ controller as a sysdev and put it | ||
551 | * to sleep at powerdown stage (the callback is named suspend, | ||
552 | * but it's old semantics, for the Device Model, it's really | ||
553 | * powerdown). The possible problem is that another sysdev that | ||
554 | * happens to be suspend after this one will have interrupts off, | ||
555 | * that may be an issue... For now, this isn't an issue on pmac | ||
556 | * though... | ||
557 | */ | ||
558 | |||
559 | static u32 save_ipi_vp[OPENPIC_NUM_IPI]; | ||
560 | static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES]; | ||
561 | static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES]; | ||
562 | static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS]; | ||
563 | static int openpic_suspend_count; | ||
564 | |||
565 | static void openpic2_cached_enable_irq(u_int irq) | ||
566 | { | ||
567 | check_arg_irq(irq); | ||
568 | save_irq_src_vp[irq - open_pic2_irq_offset] &= ~OPENPIC_MASK; | ||
569 | } | ||
570 | |||
571 | static void openpic2_cached_disable_irq(u_int irq) | ||
572 | { | ||
573 | check_arg_irq(irq); | ||
574 | save_irq_src_vp[irq - open_pic2_irq_offset] |= OPENPIC_MASK; | ||
575 | } | ||
576 | |||
577 | /* WARNING: Can be called directly by the cpufreq code with NULL parameter, | ||
578 | * we need something better to deal with that... Maybe switch to S1 for | ||
579 | * cpufreq changes | ||
580 | */ | ||
581 | int openpic2_suspend(struct sys_device *sysdev, u32 state) | ||
582 | { | ||
583 | int i; | ||
584 | unsigned long flags; | ||
585 | |||
586 | spin_lock_irqsave(&openpic2_setup_lock, flags); | ||
587 | |||
588 | if (openpic_suspend_count++ > 0) { | ||
589 | spin_unlock_irqrestore(&openpic2_setup_lock, flags); | ||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | open_pic2.enable = openpic2_cached_enable_irq; | ||
594 | open_pic2.disable = openpic2_cached_disable_irq; | ||
595 | |||
596 | for (i=0; i<NumProcessors; i++) { | ||
597 | save_cpu_task_pri[i] = openpic2_read(&OpenPIC2->Processor[i].Current_Task_Priority); | ||
598 | openpic2_writefield(&OpenPIC2->Processor[i].Current_Task_Priority, | ||
599 | OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf); | ||
600 | } | ||
601 | |||
602 | for (i=0; i<OPENPIC_NUM_IPI; i++) | ||
603 | save_ipi_vp[i] = openpic2_read(&OpenPIC2->Global.IPI_Vector_Priority(i)); | ||
604 | for (i=0; i<NumSources; i++) { | ||
605 | if (ISR[i] == 0) | ||
606 | continue; | ||
607 | save_irq_src_vp[i] = openpic2_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY; | ||
608 | save_irq_src_dest[i] = openpic2_read(&ISR[i]->Destination); | ||
609 | } | ||
610 | |||
611 | spin_unlock_irqrestore(&openpic2_setup_lock, flags); | ||
612 | |||
613 | return 0; | ||
614 | } | ||
615 | |||
616 | /* WARNING: Can be called directly by the cpufreq code with NULL parameter, | ||
617 | * we need something better to deal with that... Maybe switch to S1 for | ||
618 | * cpufreq changes | ||
619 | */ | ||
620 | int openpic2_resume(struct sys_device *sysdev) | ||
621 | { | ||
622 | int i; | ||
623 | unsigned long flags; | ||
624 | u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK | | ||
625 | OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK | | ||
626 | OPENPIC_MASK; | ||
627 | |||
628 | spin_lock_irqsave(&openpic2_setup_lock, flags); | ||
629 | |||
630 | if ((--openpic_suspend_count) > 0) { | ||
631 | spin_unlock_irqrestore(&openpic2_setup_lock, flags); | ||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | openpic2_reset(); | ||
636 | |||
637 | /* OpenPIC sometimes seem to need some time to be fully back up... */ | ||
638 | do { | ||
639 | openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+open_pic2_irq_offset); | ||
640 | } while(openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK) | ||
641 | != (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset)); | ||
642 | |||
643 | openpic2_disable_8259_pass_through(); | ||
644 | |||
645 | for (i=0; i<OPENPIC_NUM_IPI; i++) | ||
646 | openpic2_write(&OpenPIC2->Global.IPI_Vector_Priority(i), | ||
647 | save_ipi_vp[i]); | ||
648 | for (i=0; i<NumSources; i++) { | ||
649 | if (ISR[i] == 0) | ||
650 | continue; | ||
651 | openpic2_write(&ISR[i]->Destination, save_irq_src_dest[i]); | ||
652 | openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); | ||
653 | /* make sure mask gets to controller before we return to user */ | ||
654 | do { | ||
655 | openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]); | ||
656 | } while (openpic2_readfield(&ISR[i]->Vector_Priority, vppmask) | ||
657 | != (save_irq_src_vp[i] & vppmask)); | ||
658 | } | ||
659 | for (i=0; i<NumProcessors; i++) | ||
660 | openpic2_write(&OpenPIC2->Processor[i].Current_Task_Priority, | ||
661 | save_cpu_task_pri[i]); | ||
662 | |||
663 | open_pic2.enable = openpic2_enable_irq; | ||
664 | open_pic2.disable = openpic2_disable_irq; | ||
665 | |||
666 | spin_unlock_irqrestore(&openpic2_setup_lock, flags); | ||
667 | |||
668 | return 0; | ||
669 | } | ||
670 | |||
671 | #endif /* CONFIG_PM */ | ||
672 | |||
673 | /* HACK ALERT */ | ||
674 | static struct sysdev_class openpic2_sysclass = { | ||
675 | set_kset_name("openpic2"), | ||
676 | }; | ||
677 | |||
678 | static struct sys_device device_openpic2 = { | ||
679 | .id = 0, | ||
680 | .cls = &openpic2_sysclass, | ||
681 | }; | ||
682 | |||
683 | static struct sysdev_driver driver_openpic2 = { | ||
684 | #ifdef CONFIG_PM | ||
685 | .suspend = &openpic2_suspend, | ||
686 | .resume = &openpic2_resume, | ||
687 | #endif /* CONFIG_PM */ | ||
688 | }; | ||
689 | |||
690 | static int __init init_openpic2_sysfs(void) | ||
691 | { | ||
692 | int rc; | ||
693 | |||
694 | if (!OpenPIC2_Addr) | ||
695 | return -ENODEV; | ||
696 | printk(KERN_DEBUG "Registering openpic2 with sysfs...\n"); | ||
697 | rc = sysdev_class_register(&openpic2_sysclass); | ||
698 | if (rc) { | ||
699 | printk(KERN_ERR "Failed registering openpic sys class\n"); | ||
700 | return -ENODEV; | ||
701 | } | ||
702 | rc = sysdev_register(&device_openpic2); | ||
703 | if (rc) { | ||
704 | printk(KERN_ERR "Failed registering openpic sys device\n"); | ||
705 | return -ENODEV; | ||
706 | } | ||
707 | rc = sysdev_driver_register(&openpic2_sysclass, &driver_openpic2); | ||
708 | if (rc) { | ||
709 | printk(KERN_ERR "Failed registering openpic sys driver\n"); | ||
710 | return -ENODEV; | ||
711 | } | ||
712 | return 0; | ||
713 | } | ||
714 | |||
715 | subsys_initcall(init_openpic2_sysfs); | ||
716 | |||
diff --git a/arch/ppc/syslib/open_pic_defs.h b/arch/ppc/syslib/open_pic_defs.h new file mode 100644 index 000000000000..4f05624b249e --- /dev/null +++ b/arch/ppc/syslib/open_pic_defs.h | |||
@@ -0,0 +1,292 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/open_pic_defs.h -- OpenPIC definitions | ||
3 | * | ||
4 | * Copyright (C) 1997 Geert Uytterhoeven | ||
5 | * | ||
6 | * This file is based on the following documentation: | ||
7 | * | ||
8 | * The Open Programmable Interrupt Controller (PIC) | ||
9 | * Register Interface Specification Revision 1.2 | ||
10 | * | ||
11 | * Issue Date: October 1995 | ||
12 | * | ||
13 | * Issued jointly by Advanced Micro Devices and Cyrix Corporation | ||
14 | * | ||
15 | * AMD is a registered trademark of Advanced Micro Devices, Inc. | ||
16 | * Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc. | ||
17 | * All Rights Reserved. | ||
18 | * | ||
19 | * To receive a copy of this documentation, send an email to openpic@amd.com. | ||
20 | * | ||
21 | * This file is subject to the terms and conditions of the GNU General Public | ||
22 | * License. See the file COPYING in the main directory of this archive | ||
23 | * for more details. | ||
24 | */ | ||
25 | |||
26 | #ifndef _LINUX_OPENPIC_H | ||
27 | #define _LINUX_OPENPIC_H | ||
28 | |||
29 | #ifdef __KERNEL__ | ||
30 | |||
31 | /* | ||
32 | * OpenPIC supports up to 2048 interrupt sources and up to 32 processors | ||
33 | */ | ||
34 | |||
35 | #define OPENPIC_MAX_SOURCES 2048 | ||
36 | #define OPENPIC_MAX_PROCESSORS 32 | ||
37 | #define OPENPIC_MAX_ISU 16 | ||
38 | |||
39 | #define OPENPIC_NUM_TIMERS 4 | ||
40 | #define OPENPIC_NUM_IPI 4 | ||
41 | #define OPENPIC_NUM_PRI 16 | ||
42 | #define OPENPIC_NUM_VECTORS 256 | ||
43 | |||
44 | |||
45 | |||
46 | /* | ||
47 | * OpenPIC Registers are 32 bits and aligned on 128 bit boundaries | ||
48 | */ | ||
49 | |||
50 | typedef struct _OpenPIC_Reg { | ||
51 | u_int Reg; /* Little endian! */ | ||
52 | char Pad[0xc]; | ||
53 | } OpenPIC_Reg; | ||
54 | |||
55 | |||
56 | /* | ||
57 | * Per Processor Registers | ||
58 | */ | ||
59 | |||
60 | typedef struct _OpenPIC_Processor { | ||
61 | /* | ||
62 | * Private Shadow Registers (for SLiC backwards compatibility) | ||
63 | */ | ||
64 | u_int IPI0_Dispatch_Shadow; /* Write Only */ | ||
65 | char Pad1[0x4]; | ||
66 | u_int IPI0_Vector_Priority_Shadow; /* Read/Write */ | ||
67 | char Pad2[0x34]; | ||
68 | /* | ||
69 | * Interprocessor Interrupt Command Ports | ||
70 | */ | ||
71 | OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI]; /* Write Only */ | ||
72 | /* | ||
73 | * Current Task Priority Register | ||
74 | */ | ||
75 | OpenPIC_Reg _Current_Task_Priority; /* Read/Write */ | ||
76 | char Pad3[0x10]; | ||
77 | /* | ||
78 | * Interrupt Acknowledge Register | ||
79 | */ | ||
80 | OpenPIC_Reg _Interrupt_Acknowledge; /* Read Only */ | ||
81 | /* | ||
82 | * End of Interrupt (EOI) Register | ||
83 | */ | ||
84 | OpenPIC_Reg _EOI; /* Read/Write */ | ||
85 | char Pad5[0xf40]; | ||
86 | } OpenPIC_Processor; | ||
87 | |||
88 | |||
89 | /* | ||
90 | * Timer Registers | ||
91 | */ | ||
92 | |||
93 | typedef struct _OpenPIC_Timer { | ||
94 | OpenPIC_Reg _Current_Count; /* Read Only */ | ||
95 | OpenPIC_Reg _Base_Count; /* Read/Write */ | ||
96 | OpenPIC_Reg _Vector_Priority; /* Read/Write */ | ||
97 | OpenPIC_Reg _Destination; /* Read/Write */ | ||
98 | } OpenPIC_Timer; | ||
99 | |||
100 | |||
101 | /* | ||
102 | * Global Registers | ||
103 | */ | ||
104 | |||
105 | typedef struct _OpenPIC_Global { | ||
106 | /* | ||
107 | * Feature Reporting Registers | ||
108 | */ | ||
109 | OpenPIC_Reg _Feature_Reporting0; /* Read Only */ | ||
110 | OpenPIC_Reg _Feature_Reporting1; /* Future Expansion */ | ||
111 | /* | ||
112 | * Global Configuration Registers | ||
113 | */ | ||
114 | OpenPIC_Reg _Global_Configuration0; /* Read/Write */ | ||
115 | OpenPIC_Reg _Global_Configuration1; /* Future Expansion */ | ||
116 | /* | ||
117 | * Vendor Specific Registers | ||
118 | */ | ||
119 | OpenPIC_Reg _Vendor_Specific[4]; | ||
120 | /* | ||
121 | * Vendor Identification Register | ||
122 | */ | ||
123 | OpenPIC_Reg _Vendor_Identification; /* Read Only */ | ||
124 | /* | ||
125 | * Processor Initialization Register | ||
126 | */ | ||
127 | OpenPIC_Reg _Processor_Initialization; /* Read/Write */ | ||
128 | /* | ||
129 | * IPI Vector/Priority Registers | ||
130 | */ | ||
131 | OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI]; /* Read/Write */ | ||
132 | /* | ||
133 | * Spurious Vector Register | ||
134 | */ | ||
135 | OpenPIC_Reg _Spurious_Vector; /* Read/Write */ | ||
136 | /* | ||
137 | * Global Timer Registers | ||
138 | */ | ||
139 | OpenPIC_Reg _Timer_Frequency; /* Read/Write */ | ||
140 | OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS]; | ||
141 | char Pad1[0xee00]; | ||
142 | } OpenPIC_Global; | ||
143 | |||
144 | |||
145 | /* | ||
146 | * Interrupt Source Registers | ||
147 | */ | ||
148 | |||
149 | typedef struct _OpenPIC_Source { | ||
150 | OpenPIC_Reg _Vector_Priority; /* Read/Write */ | ||
151 | OpenPIC_Reg _Destination; /* Read/Write */ | ||
152 | } OpenPIC_Source, *OpenPIC_SourcePtr; | ||
153 | |||
154 | |||
155 | /* | ||
156 | * OpenPIC Register Map | ||
157 | */ | ||
158 | |||
159 | struct OpenPIC { | ||
160 | char Pad1[0x1000]; | ||
161 | /* | ||
162 | * Global Registers | ||
163 | */ | ||
164 | OpenPIC_Global Global; | ||
165 | /* | ||
166 | * Interrupt Source Configuration Registers | ||
167 | */ | ||
168 | OpenPIC_Source Source[OPENPIC_MAX_SOURCES]; | ||
169 | /* | ||
170 | * Per Processor Registers | ||
171 | */ | ||
172 | OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS]; | ||
173 | }; | ||
174 | |||
175 | extern volatile struct OpenPIC __iomem *OpenPIC; | ||
176 | |||
177 | |||
178 | /* | ||
179 | * Current Task Priority Register | ||
180 | */ | ||
181 | |||
182 | #define OPENPIC_CURRENT_TASK_PRIORITY_MASK 0x0000000f | ||
183 | |||
184 | /* | ||
185 | * Who Am I Register | ||
186 | */ | ||
187 | |||
188 | #define OPENPIC_WHO_AM_I_ID_MASK 0x0000001f | ||
189 | |||
190 | /* | ||
191 | * Feature Reporting Register 0 | ||
192 | */ | ||
193 | |||
194 | #define OPENPIC_FEATURE_LAST_SOURCE_MASK 0x07ff0000 | ||
195 | #define OPENPIC_FEATURE_LAST_SOURCE_SHIFT 16 | ||
196 | #define OPENPIC_FEATURE_LAST_PROCESSOR_MASK 0x00001f00 | ||
197 | #define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT 8 | ||
198 | #define OPENPIC_FEATURE_VERSION_MASK 0x000000ff | ||
199 | |||
200 | /* | ||
201 | * Global Configuration Register 0 | ||
202 | */ | ||
203 | |||
204 | #define OPENPIC_CONFIG_RESET 0x80000000 | ||
205 | #define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000 | ||
206 | #define OPENPIC_CONFIG_BASE_MASK 0x000fffff | ||
207 | |||
208 | /* | ||
209 | * Global Configuration Register 1 | ||
210 | * This is the EICR on EPICs. | ||
211 | */ | ||
212 | |||
213 | #define OPENPIC_EICR_S_CLK_MASK 0x70000000 | ||
214 | #define OPENPIC_EICR_SIE 0x08000000 | ||
215 | |||
216 | /* | ||
217 | * Vendor Identification Register | ||
218 | */ | ||
219 | |||
220 | #define OPENPIC_VENDOR_ID_STEPPING_MASK 0x00ff0000 | ||
221 | #define OPENPIC_VENDOR_ID_STEPPING_SHIFT 16 | ||
222 | #define OPENPIC_VENDOR_ID_DEVICE_ID_MASK 0x0000ff00 | ||
223 | #define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT 8 | ||
224 | #define OPENPIC_VENDOR_ID_VENDOR_ID_MASK 0x000000ff | ||
225 | |||
226 | /* | ||
227 | * Vector/Priority Registers | ||
228 | */ | ||
229 | |||
230 | #define OPENPIC_MASK 0x80000000 | ||
231 | #define OPENPIC_ACTIVITY 0x40000000 /* Read Only */ | ||
232 | #define OPENPIC_PRIORITY_MASK 0x000f0000 | ||
233 | #define OPENPIC_PRIORITY_SHIFT 16 | ||
234 | #define OPENPIC_VECTOR_MASK 0x000000ff | ||
235 | |||
236 | |||
237 | /* | ||
238 | * Interrupt Source Registers | ||
239 | */ | ||
240 | |||
241 | #define OPENPIC_POLARITY_POSITIVE 0x00800000 | ||
242 | #define OPENPIC_POLARITY_NEGATIVE 0x00000000 | ||
243 | #define OPENPIC_POLARITY_MASK 0x00800000 | ||
244 | #define OPENPIC_SENSE_LEVEL 0x00400000 | ||
245 | #define OPENPIC_SENSE_EDGE 0x00000000 | ||
246 | #define OPENPIC_SENSE_MASK 0x00400000 | ||
247 | |||
248 | |||
249 | /* | ||
250 | * Timer Registers | ||
251 | */ | ||
252 | |||
253 | #define OPENPIC_COUNT_MASK 0x7fffffff | ||
254 | #define OPENPIC_TIMER_TOGGLE 0x80000000 | ||
255 | #define OPENPIC_TIMER_COUNT_INHIBIT 0x80000000 | ||
256 | |||
257 | |||
258 | /* | ||
259 | * Aliases to make life simpler | ||
260 | */ | ||
261 | |||
262 | /* Per Processor Registers */ | ||
263 | #define IPI_Dispatch(i) _IPI_Dispatch[i].Reg | ||
264 | #define Current_Task_Priority _Current_Task_Priority.Reg | ||
265 | #define Interrupt_Acknowledge _Interrupt_Acknowledge.Reg | ||
266 | #define EOI _EOI.Reg | ||
267 | |||
268 | /* Global Registers */ | ||
269 | #define Feature_Reporting0 _Feature_Reporting0.Reg | ||
270 | #define Feature_Reporting1 _Feature_Reporting1.Reg | ||
271 | #define Global_Configuration0 _Global_Configuration0.Reg | ||
272 | #define Global_Configuration1 _Global_Configuration1.Reg | ||
273 | #define Vendor_Specific(i) _Vendor_Specific[i].Reg | ||
274 | #define Vendor_Identification _Vendor_Identification.Reg | ||
275 | #define Processor_Initialization _Processor_Initialization.Reg | ||
276 | #define IPI_Vector_Priority(i) _IPI_Vector_Priority[i].Reg | ||
277 | #define Spurious_Vector _Spurious_Vector.Reg | ||
278 | #define Timer_Frequency _Timer_Frequency.Reg | ||
279 | |||
280 | /* Timer Registers */ | ||
281 | #define Current_Count _Current_Count.Reg | ||
282 | #define Base_Count _Base_Count.Reg | ||
283 | #define Vector_Priority _Vector_Priority.Reg | ||
284 | #define Destination _Destination.Reg | ||
285 | |||
286 | /* Interrupt Source Registers */ | ||
287 | #define Vector_Priority _Vector_Priority.Reg | ||
288 | #define Destination _Destination.Reg | ||
289 | |||
290 | #endif /* __KERNEL__ */ | ||
291 | |||
292 | #endif /* _LINUX_OPENPIC_H */ | ||
diff --git a/arch/ppc/syslib/pci_auto.c b/arch/ppc/syslib/pci_auto.c new file mode 100644 index 000000000000..d64207c2a972 --- /dev/null +++ b/arch/ppc/syslib/pci_auto.c | |||
@@ -0,0 +1,517 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/pci_auto.c | ||
3 | * | ||
4 | * PCI autoconfiguration library | ||
5 | * | ||
6 | * Author: Matt Porter <mporter@mvista.com> | ||
7 | * | ||
8 | * 2001 (c) MontaVista, Software, Inc. This file is licensed under | ||
9 | * the terms of the GNU General Public License version 2. This program | ||
10 | * is licensed "as is" without any warranty of any kind, whether express | ||
11 | * or implied. | ||
12 | */ | ||
13 | |||
14 | /* | ||
15 | * The CardBus support is very preliminary. Preallocating space is | ||
16 | * the way to go but will require some change in card services to | ||
17 | * make it useful. Eventually this will ensure that we can put | ||
18 | * multiple CB bridges behind multiple P2P bridges. For now, at | ||
19 | * least it ensures that we place the CB bridge BAR and assigned | ||
20 | * initial bus numbers. I definitely need to do something about | ||
21 | * the lack of 16-bit I/O support. -MDP | ||
22 | */ | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/pci.h> | ||
27 | |||
28 | #include <asm/pci-bridge.h> | ||
29 | |||
30 | #define PCIAUTO_IDE_MODE_MASK 0x05 | ||
31 | |||
32 | #undef DEBUG | ||
33 | |||
34 | #ifdef DEBUG | ||
35 | #define DBG(x...) printk(x) | ||
36 | #else | ||
37 | #define DBG(x...) | ||
38 | #endif /* DEBUG */ | ||
39 | |||
40 | static int pciauto_upper_iospc; | ||
41 | static int pciauto_upper_memspc; | ||
42 | |||
43 | void __init pciauto_setup_bars(struct pci_controller *hose, | ||
44 | int current_bus, | ||
45 | int pci_devfn, | ||
46 | int bar_limit) | ||
47 | { | ||
48 | int bar_response, bar_size, bar_value; | ||
49 | int bar, addr_mask; | ||
50 | int * upper_limit; | ||
51 | int found_mem64 = 0; | ||
52 | |||
53 | DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n", | ||
54 | current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) ); | ||
55 | |||
56 | for (bar = PCI_BASE_ADDRESS_0; bar <= bar_limit; bar+=4) { | ||
57 | /* Tickle the BAR and get the response */ | ||
58 | early_write_config_dword(hose, | ||
59 | current_bus, | ||
60 | pci_devfn, | ||
61 | bar, | ||
62 | 0xffffffff); | ||
63 | early_read_config_dword(hose, | ||
64 | current_bus, | ||
65 | pci_devfn, | ||
66 | bar, | ||
67 | &bar_response); | ||
68 | |||
69 | /* If BAR is not implemented go to the next BAR */ | ||
70 | if (!bar_response) | ||
71 | continue; | ||
72 | |||
73 | /* Check the BAR type and set our address mask */ | ||
74 | if (bar_response & PCI_BASE_ADDRESS_SPACE) { | ||
75 | addr_mask = PCI_BASE_ADDRESS_IO_MASK; | ||
76 | upper_limit = &pciauto_upper_iospc; | ||
77 | DBG("PCI Autoconfig: BAR 0x%x, I/O, ", bar); | ||
78 | } else { | ||
79 | if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == | ||
80 | PCI_BASE_ADDRESS_MEM_TYPE_64) | ||
81 | found_mem64 = 1; | ||
82 | |||
83 | addr_mask = PCI_BASE_ADDRESS_MEM_MASK; | ||
84 | upper_limit = &pciauto_upper_memspc; | ||
85 | DBG("PCI Autoconfig: BAR 0x%x, Mem ", bar); | ||
86 | } | ||
87 | |||
88 | /* Calculate requested size */ | ||
89 | bar_size = ~(bar_response & addr_mask) + 1; | ||
90 | |||
91 | /* Allocate a base address */ | ||
92 | bar_value = (*upper_limit - bar_size) & ~(bar_size - 1); | ||
93 | |||
94 | /* Write it out and update our limit */ | ||
95 | early_write_config_dword(hose, | ||
96 | current_bus, | ||
97 | pci_devfn, | ||
98 | bar, | ||
99 | bar_value); | ||
100 | |||
101 | *upper_limit = bar_value; | ||
102 | |||
103 | /* | ||
104 | * If we are a 64-bit decoder then increment to the | ||
105 | * upper 32 bits of the bar and force it to locate | ||
106 | * in the lower 4GB of memory. | ||
107 | */ | ||
108 | if (found_mem64) { | ||
109 | bar += 4; | ||
110 | early_write_config_dword(hose, | ||
111 | current_bus, | ||
112 | pci_devfn, | ||
113 | bar, | ||
114 | 0x00000000); | ||
115 | found_mem64 = 0; | ||
116 | } | ||
117 | |||
118 | DBG("size=0x%x, address=0x%x\n", | ||
119 | bar_size, bar_value); | ||
120 | } | ||
121 | |||
122 | } | ||
123 | |||
124 | void __init pciauto_prescan_setup_bridge(struct pci_controller *hose, | ||
125 | int current_bus, | ||
126 | int pci_devfn, | ||
127 | int sub_bus, | ||
128 | int *iosave, | ||
129 | int *memsave) | ||
130 | { | ||
131 | /* Configure bus number registers */ | ||
132 | early_write_config_byte(hose, | ||
133 | current_bus, | ||
134 | pci_devfn, | ||
135 | PCI_PRIMARY_BUS, | ||
136 | current_bus); | ||
137 | early_write_config_byte(hose, | ||
138 | current_bus, | ||
139 | pci_devfn, | ||
140 | PCI_SECONDARY_BUS, | ||
141 | sub_bus + 1); | ||
142 | early_write_config_byte(hose, | ||
143 | current_bus, | ||
144 | pci_devfn, | ||
145 | PCI_SUBORDINATE_BUS, | ||
146 | 0xff); | ||
147 | |||
148 | /* Round memory allocator to 1MB boundary */ | ||
149 | pciauto_upper_memspc &= ~(0x100000 - 1); | ||
150 | *memsave = pciauto_upper_memspc; | ||
151 | |||
152 | /* Round I/O allocator to 4KB boundary */ | ||
153 | pciauto_upper_iospc &= ~(0x1000 - 1); | ||
154 | *iosave = pciauto_upper_iospc; | ||
155 | |||
156 | /* Set up memory and I/O filter limits, assume 32-bit I/O space */ | ||
157 | early_write_config_word(hose, | ||
158 | current_bus, | ||
159 | pci_devfn, | ||
160 | PCI_MEMORY_LIMIT, | ||
161 | ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); | ||
162 | early_write_config_byte(hose, | ||
163 | current_bus, | ||
164 | pci_devfn, | ||
165 | PCI_IO_LIMIT, | ||
166 | ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8); | ||
167 | early_write_config_word(hose, | ||
168 | current_bus, | ||
169 | pci_devfn, | ||
170 | PCI_IO_LIMIT_UPPER16, | ||
171 | ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16); | ||
172 | |||
173 | /* Zero upper 32 bits of prefetchable base/limit */ | ||
174 | early_write_config_dword(hose, | ||
175 | current_bus, | ||
176 | pci_devfn, | ||
177 | PCI_PREF_BASE_UPPER32, | ||
178 | 0); | ||
179 | early_write_config_dword(hose, | ||
180 | current_bus, | ||
181 | pci_devfn, | ||
182 | PCI_PREF_LIMIT_UPPER32, | ||
183 | 0); | ||
184 | } | ||
185 | |||
186 | void __init pciauto_postscan_setup_bridge(struct pci_controller *hose, | ||
187 | int current_bus, | ||
188 | int pci_devfn, | ||
189 | int sub_bus, | ||
190 | int *iosave, | ||
191 | int *memsave) | ||
192 | { | ||
193 | int cmdstat; | ||
194 | |||
195 | /* Configure bus number registers */ | ||
196 | early_write_config_byte(hose, | ||
197 | current_bus, | ||
198 | pci_devfn, | ||
199 | PCI_SUBORDINATE_BUS, | ||
200 | sub_bus); | ||
201 | |||
202 | /* | ||
203 | * Round memory allocator to 1MB boundary. | ||
204 | * If no space used, allocate minimum. | ||
205 | */ | ||
206 | pciauto_upper_memspc &= ~(0x100000 - 1); | ||
207 | if (*memsave == pciauto_upper_memspc) | ||
208 | pciauto_upper_memspc -= 0x00100000; | ||
209 | |||
210 | early_write_config_word(hose, | ||
211 | current_bus, | ||
212 | pci_devfn, | ||
213 | PCI_MEMORY_BASE, | ||
214 | pciauto_upper_memspc >> 16); | ||
215 | |||
216 | /* Allocate 1MB for pre-fretch */ | ||
217 | early_write_config_word(hose, | ||
218 | current_bus, | ||
219 | pci_devfn, | ||
220 | PCI_PREF_MEMORY_LIMIT, | ||
221 | ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16); | ||
222 | |||
223 | pciauto_upper_memspc -= 0x100000; | ||
224 | |||
225 | early_write_config_word(hose, | ||
226 | current_bus, | ||
227 | pci_devfn, | ||
228 | PCI_PREF_MEMORY_BASE, | ||
229 | pciauto_upper_memspc >> 16); | ||
230 | |||
231 | /* Round I/O allocator to 4KB boundary */ | ||
232 | pciauto_upper_iospc &= ~(0x1000 - 1); | ||
233 | if (*iosave == pciauto_upper_iospc) | ||
234 | pciauto_upper_iospc -= 0x1000; | ||
235 | |||
236 | early_write_config_byte(hose, | ||
237 | current_bus, | ||
238 | pci_devfn, | ||
239 | PCI_IO_BASE, | ||
240 | (pciauto_upper_iospc & 0x0000f000) >> 8); | ||
241 | early_write_config_word(hose, | ||
242 | current_bus, | ||
243 | pci_devfn, | ||
244 | PCI_IO_BASE_UPPER16, | ||
245 | pciauto_upper_iospc >> 16); | ||
246 | |||
247 | /* Enable memory and I/O accesses, enable bus master */ | ||
248 | early_read_config_dword(hose, | ||
249 | current_bus, | ||
250 | pci_devfn, | ||
251 | PCI_COMMAND, | ||
252 | &cmdstat); | ||
253 | early_write_config_dword(hose, | ||
254 | current_bus, | ||
255 | pci_devfn, | ||
256 | PCI_COMMAND, | ||
257 | cmdstat | | ||
258 | PCI_COMMAND_IO | | ||
259 | PCI_COMMAND_MEMORY | | ||
260 | PCI_COMMAND_MASTER); | ||
261 | } | ||
262 | |||
263 | void __init pciauto_prescan_setup_cardbus_bridge(struct pci_controller *hose, | ||
264 | int current_bus, | ||
265 | int pci_devfn, | ||
266 | int sub_bus, | ||
267 | int *iosave, | ||
268 | int *memsave) | ||
269 | { | ||
270 | /* Configure bus number registers */ | ||
271 | early_write_config_byte(hose, | ||
272 | current_bus, | ||
273 | pci_devfn, | ||
274 | PCI_PRIMARY_BUS, | ||
275 | current_bus); | ||
276 | early_write_config_byte(hose, | ||
277 | current_bus, | ||
278 | pci_devfn, | ||
279 | PCI_SECONDARY_BUS, | ||
280 | sub_bus + 1); | ||
281 | early_write_config_byte(hose, | ||
282 | current_bus, | ||
283 | pci_devfn, | ||
284 | PCI_SUBORDINATE_BUS, | ||
285 | 0xff); | ||
286 | |||
287 | /* Round memory allocator to 4KB boundary */ | ||
288 | pciauto_upper_memspc &= ~(0x1000 - 1); | ||
289 | *memsave = pciauto_upper_memspc; | ||
290 | |||
291 | /* Round I/O allocator to 4 byte boundary */ | ||
292 | pciauto_upper_iospc &= ~(0x4 - 1); | ||
293 | *iosave = pciauto_upper_iospc; | ||
294 | |||
295 | /* Set up memory and I/O filter limits, assume 32-bit I/O space */ | ||
296 | early_write_config_dword(hose, | ||
297 | current_bus, | ||
298 | pci_devfn, | ||
299 | 0x20, | ||
300 | pciauto_upper_memspc - 1); | ||
301 | early_write_config_dword(hose, | ||
302 | current_bus, | ||
303 | pci_devfn, | ||
304 | 0x30, | ||
305 | pciauto_upper_iospc - 1); | ||
306 | } | ||
307 | |||
308 | void __init pciauto_postscan_setup_cardbus_bridge(struct pci_controller *hose, | ||
309 | int current_bus, | ||
310 | int pci_devfn, | ||
311 | int sub_bus, | ||
312 | int *iosave, | ||
313 | int *memsave) | ||
314 | { | ||
315 | int cmdstat; | ||
316 | |||
317 | /* | ||
318 | * Configure subordinate bus number. The PCI subsystem | ||
319 | * bus scan will renumber buses (reserving three additional | ||
320 | * for this PCI<->CardBus bridge for the case where a CardBus | ||
321 | * adapter contains a P2P or CB2CB bridge. | ||
322 | */ | ||
323 | early_write_config_byte(hose, | ||
324 | current_bus, | ||
325 | pci_devfn, | ||
326 | PCI_SUBORDINATE_BUS, | ||
327 | sub_bus); | ||
328 | |||
329 | /* | ||
330 | * Reserve an additional 4MB for mem space and 16KB for | ||
331 | * I/O space. This should cover any additional space | ||
332 | * requirement of unusual CardBus devices with | ||
333 | * additional bridges that can consume more address space. | ||
334 | * | ||
335 | * Although pcmcia-cs currently will reprogram bridge | ||
336 | * windows, the goal is to add an option to leave them | ||
337 | * alone and use the bridge window ranges as the regions | ||
338 | * that are searched for free resources upon hot-insertion | ||
339 | * of a device. This will allow a PCI<->CardBus bridge | ||
340 | * configured by this routine to happily live behind a | ||
341 | * P2P bridge in a system. | ||
342 | */ | ||
343 | pciauto_upper_memspc -= 0x00400000; | ||
344 | pciauto_upper_iospc -= 0x00004000; | ||
345 | |||
346 | /* Round memory allocator to 4KB boundary */ | ||
347 | pciauto_upper_memspc &= ~(0x1000 - 1); | ||
348 | |||
349 | early_write_config_dword(hose, | ||
350 | current_bus, | ||
351 | pci_devfn, | ||
352 | 0x1c, | ||
353 | pciauto_upper_memspc); | ||
354 | |||
355 | /* Round I/O allocator to 4 byte boundary */ | ||
356 | pciauto_upper_iospc &= ~(0x4 - 1); | ||
357 | early_write_config_dword(hose, | ||
358 | current_bus, | ||
359 | pci_devfn, | ||
360 | 0x2c, | ||
361 | pciauto_upper_iospc); | ||
362 | |||
363 | /* Enable memory and I/O accesses, enable bus master */ | ||
364 | early_read_config_dword(hose, | ||
365 | current_bus, | ||
366 | pci_devfn, | ||
367 | PCI_COMMAND, | ||
368 | &cmdstat); | ||
369 | early_write_config_dword(hose, | ||
370 | current_bus, | ||
371 | pci_devfn, | ||
372 | PCI_COMMAND, | ||
373 | cmdstat | | ||
374 | PCI_COMMAND_IO | | ||
375 | PCI_COMMAND_MEMORY | | ||
376 | PCI_COMMAND_MASTER); | ||
377 | } | ||
378 | |||
379 | int __init pciauto_bus_scan(struct pci_controller *hose, int current_bus) | ||
380 | { | ||
381 | int sub_bus, pci_devfn, pci_class, cmdstat, found_multi = 0; | ||
382 | unsigned short vid; | ||
383 | unsigned char header_type; | ||
384 | |||
385 | /* | ||
386 | * Fetch our I/O and memory space upper boundaries used | ||
387 | * to allocated base addresses on this hose. | ||
388 | */ | ||
389 | if (current_bus == hose->first_busno) { | ||
390 | pciauto_upper_iospc = hose->io_space.end + 1; | ||
391 | pciauto_upper_memspc = hose->mem_space.end + 1; | ||
392 | } | ||
393 | |||
394 | sub_bus = current_bus; | ||
395 | |||
396 | for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) { | ||
397 | /* Skip our host bridge */ | ||
398 | if ( (current_bus == hose->first_busno) && (pci_devfn == 0) ) | ||
399 | continue; | ||
400 | |||
401 | if (PCI_FUNC(pci_devfn) && !found_multi) | ||
402 | continue; | ||
403 | |||
404 | /* If config space read fails from this device, move on */ | ||
405 | if (early_read_config_byte(hose, | ||
406 | current_bus, | ||
407 | pci_devfn, | ||
408 | PCI_HEADER_TYPE, | ||
409 | &header_type)) | ||
410 | continue; | ||
411 | |||
412 | if (!PCI_FUNC(pci_devfn)) | ||
413 | found_multi = header_type & 0x80; | ||
414 | |||
415 | early_read_config_word(hose, | ||
416 | current_bus, | ||
417 | pci_devfn, | ||
418 | PCI_VENDOR_ID, | ||
419 | &vid); | ||
420 | |||
421 | if (vid != 0xffff) { | ||
422 | early_read_config_dword(hose, | ||
423 | current_bus, | ||
424 | pci_devfn, | ||
425 | PCI_CLASS_REVISION, &pci_class); | ||
426 | if ( (pci_class >> 16) == PCI_CLASS_BRIDGE_PCI ) { | ||
427 | int iosave, memsave; | ||
428 | |||
429 | DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn)); | ||
430 | /* Allocate PCI I/O and/or memory space */ | ||
431 | pciauto_setup_bars(hose, | ||
432 | current_bus, | ||
433 | pci_devfn, | ||
434 | PCI_BASE_ADDRESS_1); | ||
435 | |||
436 | pciauto_prescan_setup_bridge(hose, | ||
437 | current_bus, | ||
438 | pci_devfn, | ||
439 | sub_bus, | ||
440 | &iosave, | ||
441 | &memsave); | ||
442 | sub_bus = pciauto_bus_scan(hose, sub_bus+1); | ||
443 | pciauto_postscan_setup_bridge(hose, | ||
444 | current_bus, | ||
445 | pci_devfn, | ||
446 | sub_bus, | ||
447 | &iosave, | ||
448 | &memsave); | ||
449 | } else if ((pci_class >> 16) == PCI_CLASS_BRIDGE_CARDBUS) { | ||
450 | int iosave, memsave; | ||
451 | |||
452 | DBG("PCI Autoconfig: Found CardBus bridge, device %d function %d\n", PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn)); | ||
453 | /* Place CardBus Socket/ExCA registers */ | ||
454 | pciauto_setup_bars(hose, | ||
455 | current_bus, | ||
456 | pci_devfn, | ||
457 | PCI_BASE_ADDRESS_0); | ||
458 | |||
459 | pciauto_prescan_setup_cardbus_bridge(hose, | ||
460 | current_bus, | ||
461 | pci_devfn, | ||
462 | sub_bus, | ||
463 | &iosave, | ||
464 | &memsave); | ||
465 | sub_bus = pciauto_bus_scan(hose, sub_bus+1); | ||
466 | pciauto_postscan_setup_cardbus_bridge(hose, | ||
467 | current_bus, | ||
468 | pci_devfn, | ||
469 | sub_bus, | ||
470 | &iosave, | ||
471 | &memsave); | ||
472 | } else { | ||
473 | if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) { | ||
474 | unsigned char prg_iface; | ||
475 | |||
476 | early_read_config_byte(hose, | ||
477 | current_bus, | ||
478 | pci_devfn, | ||
479 | PCI_CLASS_PROG, | ||
480 | &prg_iface); | ||
481 | if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) { | ||
482 | DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n"); | ||
483 | continue; | ||
484 | } | ||
485 | } | ||
486 | /* Allocate PCI I/O and/or memory space */ | ||
487 | pciauto_setup_bars(hose, | ||
488 | current_bus, | ||
489 | pci_devfn, | ||
490 | PCI_BASE_ADDRESS_5); | ||
491 | |||
492 | /* | ||
493 | * Enable some standard settings | ||
494 | */ | ||
495 | early_read_config_dword(hose, | ||
496 | current_bus, | ||
497 | pci_devfn, | ||
498 | PCI_COMMAND, | ||
499 | &cmdstat); | ||
500 | early_write_config_dword(hose, | ||
501 | current_bus, | ||
502 | pci_devfn, | ||
503 | PCI_COMMAND, | ||
504 | cmdstat | | ||
505 | PCI_COMMAND_IO | | ||
506 | PCI_COMMAND_MEMORY | | ||
507 | PCI_COMMAND_MASTER); | ||
508 | early_write_config_byte(hose, | ||
509 | current_bus, | ||
510 | pci_devfn, | ||
511 | PCI_LATENCY_TIMER, | ||
512 | 0x80); | ||
513 | } | ||
514 | } | ||
515 | } | ||
516 | return sub_bus; | ||
517 | } | ||
diff --git a/arch/ppc/syslib/ppc403_pic.c b/arch/ppc/syslib/ppc403_pic.c new file mode 100644 index 000000000000..06cb0af2a58d --- /dev/null +++ b/arch/ppc/syslib/ppc403_pic.c | |||
@@ -0,0 +1,127 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> | ||
4 | * | ||
5 | * Module name: ppc403_pic.c | ||
6 | * | ||
7 | * Description: | ||
8 | * Interrupt controller driver for PowerPC 403-based processors. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * The PowerPC 403 cores' Asynchronous Interrupt Controller (AIC) has | ||
13 | * 32 possible interrupts, a majority of which are not implemented on | ||
14 | * all cores. There are six configurable, external interrupt pins and | ||
15 | * there are eight internal interrupts for the on-chip serial port | ||
16 | * (SPU), DMA controller, and JTAG controller. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/sched.h> | ||
22 | #include <linux/signal.h> | ||
23 | #include <linux/stddef.h> | ||
24 | |||
25 | #include <asm/processor.h> | ||
26 | #include <asm/system.h> | ||
27 | #include <asm/irq.h> | ||
28 | #include <asm/ppc4xx_pic.h> | ||
29 | |||
30 | /* Function Prototypes */ | ||
31 | |||
32 | static void ppc403_aic_enable(unsigned int irq); | ||
33 | static void ppc403_aic_disable(unsigned int irq); | ||
34 | static void ppc403_aic_disable_and_ack(unsigned int irq); | ||
35 | |||
36 | static struct hw_interrupt_type ppc403_aic = { | ||
37 | "403GC AIC", | ||
38 | NULL, | ||
39 | NULL, | ||
40 | ppc403_aic_enable, | ||
41 | ppc403_aic_disable, | ||
42 | ppc403_aic_disable_and_ack, | ||
43 | 0 | ||
44 | }; | ||
45 | |||
46 | int | ||
47 | ppc403_pic_get_irq(struct pt_regs *regs) | ||
48 | { | ||
49 | int irq; | ||
50 | unsigned long bits; | ||
51 | |||
52 | /* | ||
53 | * Only report the status of those interrupts that are actually | ||
54 | * enabled. | ||
55 | */ | ||
56 | |||
57 | bits = mfdcr(DCRN_EXISR) & mfdcr(DCRN_EXIER); | ||
58 | |||
59 | /* | ||
60 | * Walk through the interrupts from highest priority to lowest, and | ||
61 | * report the first pending interrupt found. | ||
62 | * We want PPC, not C bit numbering, so just subtract the ffs() | ||
63 | * result from 32. | ||
64 | */ | ||
65 | irq = 32 - ffs(bits); | ||
66 | |||
67 | if (irq == NR_AIC_IRQS) | ||
68 | irq = -1; | ||
69 | |||
70 | return (irq); | ||
71 | } | ||
72 | |||
73 | static void | ||
74 | ppc403_aic_enable(unsigned int irq) | ||
75 | { | ||
76 | int bit, word; | ||
77 | |||
78 | bit = irq & 0x1f; | ||
79 | word = irq >> 5; | ||
80 | |||
81 | ppc_cached_irq_mask[word] |= (1 << (31 - bit)); | ||
82 | mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); | ||
83 | } | ||
84 | |||
85 | static void | ||
86 | ppc403_aic_disable(unsigned int irq) | ||
87 | { | ||
88 | int bit, word; | ||
89 | |||
90 | bit = irq & 0x1f; | ||
91 | word = irq >> 5; | ||
92 | |||
93 | ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); | ||
94 | mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); | ||
95 | } | ||
96 | |||
97 | static void | ||
98 | ppc403_aic_disable_and_ack(unsigned int irq) | ||
99 | { | ||
100 | int bit, word; | ||
101 | |||
102 | bit = irq & 0x1f; | ||
103 | word = irq >> 5; | ||
104 | |||
105 | ppc_cached_irq_mask[word] &= ~(1 << (31 - bit)); | ||
106 | mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]); | ||
107 | mtdcr(DCRN_EXISR, (1 << (31 - bit))); | ||
108 | } | ||
109 | |||
110 | void __init | ||
111 | ppc4xx_pic_init(void) | ||
112 | { | ||
113 | int i; | ||
114 | |||
115 | /* | ||
116 | * Disable all external interrupts until they are | ||
117 | * explicity requested. | ||
118 | */ | ||
119 | ppc_cached_irq_mask[0] = 0; | ||
120 | |||
121 | mtdcr(DCRN_EXIER, ppc_cached_irq_mask[0]); | ||
122 | |||
123 | ppc_md.get_irq = ppc403_pic_get_irq; | ||
124 | |||
125 | for (i = 0; i < NR_IRQS; i++) | ||
126 | irq_desc[i].handler = &ppc403_aic; | ||
127 | } | ||
diff --git a/arch/ppc/syslib/ppc405_pci.c b/arch/ppc/syslib/ppc405_pci.c new file mode 100644 index 000000000000..81c83bf98df4 --- /dev/null +++ b/arch/ppc/syslib/ppc405_pci.c | |||
@@ -0,0 +1,177 @@ | |||
1 | /* | ||
2 | * Authors: Frank Rowand <frank_rowand@mvista.com>, | ||
3 | * Debbie Chu <debbie_chu@mvista.com>, or source@mvista.com | ||
4 | * Further modifications by Armin Kuster <akuster@mvista.com> | ||
5 | * | ||
6 | * 2000 (c) MontaVista, Software, Inc. This file is licensed under | ||
7 | * the terms of the GNU General Public License version 2. This program | ||
8 | * is licensed "as is" without any warranty of any kind, whether express | ||
9 | * or implied. | ||
10 | * | ||
11 | * Based on arch/ppc/kernel/indirect.c, Copyright (C) 1998 Gabriel Paubert. | ||
12 | */ | ||
13 | |||
14 | #include <linux/pci.h> | ||
15 | #include <asm/io.h> | ||
16 | #include <asm/system.h> | ||
17 | #include <asm/machdep.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <asm/ocp.h> | ||
21 | #include <asm/ibm4xx.h> | ||
22 | #include <asm/pci-bridge.h> | ||
23 | #include <asm/ibm_ocp_pci.h> | ||
24 | |||
25 | |||
26 | extern void bios_fixup(struct pci_controller *, struct pcil0_regs *); | ||
27 | extern int ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, | ||
28 | unsigned char pin); | ||
29 | |||
30 | void | ||
31 | ppc405_pcibios_fixup_resources(struct pci_dev *dev) | ||
32 | { | ||
33 | int i; | ||
34 | unsigned long max_host_addr; | ||
35 | unsigned long min_host_addr; | ||
36 | struct resource *res; | ||
37 | |||
38 | /* | ||
39 | * openbios puts some graphics cards in the same range as the host | ||
40 | * controller uses to map to SDRAM. Fix it. | ||
41 | */ | ||
42 | |||
43 | min_host_addr = 0; | ||
44 | max_host_addr = PPC405_PCI_MEM_BASE - 1; | ||
45 | |||
46 | for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { | ||
47 | res = dev->resource + i; | ||
48 | if (!res->start) | ||
49 | continue; | ||
50 | if ((res->flags & IORESOURCE_MEM) && | ||
51 | (((res->start >= min_host_addr) | ||
52 | && (res->start <= max_host_addr)) | ||
53 | || ((res->end >= min_host_addr) | ||
54 | && (res->end <= max_host_addr)) | ||
55 | || ((res->start < min_host_addr) | ||
56 | && (res->end > max_host_addr)) | ||
57 | ) | ||
58 | ) { | ||
59 | |||
60 | /* force pcibios_assign_resources() to assign a new address */ | ||
61 | res->end -= res->start; | ||
62 | res->start = 0; | ||
63 | } | ||
64 | } | ||
65 | } | ||
66 | |||
67 | static int | ||
68 | ppc4xx_exclude_device(unsigned char bus, unsigned char devfn) | ||
69 | { | ||
70 | /* We prevent us from seeing ourselves to avoid having | ||
71 | * the kernel try to remap our BAR #1 and fuck up bus | ||
72 | * master from external PCI devices | ||
73 | */ | ||
74 | return (bus == 0 && devfn == 0); | ||
75 | } | ||
76 | |||
77 | void | ||
78 | ppc4xx_find_bridges(void) | ||
79 | { | ||
80 | struct pci_controller *hose_a; | ||
81 | struct pcil0_regs *pcip; | ||
82 | unsigned int tmp_addr; | ||
83 | unsigned int tmp_size; | ||
84 | unsigned int reg_index; | ||
85 | unsigned int new_pmm_max = 0; | ||
86 | unsigned int new_pmm_min = 0; | ||
87 | |||
88 | isa_io_base = 0; | ||
89 | isa_mem_base = 0; | ||
90 | pci_dram_offset = 0; | ||
91 | |||
92 | #if (PSR_PCI_ARBIT_EN > 1) | ||
93 | /* Check if running in slave mode */ | ||
94 | if ((mfdcr(DCRN_CHPSR) & PSR_PCI_ARBIT_EN) == 0) { | ||
95 | printk("Running as PCI slave, kernel PCI disabled !\n"); | ||
96 | return; | ||
97 | } | ||
98 | #endif | ||
99 | /* Setup PCI32 hose */ | ||
100 | hose_a = pcibios_alloc_controller(); | ||
101 | if (!hose_a) | ||
102 | return; | ||
103 | setup_indirect_pci(hose_a, PPC405_PCI_CONFIG_ADDR, | ||
104 | PPC405_PCI_CONFIG_DATA); | ||
105 | |||
106 | pcip = ioremap(PPC4xx_PCI_LCFG_PADDR, PAGE_SIZE); | ||
107 | if (pcip != NULL) { | ||
108 | |||
109 | #if defined(CONFIG_BIOS_FIXUP) | ||
110 | bios_fixup(hose_a, pcip); | ||
111 | #endif | ||
112 | new_pmm_min = 0xffffffff; | ||
113 | for (reg_index = 0; reg_index < 3; reg_index++) { | ||
114 | tmp_size = in_le32(&pcip->pmm[reg_index].ma); // mask & attrs | ||
115 | /* test the enable bit */ | ||
116 | if ((tmp_size & 0x1) == 0) | ||
117 | continue; | ||
118 | tmp_addr = in_le32(&pcip->pmm[reg_index].pcila); // PCI addr | ||
119 | if (tmp_addr < PPC405_PCI_PHY_MEM_BASE) { | ||
120 | printk(KERN_DEBUG | ||
121 | "Disabling mapping to PCI mem addr 0x%8.8x\n", | ||
122 | tmp_addr); | ||
123 | out_le32(&pcip->pmm[reg_index].ma, tmp_size & ~1); // *_PMMOMA | ||
124 | continue; | ||
125 | } | ||
126 | tmp_addr = in_le32(&pcip->pmm[reg_index].la); // *_PMMOLA | ||
127 | if (tmp_addr < new_pmm_min) | ||
128 | new_pmm_min = tmp_addr; | ||
129 | tmp_addr = tmp_addr + | ||
130 | (0xffffffff - (tmp_size & 0xffffc000)); | ||
131 | if (tmp_addr > PPC405_PCI_UPPER_MEM) { | ||
132 | new_pmm_max = tmp_addr; // PPC405_PCI_UPPER_MEM | ||
133 | } else { | ||
134 | new_pmm_max = PPC405_PCI_UPPER_MEM; | ||
135 | } | ||
136 | |||
137 | } // for | ||
138 | |||
139 | iounmap(pcip); | ||
140 | } | ||
141 | |||
142 | hose_a->first_busno = 0; | ||
143 | hose_a->last_busno = 0xff; | ||
144 | hose_a->pci_mem_offset = 0; | ||
145 | |||
146 | /* Setup bridge memory/IO ranges & resources | ||
147 | * TODO: Handle firmwares setting up a legacy ISA mem base | ||
148 | */ | ||
149 | hose_a->io_space.start = PPC405_PCI_LOWER_IO; | ||
150 | hose_a->io_space.end = PPC405_PCI_UPPER_IO; | ||
151 | hose_a->mem_space.start = new_pmm_min; | ||
152 | hose_a->mem_space.end = new_pmm_max; | ||
153 | hose_a->io_base_phys = PPC405_PCI_PHY_IO_BASE; | ||
154 | hose_a->io_base_virt = ioremap(hose_a->io_base_phys, 0x10000); | ||
155 | hose_a->io_resource.start = 0; | ||
156 | hose_a->io_resource.end = PPC405_PCI_UPPER_IO - PPC405_PCI_LOWER_IO; | ||
157 | hose_a->io_resource.flags = IORESOURCE_IO; | ||
158 | hose_a->io_resource.name = "PCI I/O"; | ||
159 | hose_a->mem_resources[0].start = new_pmm_min; | ||
160 | hose_a->mem_resources[0].end = new_pmm_max; | ||
161 | hose_a->mem_resources[0].flags = IORESOURCE_MEM; | ||
162 | hose_a->mem_resources[0].name = "PCI Memory"; | ||
163 | isa_io_base = (int) hose_a->io_base_virt; | ||
164 | isa_mem_base = 0; /* ISA not implemented */ | ||
165 | ISA_DMA_THRESHOLD = 0x00ffffff; /* ??? ISA not implemented */ | ||
166 | |||
167 | /* Scan busses & initial setup by pci_auto */ | ||
168 | hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); | ||
169 | hose_a->last_busno = 0; | ||
170 | |||
171 | /* Setup ppc_md */ | ||
172 | ppc_md.pcibios_fixup = NULL; | ||
173 | ppc_md.pci_exclude_device = ppc4xx_exclude_device; | ||
174 | ppc_md.pcibios_fixup_resources = ppc405_pcibios_fixup_resources; | ||
175 | ppc_md.pci_swizzle = common_swizzle; | ||
176 | ppc_md.pci_map_irq = ppc405_map_irq; | ||
177 | } | ||
diff --git a/arch/ppc/syslib/ppc4xx_dma.c b/arch/ppc/syslib/ppc4xx_dma.c new file mode 100644 index 000000000000..5015ab99afd2 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_dma.c | |||
@@ -0,0 +1,708 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/ppc4xx_dma.c | ||
3 | * | ||
4 | * IBM PPC4xx DMA engine core library | ||
5 | * | ||
6 | * Copyright 2000-2004 MontaVista Software Inc. | ||
7 | * | ||
8 | * Cleaned up and converted to new DCR access | ||
9 | * Matt Porter <mporter@kernel.crashing.org> | ||
10 | * | ||
11 | * Original code by Armin Kuster <akuster@mvista.com> | ||
12 | * and Pete Popov <ppopov@mvista.com> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify it | ||
15 | * under the terms of the GNU General Public License as published by the | ||
16 | * Free Software Foundation; either version 2 of the License, or (at your | ||
17 | * option) any later version. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/config.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/mm.h> | ||
27 | #include <linux/miscdevice.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/module.h> | ||
30 | |||
31 | #include <asm/system.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/ppc4xx_dma.h> | ||
34 | |||
35 | ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS]; | ||
36 | |||
37 | int | ||
38 | ppc4xx_get_dma_status(void) | ||
39 | { | ||
40 | return (mfdcr(DCRN_DMASR)); | ||
41 | } | ||
42 | |||
43 | void | ||
44 | ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr) | ||
45 | { | ||
46 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
47 | printk("set_src_addr: bad channel: %d\n", dmanr); | ||
48 | return; | ||
49 | } | ||
50 | |||
51 | #ifdef PPC4xx_DMA_64BIT | ||
52 | mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32)); | ||
53 | #else | ||
54 | mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr); | ||
55 | #endif | ||
56 | } | ||
57 | |||
58 | void | ||
59 | ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr) | ||
60 | { | ||
61 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
62 | printk("set_dst_addr: bad channel: %d\n", dmanr); | ||
63 | return; | ||
64 | } | ||
65 | |||
66 | #ifdef PPC4xx_DMA_64BIT | ||
67 | mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32)); | ||
68 | #else | ||
69 | mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr); | ||
70 | #endif | ||
71 | } | ||
72 | |||
73 | void | ||
74 | ppc4xx_enable_dma(unsigned int dmanr) | ||
75 | { | ||
76 | unsigned int control; | ||
77 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
78 | unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR, | ||
79 | DMA_CS1 | DMA_TS1 | DMA_CH1_ERR, | ||
80 | DMA_CS2 | DMA_TS2 | DMA_CH2_ERR, | ||
81 | DMA_CS3 | DMA_TS3 | DMA_CH3_ERR}; | ||
82 | |||
83 | if (p_dma_ch->in_use) { | ||
84 | printk("enable_dma: channel %d in use\n", dmanr); | ||
85 | return; | ||
86 | } | ||
87 | |||
88 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
89 | printk("enable_dma: bad channel: %d\n", dmanr); | ||
90 | return; | ||
91 | } | ||
92 | |||
93 | if (p_dma_ch->mode == DMA_MODE_READ) { | ||
94 | /* peripheral to memory */ | ||
95 | ppc4xx_set_src_addr(dmanr, 0); | ||
96 | ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr); | ||
97 | } else if (p_dma_ch->mode == DMA_MODE_WRITE) { | ||
98 | /* memory to peripheral */ | ||
99 | ppc4xx_set_src_addr(dmanr, p_dma_ch->addr); | ||
100 | ppc4xx_set_dst_addr(dmanr, 0); | ||
101 | } | ||
102 | |||
103 | /* for other xfer modes, the addresses are already set */ | ||
104 | control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); | ||
105 | |||
106 | control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */ | ||
107 | if (p_dma_ch->mode == DMA_MODE_MM) { | ||
108 | /* software initiated memory to memory */ | ||
109 | control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE; | ||
110 | } | ||
111 | |||
112 | mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); | ||
113 | |||
114 | /* | ||
115 | * Clear the CS, TS, RI bits for the channel from DMASR. This | ||
116 | * has been observed to happen correctly only after the mode and | ||
117 | * ETD/DCE bits in DMACRx are set above. Must do this before | ||
118 | * enabling the channel. | ||
119 | */ | ||
120 | |||
121 | mtdcr(DCRN_DMASR, status_bits[dmanr]); | ||
122 | |||
123 | /* | ||
124 | * For device-paced transfers, Terminal Count Enable apparently | ||
125 | * must be on, and this must be turned on after the mode, etc. | ||
126 | * bits are cleared above (at least on Redwood-6). | ||
127 | */ | ||
128 | |||
129 | if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) || | ||
130 | (p_dma_ch->mode == DMA_MODE_MM_DEVATSRC)) | ||
131 | control |= DMA_TCE_ENABLE; | ||
132 | |||
133 | /* | ||
134 | * Now enable the channel. | ||
135 | */ | ||
136 | |||
137 | control |= (p_dma_ch->mode | DMA_CE_ENABLE); | ||
138 | |||
139 | mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); | ||
140 | |||
141 | p_dma_ch->in_use = 1; | ||
142 | } | ||
143 | |||
144 | void | ||
145 | ppc4xx_disable_dma(unsigned int dmanr) | ||
146 | { | ||
147 | unsigned int control; | ||
148 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
149 | |||
150 | if (!p_dma_ch->in_use) { | ||
151 | printk("disable_dma: channel %d not in use\n", dmanr); | ||
152 | return; | ||
153 | } | ||
154 | |||
155 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
156 | printk("disable_dma: bad channel: %d\n", dmanr); | ||
157 | return; | ||
158 | } | ||
159 | |||
160 | control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); | ||
161 | control &= ~DMA_CE_ENABLE; | ||
162 | mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); | ||
163 | |||
164 | p_dma_ch->in_use = 0; | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Sets the dma mode for single DMA transfers only. | ||
169 | * For scatter/gather transfers, the mode is passed to the | ||
170 | * alloc_dma_handle() function as one of the parameters. | ||
171 | * | ||
172 | * The mode is simply saved and used later. This allows | ||
173 | * the driver to call set_dma_mode() and set_dma_addr() in | ||
174 | * any order. | ||
175 | * | ||
176 | * Valid mode values are: | ||
177 | * | ||
178 | * DMA_MODE_READ peripheral to memory | ||
179 | * DMA_MODE_WRITE memory to peripheral | ||
180 | * DMA_MODE_MM memory to memory | ||
181 | * DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src | ||
182 | * DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst | ||
183 | */ | ||
184 | int | ||
185 | ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode) | ||
186 | { | ||
187 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
188 | |||
189 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
190 | printk("set_dma_mode: bad channel 0x%x\n", dmanr); | ||
191 | return DMA_STATUS_BAD_CHANNEL; | ||
192 | } | ||
193 | |||
194 | p_dma_ch->mode = mode; | ||
195 | |||
196 | return DMA_STATUS_GOOD; | ||
197 | } | ||
198 | |||
199 | /* | ||
200 | * Sets the DMA Count register. Note that 'count' is in bytes. | ||
201 | * However, the DMA Count register counts the number of "transfers", | ||
202 | * where each transfer is equal to the bus width. Thus, count | ||
203 | * MUST be a multiple of the bus width. | ||
204 | */ | ||
205 | void | ||
206 | ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count) | ||
207 | { | ||
208 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
209 | |||
210 | #ifdef DEBUG_4xxDMA | ||
211 | { | ||
212 | int error = 0; | ||
213 | switch (p_dma_ch->pwidth) { | ||
214 | case PW_8: | ||
215 | break; | ||
216 | case PW_16: | ||
217 | if (count & 0x1) | ||
218 | error = 1; | ||
219 | break; | ||
220 | case PW_32: | ||
221 | if (count & 0x3) | ||
222 | error = 1; | ||
223 | break; | ||
224 | case PW_64: | ||
225 | if (count & 0x7) | ||
226 | error = 1; | ||
227 | break; | ||
228 | default: | ||
229 | printk("set_dma_count: invalid bus width: 0x%x\n", | ||
230 | p_dma_ch->pwidth); | ||
231 | return; | ||
232 | } | ||
233 | if (error) | ||
234 | printk | ||
235 | ("Warning: set_dma_count count 0x%x bus width %d\n", | ||
236 | count, p_dma_ch->pwidth); | ||
237 | } | ||
238 | #endif | ||
239 | |||
240 | count = count >> p_dma_ch->shift; | ||
241 | |||
242 | mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count); | ||
243 | } | ||
244 | |||
245 | /* | ||
246 | * Returns the number of bytes left to be transfered. | ||
247 | * After a DMA transfer, this should return zero. | ||
248 | * Reading this while a DMA transfer is still in progress will return | ||
249 | * unpredictable results. | ||
250 | */ | ||
251 | int | ||
252 | ppc4xx_get_dma_residue(unsigned int dmanr) | ||
253 | { | ||
254 | unsigned int count; | ||
255 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
256 | |||
257 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
258 | printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr); | ||
259 | return DMA_STATUS_BAD_CHANNEL; | ||
260 | } | ||
261 | |||
262 | count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)); | ||
263 | |||
264 | return (count << p_dma_ch->shift); | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * Sets the DMA address for a memory to peripheral or peripheral | ||
269 | * to memory transfer. The address is just saved in the channel | ||
270 | * structure for now and used later in enable_dma(). | ||
271 | */ | ||
272 | void | ||
273 | ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr) | ||
274 | { | ||
275 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
276 | |||
277 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
278 | printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr); | ||
279 | return; | ||
280 | } | ||
281 | |||
282 | #ifdef DEBUG_4xxDMA | ||
283 | { | ||
284 | int error = 0; | ||
285 | switch (p_dma_ch->pwidth) { | ||
286 | case PW_8: | ||
287 | break; | ||
288 | case PW_16: | ||
289 | if ((unsigned) addr & 0x1) | ||
290 | error = 1; | ||
291 | break; | ||
292 | case PW_32: | ||
293 | if ((unsigned) addr & 0x3) | ||
294 | error = 1; | ||
295 | break; | ||
296 | case PW_64: | ||
297 | if ((unsigned) addr & 0x7) | ||
298 | error = 1; | ||
299 | break; | ||
300 | default: | ||
301 | printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n", | ||
302 | p_dma_ch->pwidth); | ||
303 | return; | ||
304 | } | ||
305 | if (error) | ||
306 | printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n", | ||
307 | addr, p_dma_ch->pwidth); | ||
308 | } | ||
309 | #endif | ||
310 | |||
311 | /* save dma address and program it later after we know the xfer mode */ | ||
312 | p_dma_ch->addr = addr; | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * Sets both DMA addresses for a memory to memory transfer. | ||
317 | * For memory to peripheral or peripheral to memory transfers | ||
318 | * the function set_dma_addr() should be used instead. | ||
319 | */ | ||
320 | void | ||
321 | ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr, | ||
322 | phys_addr_t dst_dma_addr) | ||
323 | { | ||
324 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
325 | printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr); | ||
326 | return; | ||
327 | } | ||
328 | |||
329 | #ifdef DEBUG_4xxDMA | ||
330 | { | ||
331 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
332 | int error = 0; | ||
333 | switch (p_dma_ch->pwidth) { | ||
334 | case PW_8: | ||
335 | break; | ||
336 | case PW_16: | ||
337 | if (((unsigned) src_dma_addr & 0x1) || | ||
338 | ((unsigned) dst_dma_addr & 0x1) | ||
339 | ) | ||
340 | error = 1; | ||
341 | break; | ||
342 | case PW_32: | ||
343 | if (((unsigned) src_dma_addr & 0x3) || | ||
344 | ((unsigned) dst_dma_addr & 0x3) | ||
345 | ) | ||
346 | error = 1; | ||
347 | break; | ||
348 | case PW_64: | ||
349 | if (((unsigned) src_dma_addr & 0x7) || | ||
350 | ((unsigned) dst_dma_addr & 0x7) | ||
351 | ) | ||
352 | error = 1; | ||
353 | break; | ||
354 | default: | ||
355 | printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n", | ||
356 | p_dma_ch->pwidth); | ||
357 | return; | ||
358 | } | ||
359 | if (error) | ||
360 | printk | ||
361 | ("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n", | ||
362 | src_dma_addr, dst_dma_addr, p_dma_ch->pwidth); | ||
363 | } | ||
364 | #endif | ||
365 | |||
366 | ppc4xx_set_src_addr(dmanr, src_dma_addr); | ||
367 | ppc4xx_set_dst_addr(dmanr, dst_dma_addr); | ||
368 | } | ||
369 | |||
370 | /* | ||
371 | * Enables the channel interrupt. | ||
372 | * | ||
373 | * If performing a scatter/gatter transfer, this function | ||
374 | * MUST be called before calling alloc_dma_handle() and building | ||
375 | * the sgl list. Otherwise, interrupts will not be enabled, if | ||
376 | * they were previously disabled. | ||
377 | */ | ||
378 | int | ||
379 | ppc4xx_enable_dma_interrupt(unsigned int dmanr) | ||
380 | { | ||
381 | unsigned int control; | ||
382 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
383 | |||
384 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
385 | printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr); | ||
386 | return DMA_STATUS_BAD_CHANNEL; | ||
387 | } | ||
388 | |||
389 | p_dma_ch->int_enable = 1; | ||
390 | |||
391 | control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); | ||
392 | control |= DMA_CIE_ENABLE; /* Channel Interrupt Enable */ | ||
393 | mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); | ||
394 | |||
395 | return DMA_STATUS_GOOD; | ||
396 | } | ||
397 | |||
398 | /* | ||
399 | * Disables the channel interrupt. | ||
400 | * | ||
401 | * If performing a scatter/gatter transfer, this function | ||
402 | * MUST be called before calling alloc_dma_handle() and building | ||
403 | * the sgl list. Otherwise, interrupts will not be disabled, if | ||
404 | * they were previously enabled. | ||
405 | */ | ||
406 | int | ||
407 | ppc4xx_disable_dma_interrupt(unsigned int dmanr) | ||
408 | { | ||
409 | unsigned int control; | ||
410 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
411 | |||
412 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
413 | printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr); | ||
414 | return DMA_STATUS_BAD_CHANNEL; | ||
415 | } | ||
416 | |||
417 | p_dma_ch->int_enable = 0; | ||
418 | |||
419 | control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); | ||
420 | control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */ | ||
421 | mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); | ||
422 | |||
423 | return DMA_STATUS_GOOD; | ||
424 | } | ||
425 | |||
426 | /* | ||
427 | * Configures a DMA channel, including the peripheral bus width, if a | ||
428 | * peripheral is attached to the channel, the polarity of the DMAReq and | ||
429 | * DMAAck signals, etc. This information should really be setup by the boot | ||
430 | * code, since most likely the configuration won't change dynamically. | ||
431 | * If the kernel has to call this function, it's recommended that it's | ||
432 | * called from platform specific init code. The driver should not need to | ||
433 | * call this function. | ||
434 | */ | ||
435 | int | ||
436 | ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init) | ||
437 | { | ||
438 | unsigned int polarity; | ||
439 | uint32_t control = 0; | ||
440 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
441 | |||
442 | DMA_MODE_READ = (unsigned long) DMA_TD; /* Peripheral to Memory */ | ||
443 | DMA_MODE_WRITE = 0; /* Memory to Peripheral */ | ||
444 | |||
445 | if (!p_init) { | ||
446 | printk("ppc4xx_init_dma_channel: NULL p_init\n"); | ||
447 | return DMA_STATUS_NULL_POINTER; | ||
448 | } | ||
449 | |||
450 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
451 | printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr); | ||
452 | return DMA_STATUS_BAD_CHANNEL; | ||
453 | } | ||
454 | |||
455 | #if DCRN_POL > 0 | ||
456 | polarity = mfdcr(DCRN_POL); | ||
457 | #else | ||
458 | polarity = 0; | ||
459 | #endif | ||
460 | |||
461 | /* Setup the control register based on the values passed to | ||
462 | * us in p_init. Then, over-write the control register with this | ||
463 | * new value. | ||
464 | */ | ||
465 | control |= SET_DMA_CONTROL; | ||
466 | |||
467 | /* clear all polarity signals and then "or" in new signal levels */ | ||
468 | polarity &= ~GET_DMA_POLARITY(dmanr); | ||
469 | polarity |= p_init->polarity; | ||
470 | #if DCRN_POL > 0 | ||
471 | mtdcr(DCRN_POL, polarity); | ||
472 | #endif | ||
473 | mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); | ||
474 | |||
475 | /* save these values in our dma channel structure */ | ||
476 | memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t)); | ||
477 | |||
478 | /* | ||
479 | * The peripheral width values written in the control register are: | ||
480 | * PW_8 0 | ||
481 | * PW_16 1 | ||
482 | * PW_32 2 | ||
483 | * PW_64 3 | ||
484 | * | ||
485 | * Since the DMA count register takes the number of "transfers", | ||
486 | * we need to divide the count sent to us in certain | ||
487 | * functions by the appropriate number. It so happens that our | ||
488 | * right shift value is equal to the peripheral width value. | ||
489 | */ | ||
490 | p_dma_ch->shift = p_init->pwidth; | ||
491 | |||
492 | /* | ||
493 | * Save the control word for easy access. | ||
494 | */ | ||
495 | p_dma_ch->control = control; | ||
496 | |||
497 | mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */ | ||
498 | return DMA_STATUS_GOOD; | ||
499 | } | ||
500 | |||
501 | /* | ||
502 | * This function returns the channel configuration. | ||
503 | */ | ||
504 | int | ||
505 | ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch) | ||
506 | { | ||
507 | unsigned int polarity; | ||
508 | unsigned int control; | ||
509 | |||
510 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
511 | printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr); | ||
512 | return DMA_STATUS_BAD_CHANNEL; | ||
513 | } | ||
514 | |||
515 | memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t)); | ||
516 | |||
517 | #if DCRN_POL > 0 | ||
518 | polarity = mfdcr(DCRN_POL); | ||
519 | #else | ||
520 | polarity = 0; | ||
521 | #endif | ||
522 | |||
523 | p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr); | ||
524 | control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); | ||
525 | |||
526 | p_dma_ch->cp = GET_DMA_PRIORITY(control); | ||
527 | p_dma_ch->pwidth = GET_DMA_PW(control); | ||
528 | p_dma_ch->psc = GET_DMA_PSC(control); | ||
529 | p_dma_ch->pwc = GET_DMA_PWC(control); | ||
530 | p_dma_ch->phc = GET_DMA_PHC(control); | ||
531 | p_dma_ch->ce = GET_DMA_CE_ENABLE(control); | ||
532 | p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control); | ||
533 | p_dma_ch->shift = GET_DMA_PW(control); | ||
534 | |||
535 | #ifdef CONFIG_PPC4xx_EDMA | ||
536 | p_dma_ch->pf = GET_DMA_PREFETCH(control); | ||
537 | #else | ||
538 | p_dma_ch->ch_enable = GET_DMA_CH(control); | ||
539 | p_dma_ch->ece_enable = GET_DMA_ECE(control); | ||
540 | p_dma_ch->tcd_disable = GET_DMA_TCD(control); | ||
541 | #endif | ||
542 | return DMA_STATUS_GOOD; | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * Sets the priority for the DMA channel dmanr. | ||
547 | * Since this is setup by the hardware init function, this function | ||
548 | * can be used to dynamically change the priority of a channel. | ||
549 | * | ||
550 | * Acceptable priorities: | ||
551 | * | ||
552 | * PRIORITY_LOW | ||
553 | * PRIORITY_MID_LOW | ||
554 | * PRIORITY_MID_HIGH | ||
555 | * PRIORITY_HIGH | ||
556 | * | ||
557 | */ | ||
558 | int | ||
559 | ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority) | ||
560 | { | ||
561 | unsigned int control; | ||
562 | |||
563 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
564 | printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr); | ||
565 | return DMA_STATUS_BAD_CHANNEL; | ||
566 | } | ||
567 | |||
568 | if ((priority != PRIORITY_LOW) && | ||
569 | (priority != PRIORITY_MID_LOW) && | ||
570 | (priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) { | ||
571 | printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority); | ||
572 | } | ||
573 | |||
574 | control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); | ||
575 | control |= SET_DMA_PRIORITY(priority); | ||
576 | mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control); | ||
577 | |||
578 | return DMA_STATUS_GOOD; | ||
579 | } | ||
580 | |||
581 | /* | ||
582 | * Returns the width of the peripheral attached to this channel. This assumes | ||
583 | * that someone who knows the hardware configuration, boot code or some other | ||
584 | * init code, already set the width. | ||
585 | * | ||
586 | * The return value is one of: | ||
587 | * PW_8 | ||
588 | * PW_16 | ||
589 | * PW_32 | ||
590 | * PW_64 | ||
591 | * | ||
592 | * The function returns 0 on error. | ||
593 | */ | ||
594 | unsigned int | ||
595 | ppc4xx_get_peripheral_width(unsigned int dmanr) | ||
596 | { | ||
597 | unsigned int control; | ||
598 | |||
599 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
600 | printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr); | ||
601 | return DMA_STATUS_BAD_CHANNEL; | ||
602 | } | ||
603 | |||
604 | control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8)); | ||
605 | |||
606 | return (GET_DMA_PW(control)); | ||
607 | } | ||
608 | |||
609 | /* | ||
610 | * Clears the channel status bits | ||
611 | */ | ||
612 | int | ||
613 | ppc4xx_clr_dma_status(unsigned int dmanr) | ||
614 | { | ||
615 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
616 | printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr); | ||
617 | return DMA_STATUS_BAD_CHANNEL; | ||
618 | } | ||
619 | mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr); | ||
620 | return DMA_STATUS_GOOD; | ||
621 | } | ||
622 | |||
623 | /* | ||
624 | * Enables the burst on the channel (BTEN bit in the control/count register) | ||
625 | * Note: | ||
626 | * For scatter/gather dma, this function MUST be called before the | ||
627 | * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the | ||
628 | * sgl list and used as each sgl element is added. | ||
629 | */ | ||
630 | int | ||
631 | ppc4xx_enable_burst(unsigned int dmanr) | ||
632 | { | ||
633 | unsigned int ctc; | ||
634 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
635 | printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr); | ||
636 | return DMA_STATUS_BAD_CHANNEL; | ||
637 | } | ||
638 | ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN; | ||
639 | mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc); | ||
640 | return DMA_STATUS_GOOD; | ||
641 | } | ||
642 | /* | ||
643 | * Disables the burst on the channel (BTEN bit in the control/count register) | ||
644 | * Note: | ||
645 | * For scatter/gather dma, this function MUST be called before the | ||
646 | * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the | ||
647 | * sgl list and used as each sgl element is added. | ||
648 | */ | ||
649 | int | ||
650 | ppc4xx_disable_burst(unsigned int dmanr) | ||
651 | { | ||
652 | unsigned int ctc; | ||
653 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
654 | printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr); | ||
655 | return DMA_STATUS_BAD_CHANNEL; | ||
656 | } | ||
657 | ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN; | ||
658 | mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc); | ||
659 | return DMA_STATUS_GOOD; | ||
660 | } | ||
661 | /* | ||
662 | * Sets the burst size (number of peripheral widths) for the channel | ||
663 | * (BSIZ bits in the control/count register)) | ||
664 | * must be one of: | ||
665 | * DMA_CTC_BSIZ_2 | ||
666 | * DMA_CTC_BSIZ_4 | ||
667 | * DMA_CTC_BSIZ_8 | ||
668 | * DMA_CTC_BSIZ_16 | ||
669 | * Note: | ||
670 | * For scatter/gather dma, this function MUST be called before the | ||
671 | * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the | ||
672 | * sgl list and used as each sgl element is added. | ||
673 | */ | ||
674 | int | ||
675 | ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize) | ||
676 | { | ||
677 | unsigned int ctc; | ||
678 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
679 | printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr); | ||
680 | return DMA_STATUS_BAD_CHANNEL; | ||
681 | } | ||
682 | ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK; | ||
683 | ctc |= (bsize & DMA_CTC_BSIZ_MSK); | ||
684 | mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc); | ||
685 | return DMA_STATUS_GOOD; | ||
686 | } | ||
687 | |||
688 | EXPORT_SYMBOL(ppc4xx_init_dma_channel); | ||
689 | EXPORT_SYMBOL(ppc4xx_get_channel_config); | ||
690 | EXPORT_SYMBOL(ppc4xx_set_channel_priority); | ||
691 | EXPORT_SYMBOL(ppc4xx_get_peripheral_width); | ||
692 | EXPORT_SYMBOL(dma_channels); | ||
693 | EXPORT_SYMBOL(ppc4xx_set_src_addr); | ||
694 | EXPORT_SYMBOL(ppc4xx_set_dst_addr); | ||
695 | EXPORT_SYMBOL(ppc4xx_set_dma_addr); | ||
696 | EXPORT_SYMBOL(ppc4xx_set_dma_addr2); | ||
697 | EXPORT_SYMBOL(ppc4xx_enable_dma); | ||
698 | EXPORT_SYMBOL(ppc4xx_disable_dma); | ||
699 | EXPORT_SYMBOL(ppc4xx_set_dma_mode); | ||
700 | EXPORT_SYMBOL(ppc4xx_set_dma_count); | ||
701 | EXPORT_SYMBOL(ppc4xx_get_dma_residue); | ||
702 | EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt); | ||
703 | EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt); | ||
704 | EXPORT_SYMBOL(ppc4xx_get_dma_status); | ||
705 | EXPORT_SYMBOL(ppc4xx_clr_dma_status); | ||
706 | EXPORT_SYMBOL(ppc4xx_enable_burst); | ||
707 | EXPORT_SYMBOL(ppc4xx_disable_burst); | ||
708 | EXPORT_SYMBOL(ppc4xx_set_burst_size); | ||
diff --git a/arch/ppc/syslib/ppc4xx_kgdb.c b/arch/ppc/syslib/ppc4xx_kgdb.c new file mode 100644 index 000000000000..fe8668bf8137 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_kgdb.c | |||
@@ -0,0 +1,124 @@ | |||
1 | #include <linux/config.h> | ||
2 | #include <linux/types.h> | ||
3 | #include <asm/ibm4xx.h> | ||
4 | #include <linux/kernel.h> | ||
5 | |||
6 | |||
7 | |||
8 | #define LSR_DR 0x01 /* Data ready */ | ||
9 | #define LSR_OE 0x02 /* Overrun */ | ||
10 | #define LSR_PE 0x04 /* Parity error */ | ||
11 | #define LSR_FE 0x08 /* Framing error */ | ||
12 | #define LSR_BI 0x10 /* Break */ | ||
13 | #define LSR_THRE 0x20 /* Xmit holding register empty */ | ||
14 | #define LSR_TEMT 0x40 /* Xmitter empty */ | ||
15 | #define LSR_ERR 0x80 /* Error */ | ||
16 | |||
17 | #include <platforms/4xx/ibm_ocp.h> | ||
18 | |||
19 | extern struct NS16550* COM_PORTS[]; | ||
20 | #ifndef NULL | ||
21 | #define NULL 0x00 | ||
22 | #endif | ||
23 | |||
24 | static volatile struct NS16550 *kgdb_debugport = NULL; | ||
25 | |||
26 | volatile struct NS16550 * | ||
27 | NS16550_init(int chan) | ||
28 | { | ||
29 | volatile struct NS16550 *com_port; | ||
30 | int quot; | ||
31 | #ifdef BASE_BAUD | ||
32 | quot = BASE_BAUD / 9600; | ||
33 | #else | ||
34 | quot = 0x000c; /* 0xc = 9600 baud (on a pc) */ | ||
35 | #endif | ||
36 | |||
37 | com_port = (struct NS16550 *) COM_PORTS[chan]; | ||
38 | |||
39 | com_port->lcr = 0x00; | ||
40 | com_port->ier = 0xFF; | ||
41 | com_port->ier = 0x00; | ||
42 | com_port->lcr = com_port->lcr | 0x80; /* Access baud rate */ | ||
43 | com_port->dll = ( quot & 0x00ff ); /* 0xc = 9600 baud */ | ||
44 | com_port->dlm = ( quot & 0xff00 ) >> 8; | ||
45 | com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */ | ||
46 | com_port->mcr = 0x00; /* RTS/DTR */ | ||
47 | com_port->fcr = 0x07; /* Clear & enable FIFOs */ | ||
48 | |||
49 | return( com_port ); | ||
50 | } | ||
51 | |||
52 | |||
53 | void | ||
54 | NS16550_putc(volatile struct NS16550 *com_port, unsigned char c) | ||
55 | { | ||
56 | while ((com_port->lsr & LSR_THRE) == 0) | ||
57 | ; | ||
58 | com_port->thr = c; | ||
59 | return; | ||
60 | } | ||
61 | |||
62 | unsigned char | ||
63 | NS16550_getc(volatile struct NS16550 *com_port) | ||
64 | { | ||
65 | while ((com_port->lsr & LSR_DR) == 0) | ||
66 | ; | ||
67 | return (com_port->rbr); | ||
68 | } | ||
69 | |||
70 | unsigned char | ||
71 | NS16550_tstc(volatile struct NS16550 *com_port) | ||
72 | { | ||
73 | return ((com_port->lsr & LSR_DR) != 0); | ||
74 | } | ||
75 | |||
76 | |||
77 | #if defined(CONFIG_KGDB_TTYS0) | ||
78 | #define KGDB_PORT 0 | ||
79 | #elif defined(CONFIG_KGDB_TTYS1) | ||
80 | #define KGDB_PORT 1 | ||
81 | #elif defined(CONFIG_KGDB_TTYS2) | ||
82 | #define KGDB_PORT 2 | ||
83 | #elif defined(CONFIG_KGDB_TTYS3) | ||
84 | #define KGDB_PORT 3 | ||
85 | #else | ||
86 | #error "invalid kgdb_tty port" | ||
87 | #endif | ||
88 | |||
89 | void putDebugChar( unsigned char c ) | ||
90 | { | ||
91 | if ( kgdb_debugport == NULL ) | ||
92 | kgdb_debugport = NS16550_init(KGDB_PORT); | ||
93 | NS16550_putc( kgdb_debugport, c ); | ||
94 | } | ||
95 | |||
96 | int getDebugChar( void ) | ||
97 | { | ||
98 | if (kgdb_debugport == NULL) | ||
99 | kgdb_debugport = NS16550_init(KGDB_PORT); | ||
100 | |||
101 | return(NS16550_getc(kgdb_debugport)); | ||
102 | } | ||
103 | |||
104 | void kgdb_interruptible(int enable) | ||
105 | { | ||
106 | return; | ||
107 | } | ||
108 | |||
109 | void putDebugString(char* str) | ||
110 | { | ||
111 | while (*str != '\0') { | ||
112 | putDebugChar(*str); | ||
113 | str++; | ||
114 | } | ||
115 | putDebugChar('\r'); | ||
116 | return; | ||
117 | } | ||
118 | |||
119 | void | ||
120 | kgdb_map_scc(void) | ||
121 | { | ||
122 | printk("kgdb init \n"); | ||
123 | kgdb_debugport = NS16550_init(KGDB_PORT); | ||
124 | } | ||
diff --git a/arch/ppc/syslib/ppc4xx_pic.c b/arch/ppc/syslib/ppc4xx_pic.c new file mode 100644 index 000000000000..08f06dd17e7b --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_pic.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ppc4xx_pic.c | ||
3 | * | ||
4 | * Interrupt controller driver for PowerPC 4xx-based processors. | ||
5 | * | ||
6 | * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net> | ||
7 | * Copyright (c) 2004, 2005 Zultys Technologies | ||
8 | * | ||
9 | * Based on original code by | ||
10 | * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu> | ||
11 | * Armin Custer <akuster@mvista.com> | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify it | ||
14 | * under the terms of the GNU General Public License as published by the | ||
15 | * Free Software Foundation; either version 2 of the License, or (at your | ||
16 | * option) any later version. | ||
17 | */ | ||
18 | #include <linux/config.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/sched.h> | ||
21 | #include <linux/signal.h> | ||
22 | #include <linux/stddef.h> | ||
23 | |||
24 | #include <asm/processor.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/irq.h> | ||
27 | #include <asm/ppc4xx_pic.h> | ||
28 | |||
29 | /* See comment in include/arch-ppc/ppc4xx_pic.h | ||
30 | * for more info about these two variables | ||
31 | */ | ||
32 | extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS] | ||
33 | __attribute__ ((weak)); | ||
34 | extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak)); | ||
35 | |||
36 | #define IRQ_MASK_UIC0(irq) (1 << (31 - (irq))) | ||
37 | #define IRQ_MASK_UICx(irq) (1 << (31 - ((irq) & 0x1f))) | ||
38 | #define IRQ_MASK_UIC1(irq) IRQ_MASK_UICx(irq) | ||
39 | #define IRQ_MASK_UIC2(irq) IRQ_MASK_UICx(irq) | ||
40 | |||
41 | #define UIC_HANDLERS(n) \ | ||
42 | static void ppc4xx_uic##n##_enable(unsigned int irq) \ | ||
43 | { \ | ||
44 | ppc_cached_irq_mask[n] |= IRQ_MASK_UIC##n(irq); \ | ||
45 | mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \ | ||
46 | } \ | ||
47 | \ | ||
48 | static void ppc4xx_uic##n##_disable(unsigned int irq) \ | ||
49 | { \ | ||
50 | ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq); \ | ||
51 | mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \ | ||
52 | ACK_UIC##n##_PARENT \ | ||
53 | } \ | ||
54 | \ | ||
55 | static void ppc4xx_uic##n##_ack(unsigned int irq) \ | ||
56 | { \ | ||
57 | u32 mask = IRQ_MASK_UIC##n(irq); \ | ||
58 | ppc_cached_irq_mask[n] &= ~mask; \ | ||
59 | mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \ | ||
60 | mtdcr(DCRN_UIC_SR(UIC##n), mask); \ | ||
61 | ACK_UIC##n##_PARENT \ | ||
62 | } \ | ||
63 | \ | ||
64 | static void ppc4xx_uic##n##_end(unsigned int irq) \ | ||
65 | { \ | ||
66 | unsigned int status = irq_desc[irq].status; \ | ||
67 | u32 mask = IRQ_MASK_UIC##n(irq); \ | ||
68 | if (status & IRQ_LEVEL) { \ | ||
69 | mtdcr(DCRN_UIC_SR(UIC##n), mask); \ | ||
70 | ACK_UIC##n##_PARENT \ | ||
71 | } \ | ||
72 | if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { \ | ||
73 | ppc_cached_irq_mask[n] |= mask; \ | ||
74 | mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \ | ||
75 | } \ | ||
76 | } | ||
77 | |||
78 | #define DECLARE_UIC(n) \ | ||
79 | { \ | ||
80 | .typename = "UIC"#n, \ | ||
81 | .enable = ppc4xx_uic##n##_enable, \ | ||
82 | .disable = ppc4xx_uic##n##_disable, \ | ||
83 | .ack = ppc4xx_uic##n##_ack, \ | ||
84 | .end = ppc4xx_uic##n##_end, \ | ||
85 | } \ | ||
86 | |||
87 | #if NR_UICS == 3 | ||
88 | #define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC); | ||
89 | #define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC); | ||
90 | #define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC); | ||
91 | UIC_HANDLERS(0); | ||
92 | UIC_HANDLERS(1); | ||
93 | UIC_HANDLERS(2); | ||
94 | |||
95 | static int ppc4xx_pic_get_irq(struct pt_regs *regs) | ||
96 | { | ||
97 | u32 uicb = mfdcr(DCRN_UIC_MSR(UICB)); | ||
98 | if (uicb & UICB_UIC0NC) | ||
99 | return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0))); | ||
100 | else if (uicb & UICB_UIC1NC) | ||
101 | return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1))); | ||
102 | else if (uicb & UICB_UIC2NC) | ||
103 | return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2))); | ||
104 | else | ||
105 | return -1; | ||
106 | } | ||
107 | |||
108 | static void __init ppc4xx_pic_impl_init(void) | ||
109 | { | ||
110 | /* Configure Base UIC */ | ||
111 | mtdcr(DCRN_UIC_CR(UICB), 0); | ||
112 | mtdcr(DCRN_UIC_TR(UICB), 0); | ||
113 | mtdcr(DCRN_UIC_PR(UICB), 0xffffffff); | ||
114 | mtdcr(DCRN_UIC_SR(UICB), 0xffffffff); | ||
115 | mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC); | ||
116 | } | ||
117 | |||
118 | #elif NR_UICS == 2 | ||
119 | #define ACK_UIC0_PARENT | ||
120 | #define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC); | ||
121 | UIC_HANDLERS(0); | ||
122 | UIC_HANDLERS(1); | ||
123 | |||
124 | static int ppc4xx_pic_get_irq(struct pt_regs *regs) | ||
125 | { | ||
126 | u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0)); | ||
127 | if (uic0 & UIC0_UIC1NC) | ||
128 | return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1))); | ||
129 | else | ||
130 | return uic0 ? 32 - ffs(uic0) : -1; | ||
131 | } | ||
132 | |||
133 | static void __init ppc4xx_pic_impl_init(void) | ||
134 | { | ||
135 | /* Enable cascade interrupt in UIC0 */ | ||
136 | ppc_cached_irq_mask[0] |= UIC0_UIC1NC; | ||
137 | mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC); | ||
138 | mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]); | ||
139 | } | ||
140 | |||
141 | #elif NR_UICS == 1 | ||
142 | #define ACK_UIC0_PARENT | ||
143 | UIC_HANDLERS(0); | ||
144 | |||
145 | static int ppc4xx_pic_get_irq(struct pt_regs *regs) | ||
146 | { | ||
147 | u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0)); | ||
148 | return uic0 ? 32 - ffs(uic0) : -1; | ||
149 | } | ||
150 | |||
151 | static inline void ppc4xx_pic_impl_init(void) | ||
152 | { | ||
153 | } | ||
154 | #endif | ||
155 | |||
156 | static struct ppc4xx_uic_impl { | ||
157 | struct hw_interrupt_type decl; | ||
158 | int base; /* Base DCR number */ | ||
159 | } __uic[] = { | ||
160 | { .decl = DECLARE_UIC(0), .base = UIC0 }, | ||
161 | #if NR_UICS > 1 | ||
162 | { .decl = DECLARE_UIC(1), .base = UIC1 }, | ||
163 | #if NR_UICS > 2 | ||
164 | { .decl = DECLARE_UIC(2), .base = UIC2 }, | ||
165 | #endif | ||
166 | #endif | ||
167 | }; | ||
168 | |||
169 | static inline int is_level_sensitive(int irq) | ||
170 | { | ||
171 | u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base)); | ||
172 | return (tr & IRQ_MASK_UICx(irq)) == 0; | ||
173 | } | ||
174 | |||
175 | void __init ppc4xx_pic_init(void) | ||
176 | { | ||
177 | int i; | ||
178 | unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg; | ||
179 | |||
180 | for (i = 0; i < NR_UICS; ++i) { | ||
181 | int base = __uic[i].base; | ||
182 | |||
183 | /* Disable everything by default */ | ||
184 | ppc_cached_irq_mask[i] = 0; | ||
185 | mtdcr(DCRN_UIC_ER(base), 0); | ||
186 | |||
187 | /* We don't use critical interrupts */ | ||
188 | mtdcr(DCRN_UIC_CR(base), 0); | ||
189 | |||
190 | /* Configure polarity and triggering */ | ||
191 | if (ppc4xx_core_uic_cfg) { | ||
192 | struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i; | ||
193 | u32 mask = p->ext_irq_mask; | ||
194 | u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask; | ||
195 | u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask; | ||
196 | |||
197 | /* "Fixed" interrupts (on-chip devices) */ | ||
198 | pr |= p->polarity & ~mask; | ||
199 | tr |= p->triggering & ~mask; | ||
200 | |||
201 | /* Merge external IRQs settings if board port | ||
202 | * provided them | ||
203 | */ | ||
204 | if (eirqs && mask) { | ||
205 | pr &= ~mask; | ||
206 | tr &= ~mask; | ||
207 | while (mask) { | ||
208 | /* Extract current external IRQ mask */ | ||
209 | u32 eirq_mask = 1 << __ilog2(mask); | ||
210 | |||
211 | if (!(*eirqs & IRQ_SENSE_LEVEL)) | ||
212 | tr |= eirq_mask; | ||
213 | |||
214 | if (*eirqs & IRQ_POLARITY_POSITIVE) | ||
215 | pr |= eirq_mask; | ||
216 | |||
217 | mask &= ~eirq_mask; | ||
218 | ++eirqs; | ||
219 | } | ||
220 | } | ||
221 | mtdcr(DCRN_UIC_PR(base), pr); | ||
222 | mtdcr(DCRN_UIC_TR(base), tr); | ||
223 | } | ||
224 | |||
225 | /* ACK any pending interrupts to prevent false | ||
226 | * triggering after first enable | ||
227 | */ | ||
228 | mtdcr(DCRN_UIC_SR(base), 0xffffffff); | ||
229 | } | ||
230 | |||
231 | /* Perform optional implementation specific setup | ||
232 | * (e.g. enable cascade interrupts for multi-UIC configurations) | ||
233 | */ | ||
234 | ppc4xx_pic_impl_init(); | ||
235 | |||
236 | /* Attach low-level handlers */ | ||
237 | for (i = 0; i < (NR_UICS << 5); ++i) { | ||
238 | irq_desc[i].handler = &__uic[i >> 5].decl; | ||
239 | if (is_level_sensitive(i)) | ||
240 | irq_desc[i].status |= IRQ_LEVEL; | ||
241 | } | ||
242 | |||
243 | ppc_md.get_irq = ppc4xx_pic_get_irq; | ||
244 | } | ||
diff --git a/arch/ppc/syslib/ppc4xx_pm.c b/arch/ppc/syslib/ppc4xx_pm.c new file mode 100644 index 000000000000..60a479204885 --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_pm.c | |||
@@ -0,0 +1,47 @@ | |||
1 | /* | ||
2 | * Author: Armin Kuster <akuster@mvista.com> | ||
3 | * | ||
4 | * 2002 (c) MontaVista, Software, Inc. This file is licensed under | ||
5 | * the terms of the GNU General Public License version 2. This program | ||
6 | * is licensed "as is" without any warranty of any kind, whether express | ||
7 | * or implied. | ||
8 | * | ||
9 | * This an attempt to get Power Management going for the IBM 4xx processor. | ||
10 | * This was derived from the ppc4xx._setup.c file | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | #include <linux/init.h> | ||
15 | |||
16 | #include <asm/ibm4xx.h> | ||
17 | |||
18 | void __init | ||
19 | ppc4xx_pm_init(void) | ||
20 | { | ||
21 | |||
22 | unsigned int value = 0; | ||
23 | |||
24 | /* turn off unused hardware to save power */ | ||
25 | #ifdef CONFIG_405GP | ||
26 | value |= CPM_DCP; /* CodePack */ | ||
27 | #endif | ||
28 | |||
29 | #if !defined(CONFIG_IBM_OCP_GPIO) | ||
30 | value |= CPM_GPIO0; | ||
31 | #endif | ||
32 | |||
33 | #if !defined(CONFIG_PPC405_I2C_ADAP) | ||
34 | value |= CPM_IIC0; | ||
35 | #ifdef CONFIG_STB03xxx | ||
36 | value |= CPM_IIC1; | ||
37 | #endif | ||
38 | #endif | ||
39 | |||
40 | |||
41 | #if !defined(CONFIG_405_DMA) | ||
42 | value |= CPM_DMA; | ||
43 | #endif | ||
44 | |||
45 | mtdcr(DCRN_CPMFR, value); | ||
46 | |||
47 | } | ||
diff --git a/arch/ppc/syslib/ppc4xx_setup.c b/arch/ppc/syslib/ppc4xx_setup.c new file mode 100644 index 000000000000..e170aebeb69b --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_setup.c | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * | ||
3 | * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu> | ||
4 | * | ||
5 | * Copyright 2000-2001 MontaVista Software Inc. | ||
6 | * Completed implementation. | ||
7 | * Author: MontaVista Software, Inc. <source@mvista.com> | ||
8 | * Frank Rowand <frank_rowand@mvista.com> | ||
9 | * Debbie Chu <debbie_chu@mvista.com> | ||
10 | * Further modifications by Armin Kuster | ||
11 | * | ||
12 | * Module name: ppc4xx_setup.c | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/init.h> | ||
18 | #include <linux/smp.h> | ||
19 | #include <linux/threads.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/irq.h> | ||
22 | #include <linux/reboot.h> | ||
23 | #include <linux/param.h> | ||
24 | #include <linux/string.h> | ||
25 | #include <linux/initrd.h> | ||
26 | #include <linux/pci.h> | ||
27 | #include <linux/rtc.h> | ||
28 | #include <linux/console.h> | ||
29 | #include <linux/ide.h> | ||
30 | #include <linux/serial_reg.h> | ||
31 | #include <linux/seq_file.h> | ||
32 | |||
33 | #include <asm/system.h> | ||
34 | #include <asm/processor.h> | ||
35 | #include <asm/machdep.h> | ||
36 | #include <asm/page.h> | ||
37 | #include <asm/kgdb.h> | ||
38 | #include <asm/ibm4xx.h> | ||
39 | #include <asm/time.h> | ||
40 | #include <asm/todc.h> | ||
41 | #include <asm/ppc4xx_pic.h> | ||
42 | #include <asm/pci-bridge.h> | ||
43 | #include <asm/bootinfo.h> | ||
44 | |||
45 | #include <syslib/gen550.h> | ||
46 | |||
47 | /* Function Prototypes */ | ||
48 | extern void abort(void); | ||
49 | extern void ppc4xx_find_bridges(void); | ||
50 | |||
51 | extern void ppc4xx_wdt_heartbeat(void); | ||
52 | extern int wdt_enable; | ||
53 | extern unsigned long wdt_period; | ||
54 | |||
55 | /* Global Variables */ | ||
56 | bd_t __res; | ||
57 | |||
58 | void __init | ||
59 | ppc4xx_setup_arch(void) | ||
60 | { | ||
61 | #if !defined(CONFIG_BDI_SWITCH) | ||
62 | /* | ||
63 | * The Abatron BDI JTAG debugger does not tolerate others | ||
64 | * mucking with the debug registers. | ||
65 | */ | ||
66 | mtspr(SPRN_DBCR0, (DBCR0_IDM)); | ||
67 | mtspr(SPRN_DBSR, 0xffffffff); | ||
68 | #endif | ||
69 | |||
70 | /* Setup PCI host bridges */ | ||
71 | #ifdef CONFIG_PCI | ||
72 | ppc4xx_find_bridges(); | ||
73 | #endif | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * This routine pretty-prints the platform's internal CPU clock | ||
78 | * frequencies into the buffer for usage in /proc/cpuinfo. | ||
79 | */ | ||
80 | |||
81 | static int | ||
82 | ppc4xx_show_percpuinfo(struct seq_file *m, int i) | ||
83 | { | ||
84 | seq_printf(m, "clock\t\t: %ldMHz\n", (long)__res.bi_intfreq / 1000000); | ||
85 | |||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * This routine pretty-prints the platform's internal bus clock | ||
91 | * frequencies into the buffer for usage in /proc/cpuinfo. | ||
92 | */ | ||
93 | static int | ||
94 | ppc4xx_show_cpuinfo(struct seq_file *m) | ||
95 | { | ||
96 | bd_t *bip = &__res; | ||
97 | |||
98 | seq_printf(m, "machine\t\t: %s\n", PPC4xx_MACHINE_NAME); | ||
99 | seq_printf(m, "plb bus clock\t: %ldMHz\n", | ||
100 | (long) bip->bi_busfreq / 1000000); | ||
101 | #ifdef CONFIG_PCI | ||
102 | seq_printf(m, "pci bus clock\t: %dMHz\n", | ||
103 | bip->bi_pci_busfreq / 1000000); | ||
104 | #endif | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * Return the virtual address representing the top of physical RAM. | ||
111 | */ | ||
112 | static unsigned long __init | ||
113 | ppc4xx_find_end_of_memory(void) | ||
114 | { | ||
115 | return ((unsigned long) __res.bi_memsize); | ||
116 | } | ||
117 | |||
118 | void __init | ||
119 | ppc4xx_map_io(void) | ||
120 | { | ||
121 | io_block_mapping(PPC4xx_ONB_IO_VADDR, | ||
122 | PPC4xx_ONB_IO_PADDR, PPC4xx_ONB_IO_SIZE, _PAGE_IO); | ||
123 | #ifdef CONFIG_PCI | ||
124 | io_block_mapping(PPC4xx_PCI_IO_VADDR, | ||
125 | PPC4xx_PCI_IO_PADDR, PPC4xx_PCI_IO_SIZE, _PAGE_IO); | ||
126 | io_block_mapping(PPC4xx_PCI_CFG_VADDR, | ||
127 | PPC4xx_PCI_CFG_PADDR, PPC4xx_PCI_CFG_SIZE, _PAGE_IO); | ||
128 | io_block_mapping(PPC4xx_PCI_LCFG_VADDR, | ||
129 | PPC4xx_PCI_LCFG_PADDR, PPC4xx_PCI_LCFG_SIZE, _PAGE_IO); | ||
130 | #endif | ||
131 | } | ||
132 | |||
133 | void __init | ||
134 | ppc4xx_init_IRQ(void) | ||
135 | { | ||
136 | ppc4xx_pic_init(); | ||
137 | } | ||
138 | |||
139 | static void | ||
140 | ppc4xx_restart(char *cmd) | ||
141 | { | ||
142 | printk("%s\n", cmd); | ||
143 | abort(); | ||
144 | } | ||
145 | |||
146 | static void | ||
147 | ppc4xx_power_off(void) | ||
148 | { | ||
149 | printk("System Halted\n"); | ||
150 | local_irq_disable(); | ||
151 | while (1) ; | ||
152 | } | ||
153 | |||
154 | static void | ||
155 | ppc4xx_halt(void) | ||
156 | { | ||
157 | printk("System Halted\n"); | ||
158 | local_irq_disable(); | ||
159 | while (1) ; | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * This routine retrieves the internal processor frequency from the board | ||
164 | * information structure, sets up the kernel timer decrementer based on | ||
165 | * that value, enables the 4xx programmable interval timer (PIT) and sets | ||
166 | * it up for auto-reload. | ||
167 | */ | ||
168 | static void __init | ||
169 | ppc4xx_calibrate_decr(void) | ||
170 | { | ||
171 | unsigned int freq; | ||
172 | bd_t *bip = &__res; | ||
173 | |||
174 | #if defined(CONFIG_WALNUT) || defined(CONFIG_ASH) || defined(CONFIG_SYCAMORE) | ||
175 | /* Walnut boot rom sets DCR CHCR1 (aka CPC0_CR1) bit CETE to 1 */ | ||
176 | mtdcr(DCRN_CHCR1, mfdcr(DCRN_CHCR1) & ~CHR1_CETE); | ||
177 | #endif | ||
178 | freq = bip->bi_tbfreq; | ||
179 | tb_ticks_per_jiffy = freq / HZ; | ||
180 | tb_to_us = mulhwu_scale_factor(freq, 1000000); | ||
181 | |||
182 | /* Set the time base to zero. | ||
183 | ** At 200 Mhz, time base will rollover in ~2925 years. | ||
184 | */ | ||
185 | |||
186 | mtspr(SPRN_TBWL, 0); | ||
187 | mtspr(SPRN_TBWU, 0); | ||
188 | |||
189 | /* Clear any pending timer interrupts */ | ||
190 | |||
191 | mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS); | ||
192 | mtspr(SPRN_TCR, TCR_PIE | TCR_ARE); | ||
193 | |||
194 | /* Set the PIT reload value and just let it run. */ | ||
195 | mtspr(SPRN_PIT, tb_ticks_per_jiffy); | ||
196 | } | ||
197 | |||
198 | /* | ||
199 | * IDE stuff. | ||
200 | * should be generic for every IDE PCI chipset | ||
201 | */ | ||
202 | #if defined(CONFIG_PCI) && defined(CONFIG_IDE) | ||
203 | static void | ||
204 | ppc4xx_ide_init_hwif_ports(hw_regs_t * hw, unsigned long data_port, | ||
205 | unsigned long ctrl_port, int *irq) | ||
206 | { | ||
207 | int i; | ||
208 | |||
209 | for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i) | ||
210 | hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET; | ||
211 | |||
212 | hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; | ||
213 | } | ||
214 | #endif /* defined(CONFIG_PCI) && defined(CONFIG_IDE) */ | ||
215 | |||
216 | TODC_ALLOC(); | ||
217 | |||
218 | /* | ||
219 | * Input(s): | ||
220 | * r3 - Optional pointer to a board information structure. | ||
221 | * r4 - Optional pointer to the physical starting address of the init RAM | ||
222 | * disk. | ||
223 | * r5 - Optional pointer to the physical ending address of the init RAM | ||
224 | * disk. | ||
225 | * r6 - Optional pointer to the physical starting address of any kernel | ||
226 | * command-line parameters. | ||
227 | * r7 - Optional pointer to the physical ending address of any kernel | ||
228 | * command-line parameters. | ||
229 | */ | ||
230 | void __init | ||
231 | ppc4xx_init(unsigned long r3, unsigned long r4, unsigned long r5, | ||
232 | unsigned long r6, unsigned long r7) | ||
233 | { | ||
234 | parse_bootinfo(find_bootinfo()); | ||
235 | |||
236 | /* | ||
237 | * If we were passed in a board information, copy it into the | ||
238 | * residual data area. | ||
239 | */ | ||
240 | if (r3) | ||
241 | __res = *(bd_t *)(r3 + KERNELBASE); | ||
242 | |||
243 | #if defined(CONFIG_BLK_DEV_INITRD) | ||
244 | /* | ||
245 | * If the init RAM disk has been configured in, and there's a valid | ||
246 | * starting address for it, set it up. | ||
247 | */ | ||
248 | if (r4) { | ||
249 | initrd_start = r4 + KERNELBASE; | ||
250 | initrd_end = r5 + KERNELBASE; | ||
251 | } | ||
252 | #endif /* CONFIG_BLK_DEV_INITRD */ | ||
253 | |||
254 | /* Copy the kernel command line arguments to a safe place. */ | ||
255 | |||
256 | if (r6) { | ||
257 | *(char *) (r7 + KERNELBASE) = 0; | ||
258 | strcpy(cmd_line, (char *) (r6 + KERNELBASE)); | ||
259 | } | ||
260 | #if defined(CONFIG_PPC405_WDT) | ||
261 | /* Look for wdt= option on command line */ | ||
262 | if (strstr(cmd_line, "wdt=")) { | ||
263 | int valid_wdt = 0; | ||
264 | char *p, *q; | ||
265 | for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) { | ||
266 | q = p + 4; | ||
267 | if (p > cmd_line && p[-1] != ' ') | ||
268 | continue; | ||
269 | wdt_period = simple_strtoul(q, &q, 0); | ||
270 | valid_wdt = 1; | ||
271 | ++q; | ||
272 | } | ||
273 | wdt_enable = valid_wdt; | ||
274 | } | ||
275 | #endif | ||
276 | |||
277 | /* Initialize machine-dependent vectors */ | ||
278 | |||
279 | ppc_md.setup_arch = ppc4xx_setup_arch; | ||
280 | ppc_md.show_percpuinfo = ppc4xx_show_percpuinfo; | ||
281 | ppc_md.show_cpuinfo = ppc4xx_show_cpuinfo; | ||
282 | ppc_md.init_IRQ = ppc4xx_init_IRQ; | ||
283 | |||
284 | ppc_md.restart = ppc4xx_restart; | ||
285 | ppc_md.power_off = ppc4xx_power_off; | ||
286 | ppc_md.halt = ppc4xx_halt; | ||
287 | |||
288 | ppc_md.calibrate_decr = ppc4xx_calibrate_decr; | ||
289 | |||
290 | #ifdef CONFIG_PPC405_WDT | ||
291 | ppc_md.heartbeat = ppc4xx_wdt_heartbeat; | ||
292 | #endif | ||
293 | ppc_md.heartbeat_count = 0; | ||
294 | |||
295 | ppc_md.find_end_of_memory = ppc4xx_find_end_of_memory; | ||
296 | ppc_md.setup_io_mappings = ppc4xx_map_io; | ||
297 | |||
298 | #ifdef CONFIG_SERIAL_TEXT_DEBUG | ||
299 | ppc_md.progress = gen550_progress; | ||
300 | #endif | ||
301 | |||
302 | #if defined(CONFIG_PCI) && defined(CONFIG_IDE) | ||
303 | ppc_ide_md.ide_init_hwif = ppc4xx_ide_init_hwif_ports; | ||
304 | #endif /* defined(CONFIG_PCI) && defined(CONFIG_IDE) */ | ||
305 | } | ||
306 | |||
307 | /* Called from MachineCheckException */ | ||
308 | void platform_machine_check(struct pt_regs *regs) | ||
309 | { | ||
310 | #if defined(DCRN_PLB0_BEAR) | ||
311 | printk("PLB0: BEAR= 0x%08x ACR= 0x%08x BESR= 0x%08x\n", | ||
312 | mfdcr(DCRN_PLB0_BEAR), mfdcr(DCRN_PLB0_ACR), | ||
313 | mfdcr(DCRN_PLB0_BESR)); | ||
314 | #endif | ||
315 | #if defined(DCRN_POB0_BEAR) | ||
316 | printk("PLB0 to OPB: BEAR= 0x%08x BESR0= 0x%08x BESR1= 0x%08x\n", | ||
317 | mfdcr(DCRN_POB0_BEAR), mfdcr(DCRN_POB0_BESR0), | ||
318 | mfdcr(DCRN_POB0_BESR1)); | ||
319 | #endif | ||
320 | |||
321 | } | ||
diff --git a/arch/ppc/syslib/ppc4xx_sgdma.c b/arch/ppc/syslib/ppc4xx_sgdma.c new file mode 100644 index 000000000000..9f76e8ee39ed --- /dev/null +++ b/arch/ppc/syslib/ppc4xx_sgdma.c | |||
@@ -0,0 +1,467 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/ppc4xx_sgdma.c | ||
3 | * | ||
4 | * IBM PPC4xx DMA engine scatter/gather library | ||
5 | * | ||
6 | * Copyright 2002-2003 MontaVista Software Inc. | ||
7 | * | ||
8 | * Cleaned up and converted to new DCR access | ||
9 | * Matt Porter <mporter@kernel.crashing.org> | ||
10 | * | ||
11 | * Original code by Armin Kuster <akuster@mvista.com> | ||
12 | * and Pete Popov <ppopov@mvista.com> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or modify it | ||
15 | * under the terms of the GNU General Public License as published by the | ||
16 | * Free Software Foundation; either version 2 of the License, or (at your | ||
17 | * option) any later version. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write to the Free Software Foundation, Inc., | ||
21 | * 675 Mass Ave, Cambridge, MA 02139, USA. | ||
22 | */ | ||
23 | |||
24 | #include <linux/config.h> | ||
25 | #include <linux/kernel.h> | ||
26 | #include <linux/mm.h> | ||
27 | #include <linux/init.h> | ||
28 | #include <linux/module.h> | ||
29 | #include <linux/pci.h> | ||
30 | |||
31 | #include <asm/system.h> | ||
32 | #include <asm/io.h> | ||
33 | #include <asm/ppc4xx_dma.h> | ||
34 | |||
35 | void | ||
36 | ppc4xx_set_sg_addr(int dmanr, phys_addr_t sg_addr) | ||
37 | { | ||
38 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
39 | printk("ppc4xx_set_sg_addr: bad channel: %d\n", dmanr); | ||
40 | return; | ||
41 | } | ||
42 | |||
43 | #ifdef PPC4xx_DMA_64BIT | ||
44 | mtdcr(DCRN_ASGH0 + (dmanr * 0x8), (u32)(sg_addr >> 32)); | ||
45 | #endif | ||
46 | mtdcr(DCRN_ASG0 + (dmanr * 0x8), (u32)sg_addr); | ||
47 | } | ||
48 | |||
49 | /* | ||
50 | * Add a new sgl descriptor to the end of a scatter/gather list | ||
51 | * which was created by alloc_dma_handle(). | ||
52 | * | ||
53 | * For a memory to memory transfer, both dma addresses must be | ||
54 | * valid. For a peripheral to memory transfer, one of the addresses | ||
55 | * must be set to NULL, depending on the direction of the transfer: | ||
56 | * memory to peripheral: set dst_addr to NULL, | ||
57 | * peripheral to memory: set src_addr to NULL. | ||
58 | */ | ||
59 | int | ||
60 | ppc4xx_add_dma_sgl(sgl_handle_t handle, phys_addr_t src_addr, phys_addr_t dst_addr, | ||
61 | unsigned int count) | ||
62 | { | ||
63 | sgl_list_info_t *psgl = (sgl_list_info_t *) handle; | ||
64 | ppc_dma_ch_t *p_dma_ch; | ||
65 | |||
66 | if (!handle) { | ||
67 | printk("ppc4xx_add_dma_sgl: null handle\n"); | ||
68 | return DMA_STATUS_BAD_HANDLE; | ||
69 | } | ||
70 | |||
71 | if (psgl->dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
72 | printk("ppc4xx_add_dma_sgl: bad channel: %d\n", psgl->dmanr); | ||
73 | return DMA_STATUS_BAD_CHANNEL; | ||
74 | } | ||
75 | |||
76 | p_dma_ch = &dma_channels[psgl->dmanr]; | ||
77 | |||
78 | #ifdef DEBUG_4xxDMA | ||
79 | { | ||
80 | int error = 0; | ||
81 | unsigned int aligned = | ||
82 | (unsigned) src_addr | (unsigned) dst_addr | count; | ||
83 | switch (p_dma_ch->pwidth) { | ||
84 | case PW_8: | ||
85 | break; | ||
86 | case PW_16: | ||
87 | if (aligned & 0x1) | ||
88 | error = 1; | ||
89 | break; | ||
90 | case PW_32: | ||
91 | if (aligned & 0x3) | ||
92 | error = 1; | ||
93 | break; | ||
94 | case PW_64: | ||
95 | if (aligned & 0x7) | ||
96 | error = 1; | ||
97 | break; | ||
98 | default: | ||
99 | printk("ppc4xx_add_dma_sgl: invalid bus width: 0x%x\n", | ||
100 | p_dma_ch->pwidth); | ||
101 | return DMA_STATUS_GENERAL_ERROR; | ||
102 | } | ||
103 | if (error) | ||
104 | printk | ||
105 | ("Alignment warning: ppc4xx_add_dma_sgl src 0x%x dst 0x%x count 0x%x bus width var %d\n", | ||
106 | src_addr, dst_addr, count, p_dma_ch->pwidth); | ||
107 | |||
108 | } | ||
109 | #endif | ||
110 | |||
111 | if ((unsigned) (psgl->ptail + 1) >= ((unsigned) psgl + SGL_LIST_SIZE)) { | ||
112 | printk("sgl handle out of memory \n"); | ||
113 | return DMA_STATUS_OUT_OF_MEMORY; | ||
114 | } | ||
115 | |||
116 | if (!psgl->ptail) { | ||
117 | psgl->phead = (ppc_sgl_t *) | ||
118 | ((unsigned) psgl + sizeof (sgl_list_info_t)); | ||
119 | psgl->phead_dma = psgl->dma_addr + sizeof(sgl_list_info_t); | ||
120 | psgl->ptail = psgl->phead; | ||
121 | psgl->ptail_dma = psgl->phead_dma; | ||
122 | } else { | ||
123 | if(p_dma_ch->int_on_final_sg) { | ||
124 | /* mask out all dma interrupts, except error, on tail | ||
125 | before adding new tail. */ | ||
126 | psgl->ptail->control_count &= | ||
127 | ~(SG_TCI_ENABLE | SG_ETI_ENABLE); | ||
128 | } | ||
129 | psgl->ptail->next = psgl->ptail_dma + sizeof(ppc_sgl_t); | ||
130 | psgl->ptail++; | ||
131 | psgl->ptail_dma += sizeof(ppc_sgl_t); | ||
132 | } | ||
133 | |||
134 | psgl->ptail->control = psgl->control; | ||
135 | psgl->ptail->src_addr = src_addr; | ||
136 | psgl->ptail->dst_addr = dst_addr; | ||
137 | psgl->ptail->control_count = (count >> p_dma_ch->shift) | | ||
138 | psgl->sgl_control; | ||
139 | psgl->ptail->next = (uint32_t) NULL; | ||
140 | |||
141 | return DMA_STATUS_GOOD; | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * Enable (start) the DMA described by the sgl handle. | ||
146 | */ | ||
147 | void | ||
148 | ppc4xx_enable_dma_sgl(sgl_handle_t handle) | ||
149 | { | ||
150 | sgl_list_info_t *psgl = (sgl_list_info_t *) handle; | ||
151 | ppc_dma_ch_t *p_dma_ch; | ||
152 | uint32_t sg_command; | ||
153 | |||
154 | if (!handle) { | ||
155 | printk("ppc4xx_enable_dma_sgl: null handle\n"); | ||
156 | return; | ||
157 | } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { | ||
158 | printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n", | ||
159 | psgl->dmanr); | ||
160 | return; | ||
161 | } else if (!psgl->phead) { | ||
162 | printk("ppc4xx_enable_dma_sgl: sg list empty\n"); | ||
163 | return; | ||
164 | } | ||
165 | |||
166 | p_dma_ch = &dma_channels[psgl->dmanr]; | ||
167 | psgl->ptail->control_count &= ~SG_LINK; /* make this the last dscrptr */ | ||
168 | sg_command = mfdcr(DCRN_ASGC); | ||
169 | |||
170 | ppc4xx_set_sg_addr(psgl->dmanr, psgl->phead_dma); | ||
171 | |||
172 | sg_command |= SSG_ENABLE(psgl->dmanr); | ||
173 | |||
174 | mtdcr(DCRN_ASGC, sg_command); /* start transfer */ | ||
175 | } | ||
176 | |||
177 | /* | ||
178 | * Halt an active scatter/gather DMA operation. | ||
179 | */ | ||
180 | void | ||
181 | ppc4xx_disable_dma_sgl(sgl_handle_t handle) | ||
182 | { | ||
183 | sgl_list_info_t *psgl = (sgl_list_info_t *) handle; | ||
184 | uint32_t sg_command; | ||
185 | |||
186 | if (!handle) { | ||
187 | printk("ppc4xx_enable_dma_sgl: null handle\n"); | ||
188 | return; | ||
189 | } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { | ||
190 | printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n", | ||
191 | psgl->dmanr); | ||
192 | return; | ||
193 | } | ||
194 | |||
195 | sg_command = mfdcr(DCRN_ASGC); | ||
196 | sg_command &= ~SSG_ENABLE(psgl->dmanr); | ||
197 | mtdcr(DCRN_ASGC, sg_command); /* stop transfer */ | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * Returns number of bytes left to be transferred from the entire sgl list. | ||
202 | * *src_addr and *dst_addr get set to the source/destination address of | ||
203 | * the sgl descriptor where the DMA stopped. | ||
204 | * | ||
205 | * An sgl transfer must NOT be active when this function is called. | ||
206 | */ | ||
207 | int | ||
208 | ppc4xx_get_dma_sgl_residue(sgl_handle_t handle, phys_addr_t * src_addr, | ||
209 | phys_addr_t * dst_addr) | ||
210 | { | ||
211 | sgl_list_info_t *psgl = (sgl_list_info_t *) handle; | ||
212 | ppc_dma_ch_t *p_dma_ch; | ||
213 | ppc_sgl_t *pnext, *sgl_addr; | ||
214 | uint32_t count_left; | ||
215 | |||
216 | if (!handle) { | ||
217 | printk("ppc4xx_get_dma_sgl_residue: null handle\n"); | ||
218 | return DMA_STATUS_BAD_HANDLE; | ||
219 | } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { | ||
220 | printk("ppc4xx_get_dma_sgl_residue: bad channel in handle %d\n", | ||
221 | psgl->dmanr); | ||
222 | return DMA_STATUS_BAD_CHANNEL; | ||
223 | } | ||
224 | |||
225 | sgl_addr = (ppc_sgl_t *) __va(mfdcr(DCRN_ASG0 + (psgl->dmanr * 0x8))); | ||
226 | count_left = mfdcr(DCRN_DMACT0 + (psgl->dmanr * 0x8)) & SG_COUNT_MASK; | ||
227 | |||
228 | if (!sgl_addr) { | ||
229 | printk("ppc4xx_get_dma_sgl_residue: sgl addr register is null\n"); | ||
230 | goto error; | ||
231 | } | ||
232 | |||
233 | pnext = psgl->phead; | ||
234 | while (pnext && | ||
235 | ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE) && | ||
236 | (pnext != sgl_addr)) | ||
237 | ) { | ||
238 | pnext++; | ||
239 | } | ||
240 | |||
241 | if (pnext == sgl_addr) { /* found the sgl descriptor */ | ||
242 | |||
243 | *src_addr = pnext->src_addr; | ||
244 | *dst_addr = pnext->dst_addr; | ||
245 | |||
246 | /* | ||
247 | * Now search the remaining descriptors and add their count. | ||
248 | * We already have the remaining count from this descriptor in | ||
249 | * count_left. | ||
250 | */ | ||
251 | pnext++; | ||
252 | |||
253 | while ((pnext != psgl->ptail) && | ||
254 | ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE)) | ||
255 | ) { | ||
256 | count_left += pnext->control_count & SG_COUNT_MASK; | ||
257 | } | ||
258 | |||
259 | if (pnext != psgl->ptail) { /* should never happen */ | ||
260 | printk | ||
261 | ("ppc4xx_get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n", | ||
262 | (unsigned int) psgl->ptail, (unsigned int) handle); | ||
263 | goto error; | ||
264 | } | ||
265 | |||
266 | /* success */ | ||
267 | p_dma_ch = &dma_channels[psgl->dmanr]; | ||
268 | return (count_left << p_dma_ch->shift); /* count in bytes */ | ||
269 | |||
270 | } else { | ||
271 | /* this shouldn't happen */ | ||
272 | printk | ||
273 | ("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n", | ||
274 | (unsigned int) sgl_addr, (unsigned int) handle); | ||
275 | |||
276 | } | ||
277 | |||
278 | error: | ||
279 | *src_addr = (phys_addr_t) NULL; | ||
280 | *dst_addr = (phys_addr_t) NULL; | ||
281 | return 0; | ||
282 | } | ||
283 | |||
284 | /* | ||
285 | * Returns the address(es) of the buffer(s) contained in the head element of | ||
286 | * the scatter/gather list. The element is removed from the scatter/gather | ||
287 | * list and the next element becomes the head. | ||
288 | * | ||
289 | * This function should only be called when the DMA is not active. | ||
290 | */ | ||
291 | int | ||
292 | ppc4xx_delete_dma_sgl_element(sgl_handle_t handle, phys_addr_t * src_dma_addr, | ||
293 | phys_addr_t * dst_dma_addr) | ||
294 | { | ||
295 | sgl_list_info_t *psgl = (sgl_list_info_t *) handle; | ||
296 | |||
297 | if (!handle) { | ||
298 | printk("ppc4xx_delete_sgl_element: null handle\n"); | ||
299 | return DMA_STATUS_BAD_HANDLE; | ||
300 | } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { | ||
301 | printk("ppc4xx_delete_sgl_element: bad channel in handle %d\n", | ||
302 | psgl->dmanr); | ||
303 | return DMA_STATUS_BAD_CHANNEL; | ||
304 | } | ||
305 | |||
306 | if (!psgl->phead) { | ||
307 | printk("ppc4xx_delete_sgl_element: sgl list empty\n"); | ||
308 | *src_dma_addr = (phys_addr_t) NULL; | ||
309 | *dst_dma_addr = (phys_addr_t) NULL; | ||
310 | return DMA_STATUS_SGL_LIST_EMPTY; | ||
311 | } | ||
312 | |||
313 | *src_dma_addr = (phys_addr_t) psgl->phead->src_addr; | ||
314 | *dst_dma_addr = (phys_addr_t) psgl->phead->dst_addr; | ||
315 | |||
316 | if (psgl->phead == psgl->ptail) { | ||
317 | /* last descriptor on the list */ | ||
318 | psgl->phead = NULL; | ||
319 | psgl->ptail = NULL; | ||
320 | } else { | ||
321 | psgl->phead++; | ||
322 | psgl->phead_dma += sizeof(ppc_sgl_t); | ||
323 | } | ||
324 | |||
325 | return DMA_STATUS_GOOD; | ||
326 | } | ||
327 | |||
328 | |||
329 | /* | ||
330 | * Create a scatter/gather list handle. This is simply a structure which | ||
331 | * describes a scatter/gather list. | ||
332 | * | ||
333 | * A handle is returned in "handle" which the driver should save in order to | ||
334 | * be able to access this list later. A chunk of memory will be allocated | ||
335 | * to be used by the API for internal management purposes, including managing | ||
336 | * the sg list and allocating memory for the sgl descriptors. One page should | ||
337 | * be more than enough for that purpose. Perhaps it's a bit wasteful to use | ||
338 | * a whole page for a single sg list, but most likely there will be only one | ||
339 | * sg list per channel. | ||
340 | * | ||
341 | * Interrupt notes: | ||
342 | * Each sgl descriptor has a copy of the DMA control word which the DMA engine | ||
343 | * loads in the control register. The control word has a "global" interrupt | ||
344 | * enable bit for that channel. Interrupts are further qualified by a few bits | ||
345 | * in the sgl descriptor count register. In order to setup an sgl, we have to | ||
346 | * know ahead of time whether or not interrupts will be enabled at the completion | ||
347 | * of the transfers. Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST | ||
348 | * be called before calling alloc_dma_handle(). If the interrupt mode will never | ||
349 | * change after powerup, then enable_dma_interrupt()/disable_dma_interrupt() | ||
350 | * do not have to be called -- interrupts will be enabled or disabled based | ||
351 | * on how the channel was configured after powerup by the hw_init_dma_channel() | ||
352 | * function. Each sgl descriptor will be setup to interrupt if an error occurs; | ||
353 | * however, only the last descriptor will be setup to interrupt. Thus, an | ||
354 | * interrupt will occur (if interrupts are enabled) only after the complete | ||
355 | * sgl transfer is done. | ||
356 | */ | ||
357 | int | ||
358 | ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr) | ||
359 | { | ||
360 | sgl_list_info_t *psgl=NULL; | ||
361 | dma_addr_t dma_addr; | ||
362 | ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; | ||
363 | uint32_t sg_command; | ||
364 | uint32_t ctc_settings; | ||
365 | void *ret; | ||
366 | |||
367 | if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { | ||
368 | printk("ppc4xx_alloc_dma_handle: invalid channel 0x%x\n", dmanr); | ||
369 | return DMA_STATUS_BAD_CHANNEL; | ||
370 | } | ||
371 | |||
372 | if (!phandle) { | ||
373 | printk("ppc4xx_alloc_dma_handle: null handle pointer\n"); | ||
374 | return DMA_STATUS_NULL_POINTER; | ||
375 | } | ||
376 | |||
377 | /* Get a page of memory, which is zeroed out by consistent_alloc() */ | ||
378 | ret = dma_alloc_coherent(NULL, DMA_PPC4xx_SIZE, &dma_addr, GFP_KERNEL); | ||
379 | if (ret != NULL) { | ||
380 | memset(ret, 0, DMA_PPC4xx_SIZE); | ||
381 | psgl = (sgl_list_info_t *) ret; | ||
382 | } | ||
383 | |||
384 | if (psgl == NULL) { | ||
385 | *phandle = (sgl_handle_t) NULL; | ||
386 | return DMA_STATUS_OUT_OF_MEMORY; | ||
387 | } | ||
388 | |||
389 | psgl->dma_addr = dma_addr; | ||
390 | psgl->dmanr = dmanr; | ||
391 | |||
392 | /* | ||
393 | * Modify and save the control word. These words will be | ||
394 | * written to each sgl descriptor. The DMA engine then | ||
395 | * loads this control word into the control register | ||
396 | * every time it reads a new descriptor. | ||
397 | */ | ||
398 | psgl->control = p_dma_ch->control; | ||
399 | /* Clear all mode bits */ | ||
400 | psgl->control &= ~(DMA_TM_MASK | DMA_TD); | ||
401 | /* Save control word and mode */ | ||
402 | psgl->control |= (mode | DMA_CE_ENABLE); | ||
403 | |||
404 | /* In MM mode, we must set ETD/TCE */ | ||
405 | if (mode == DMA_MODE_MM) | ||
406 | psgl->control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE; | ||
407 | |||
408 | if (p_dma_ch->int_enable) { | ||
409 | /* Enable channel interrupt */ | ||
410 | psgl->control |= DMA_CIE_ENABLE; | ||
411 | } else { | ||
412 | psgl->control &= ~DMA_CIE_ENABLE; | ||
413 | } | ||
414 | |||
415 | sg_command = mfdcr(DCRN_ASGC); | ||
416 | sg_command |= SSG_MASK_ENABLE(dmanr); | ||
417 | |||
418 | /* Enable SGL control access */ | ||
419 | mtdcr(DCRN_ASGC, sg_command); | ||
420 | psgl->sgl_control = SG_ERI_ENABLE | SG_LINK; | ||
421 | |||
422 | /* keep control count register settings */ | ||
423 | ctc_settings = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | ||
424 | & (DMA_CTC_BSIZ_MSK | DMA_CTC_BTEN); /*burst mode settings*/ | ||
425 | psgl->sgl_control |= ctc_settings; | ||
426 | |||
427 | if (p_dma_ch->int_enable) { | ||
428 | if (p_dma_ch->tce_enable) | ||
429 | psgl->sgl_control |= SG_TCI_ENABLE; | ||
430 | else | ||
431 | psgl->sgl_control |= SG_ETI_ENABLE; | ||
432 | } | ||
433 | |||
434 | *phandle = (sgl_handle_t) psgl; | ||
435 | return DMA_STATUS_GOOD; | ||
436 | } | ||
437 | |||
438 | /* | ||
439 | * Destroy a scatter/gather list handle that was created by alloc_dma_handle(). | ||
440 | * The list must be empty (contain no elements). | ||
441 | */ | ||
442 | void | ||
443 | ppc4xx_free_dma_handle(sgl_handle_t handle) | ||
444 | { | ||
445 | sgl_list_info_t *psgl = (sgl_list_info_t *) handle; | ||
446 | |||
447 | if (!handle) { | ||
448 | printk("ppc4xx_free_dma_handle: got NULL\n"); | ||
449 | return; | ||
450 | } else if (psgl->phead) { | ||
451 | printk("ppc4xx_free_dma_handle: list not empty\n"); | ||
452 | return; | ||
453 | } else if (!psgl->dma_addr) { /* should never happen */ | ||
454 | printk("ppc4xx_free_dma_handle: no dma address\n"); | ||
455 | return; | ||
456 | } | ||
457 | |||
458 | dma_free_coherent(NULL, DMA_PPC4xx_SIZE, (void *) psgl, 0); | ||
459 | } | ||
460 | |||
461 | EXPORT_SYMBOL(ppc4xx_alloc_dma_handle); | ||
462 | EXPORT_SYMBOL(ppc4xx_free_dma_handle); | ||
463 | EXPORT_SYMBOL(ppc4xx_add_dma_sgl); | ||
464 | EXPORT_SYMBOL(ppc4xx_delete_dma_sgl_element); | ||
465 | EXPORT_SYMBOL(ppc4xx_enable_dma_sgl); | ||
466 | EXPORT_SYMBOL(ppc4xx_disable_dma_sgl); | ||
467 | EXPORT_SYMBOL(ppc4xx_get_dma_sgl_residue); | ||
diff --git a/arch/ppc/syslib/ppc83xx_setup.c b/arch/ppc/syslib/ppc83xx_setup.c new file mode 100644 index 000000000000..c28f9d679484 --- /dev/null +++ b/arch/ppc/syslib/ppc83xx_setup.c | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ppc83xx_setup.c | ||
3 | * | ||
4 | * MPC83XX common board code | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2005 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/serial.h> | ||
22 | #include <linux/tty.h> /* for linux/serial_core.h */ | ||
23 | #include <linux/serial_core.h> | ||
24 | #include <linux/serial_8250.h> | ||
25 | |||
26 | #include <asm/prom.h> | ||
27 | #include <asm/time.h> | ||
28 | #include <asm/mpc83xx.h> | ||
29 | #include <asm/mmu.h> | ||
30 | #include <asm/ppc_sys.h> | ||
31 | #include <asm/kgdb.h> | ||
32 | |||
33 | #include <syslib/ppc83xx_setup.h> | ||
34 | |||
35 | phys_addr_t immrbar; | ||
36 | |||
37 | /* Return the amount of memory */ | ||
38 | unsigned long __init | ||
39 | mpc83xx_find_end_of_memory(void) | ||
40 | { | ||
41 | bd_t *binfo; | ||
42 | |||
43 | binfo = (bd_t *) __res; | ||
44 | |||
45 | return binfo->bi_memsize; | ||
46 | } | ||
47 | |||
48 | long __init | ||
49 | mpc83xx_time_init(void) | ||
50 | { | ||
51 | #define SPCR_OFFS 0x00000110 | ||
52 | #define SPCR_TBEN 0x00400000 | ||
53 | |||
54 | bd_t *binfo = (bd_t *)__res; | ||
55 | u32 *spcr = ioremap(binfo->bi_immr_base + SPCR_OFFS, 4); | ||
56 | |||
57 | *spcr |= SPCR_TBEN; | ||
58 | |||
59 | iounmap(spcr); | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | /* The decrementer counts at the system (internal) clock freq divided by 4 */ | ||
65 | void __init | ||
66 | mpc83xx_calibrate_decr(void) | ||
67 | { | ||
68 | bd_t *binfo = (bd_t *) __res; | ||
69 | unsigned int freq, divisor; | ||
70 | |||
71 | freq = binfo->bi_busfreq; | ||
72 | divisor = 4; | ||
73 | tb_ticks_per_jiffy = freq / HZ / divisor; | ||
74 | tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); | ||
75 | } | ||
76 | |||
77 | #ifdef CONFIG_SERIAL_8250 | ||
78 | void __init | ||
79 | mpc83xx_early_serial_map(void) | ||
80 | { | ||
81 | #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) | ||
82 | struct uart_port serial_req; | ||
83 | #endif | ||
84 | struct plat_serial8250_port *pdata; | ||
85 | bd_t *binfo = (bd_t *) __res; | ||
86 | pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC83xx_DUART); | ||
87 | |||
88 | /* Setup serial port access */ | ||
89 | pdata[0].uartclk = binfo->bi_busfreq; | ||
90 | pdata[0].mapbase += binfo->bi_immr_base; | ||
91 | pdata[0].membase = ioremap(pdata[0].mapbase, 0x100); | ||
92 | |||
93 | #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) | ||
94 | memset(&serial_req, 0, sizeof (serial_req)); | ||
95 | serial_req.iotype = SERIAL_IO_MEM; | ||
96 | serial_req.mapbase = pdata[0].mapbase; | ||
97 | serial_req.membase = pdata[0].membase; | ||
98 | serial_req.regshift = 0; | ||
99 | |||
100 | gen550_init(0, &serial_req); | ||
101 | #endif | ||
102 | |||
103 | pdata[1].uartclk = binfo->bi_busfreq; | ||
104 | pdata[1].mapbase += binfo->bi_immr_base; | ||
105 | pdata[1].membase = ioremap(pdata[1].mapbase, 0x100); | ||
106 | |||
107 | #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) | ||
108 | /* Assume gen550_init() doesn't modify serial_req */ | ||
109 | serial_req.mapbase = pdata[1].mapbase; | ||
110 | serial_req.membase = pdata[1].membase; | ||
111 | |||
112 | gen550_init(1, &serial_req); | ||
113 | #endif | ||
114 | } | ||
115 | #endif | ||
116 | |||
117 | void | ||
118 | mpc83xx_restart(char *cmd) | ||
119 | { | ||
120 | local_irq_disable(); | ||
121 | for(;;); | ||
122 | } | ||
123 | |||
124 | void | ||
125 | mpc83xx_power_off(void) | ||
126 | { | ||
127 | local_irq_disable(); | ||
128 | for(;;); | ||
129 | } | ||
130 | |||
131 | void | ||
132 | mpc83xx_halt(void) | ||
133 | { | ||
134 | local_irq_disable(); | ||
135 | for(;;); | ||
136 | } | ||
137 | |||
138 | /* PCI SUPPORT DOES NOT EXIT, MODEL after ppc85xx_setup.c */ | ||
diff --git a/arch/ppc/syslib/ppc83xx_setup.h b/arch/ppc/syslib/ppc83xx_setup.h new file mode 100644 index 000000000000..683f179b746c --- /dev/null +++ b/arch/ppc/syslib/ppc83xx_setup.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ppc83xx_setup.h | ||
3 | * | ||
4 | * MPC83XX common board definitions | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2005 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef __PPC_SYSLIB_PPC83XX_SETUP_H | ||
18 | #define __PPC_SYSLIB_PPC83XX_SETUP_H | ||
19 | |||
20 | #include <linux/config.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <asm/ppcboot.h> | ||
23 | |||
24 | extern unsigned long mpc83xx_find_end_of_memory(void) __init; | ||
25 | extern long mpc83xx_time_init(void) __init; | ||
26 | extern void mpc83xx_calibrate_decr(void) __init; | ||
27 | extern void mpc83xx_early_serial_map(void) __init; | ||
28 | extern void mpc83xx_restart(char *cmd); | ||
29 | extern void mpc83xx_power_off(void); | ||
30 | extern void mpc83xx_halt(void); | ||
31 | extern void mpc83xx_setup_hose(void) __init; | ||
32 | |||
33 | /* PCI config */ | ||
34 | #if 0 | ||
35 | #define PCI1_CFG_ADDR_OFFSET (FIXME) | ||
36 | #define PCI1_CFG_DATA_OFFSET (FIXME) | ||
37 | |||
38 | #define PCI2_CFG_ADDR_OFFSET (FIXME) | ||
39 | #define PCI2_CFG_DATA_OFFSET (FIXME) | ||
40 | #endif | ||
41 | |||
42 | /* Serial Config */ | ||
43 | #ifdef CONFIG_SERIAL_MANY_PORTS | ||
44 | #define RS_TABLE_SIZE 64 | ||
45 | #else | ||
46 | #define RS_TABLE_SIZE 2 | ||
47 | #endif | ||
48 | |||
49 | #ifndef BASE_BAUD | ||
50 | #define BASE_BAUD 115200 | ||
51 | #endif | ||
52 | |||
53 | #endif /* __PPC_SYSLIB_PPC83XX_SETUP_H */ | ||
diff --git a/arch/ppc/syslib/ppc85xx_common.c b/arch/ppc/syslib/ppc85xx_common.c new file mode 100644 index 000000000000..e83f2f8686d3 --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_common.c | |||
@@ -0,0 +1,33 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ppc85xx_common.c | ||
3 | * | ||
4 | * MPC85xx support routines | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2004 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | |||
21 | #include <asm/mpc85xx.h> | ||
22 | #include <asm/mmu.h> | ||
23 | |||
24 | /* ************************************************************************ */ | ||
25 | /* Return the value of CCSRBAR for the current board */ | ||
26 | |||
27 | phys_addr_t | ||
28 | get_ccsrbar(void) | ||
29 | { | ||
30 | return BOARD_CCSRBAR; | ||
31 | } | ||
32 | |||
33 | EXPORT_SYMBOL(get_ccsrbar); | ||
diff --git a/arch/ppc/syslib/ppc85xx_common.h b/arch/ppc/syslib/ppc85xx_common.h new file mode 100644 index 000000000000..2c8f304441bf --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_common.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ppc85xx_common.h | ||
3 | * | ||
4 | * MPC85xx support routines | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2004 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #ifndef __PPC_SYSLIB_PPC85XX_COMMON_H | ||
17 | #define __PPC_SYSLIB_PPC85XX_COMMON_H | ||
18 | |||
19 | #include <linux/config.h> | ||
20 | #include <linux/init.h> | ||
21 | |||
22 | /* Provide access to ccsrbar for any modules, etc */ | ||
23 | phys_addr_t get_ccsrbar(void); | ||
24 | |||
25 | #endif /* __PPC_SYSLIB_PPC85XX_COMMON_H */ | ||
diff --git a/arch/ppc/syslib/ppc85xx_setup.c b/arch/ppc/syslib/ppc85xx_setup.c new file mode 100644 index 000000000000..81f1968c3269 --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_setup.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ppc85xx_setup.c | ||
3 | * | ||
4 | * MPC85XX common board code | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2004 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/config.h> | ||
17 | #include <linux/types.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/serial.h> | ||
22 | #include <linux/tty.h> /* for linux/serial_core.h */ | ||
23 | #include <linux/serial_core.h> | ||
24 | #include <linux/serial_8250.h> | ||
25 | |||
26 | #include <asm/prom.h> | ||
27 | #include <asm/time.h> | ||
28 | #include <asm/mpc85xx.h> | ||
29 | #include <asm/immap_85xx.h> | ||
30 | #include <asm/mmu.h> | ||
31 | #include <asm/ppc_sys.h> | ||
32 | #include <asm/kgdb.h> | ||
33 | |||
34 | #include <syslib/ppc85xx_setup.h> | ||
35 | |||
36 | /* Return the amount of memory */ | ||
37 | unsigned long __init | ||
38 | mpc85xx_find_end_of_memory(void) | ||
39 | { | ||
40 | bd_t *binfo; | ||
41 | |||
42 | binfo = (bd_t *) __res; | ||
43 | |||
44 | return binfo->bi_memsize; | ||
45 | } | ||
46 | |||
47 | /* The decrementer counts at the system (internal) clock freq divided by 8 */ | ||
48 | void __init | ||
49 | mpc85xx_calibrate_decr(void) | ||
50 | { | ||
51 | bd_t *binfo = (bd_t *) __res; | ||
52 | unsigned int freq, divisor; | ||
53 | |||
54 | /* get the core frequency */ | ||
55 | freq = binfo->bi_busfreq; | ||
56 | |||
57 | /* The timebase is updated every 8 bus clocks, HID0[SEL_TBCLK] = 0 */ | ||
58 | divisor = 8; | ||
59 | tb_ticks_per_jiffy = freq / divisor / HZ; | ||
60 | tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000); | ||
61 | |||
62 | /* Set the time base to zero */ | ||
63 | mtspr(SPRN_TBWL, 0); | ||
64 | mtspr(SPRN_TBWU, 0); | ||
65 | |||
66 | /* Clear any pending timer interrupts */ | ||
67 | mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); | ||
68 | |||
69 | /* Enable decrementer interrupt */ | ||
70 | mtspr(SPRN_TCR, TCR_DIE); | ||
71 | } | ||
72 | |||
73 | #ifdef CONFIG_SERIAL_8250 | ||
74 | void __init | ||
75 | mpc85xx_early_serial_map(void) | ||
76 | { | ||
77 | #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) | ||
78 | struct uart_port serial_req; | ||
79 | #endif | ||
80 | struct plat_serial8250_port *pdata; | ||
81 | bd_t *binfo = (bd_t *) __res; | ||
82 | pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC85xx_DUART); | ||
83 | |||
84 | /* Setup serial port access */ | ||
85 | pdata[0].uartclk = binfo->bi_busfreq; | ||
86 | pdata[0].mapbase += binfo->bi_immr_base; | ||
87 | pdata[0].membase = ioremap(pdata[0].mapbase, MPC85xx_UART0_SIZE); | ||
88 | |||
89 | #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) | ||
90 | memset(&serial_req, 0, sizeof (serial_req)); | ||
91 | serial_req.iotype = SERIAL_IO_MEM; | ||
92 | serial_req.mapbase = pdata[0].mapbase; | ||
93 | serial_req.membase = pdata[0].membase; | ||
94 | serial_req.regshift = 0; | ||
95 | |||
96 | gen550_init(0, &serial_req); | ||
97 | #endif | ||
98 | |||
99 | pdata[1].uartclk = binfo->bi_busfreq; | ||
100 | pdata[1].mapbase += binfo->bi_immr_base; | ||
101 | pdata[1].membase = ioremap(pdata[1].mapbase, MPC85xx_UART0_SIZE); | ||
102 | |||
103 | #if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) | ||
104 | /* Assume gen550_init() doesn't modify serial_req */ | ||
105 | serial_req.mapbase = pdata[1].mapbase; | ||
106 | serial_req.membase = pdata[1].membase; | ||
107 | |||
108 | gen550_init(1, &serial_req); | ||
109 | #endif | ||
110 | } | ||
111 | #endif | ||
112 | |||
113 | void | ||
114 | mpc85xx_restart(char *cmd) | ||
115 | { | ||
116 | local_irq_disable(); | ||
117 | abort(); | ||
118 | } | ||
119 | |||
120 | void | ||
121 | mpc85xx_power_off(void) | ||
122 | { | ||
123 | local_irq_disable(); | ||
124 | for(;;); | ||
125 | } | ||
126 | |||
127 | void | ||
128 | mpc85xx_halt(void) | ||
129 | { | ||
130 | local_irq_disable(); | ||
131 | for(;;); | ||
132 | } | ||
133 | |||
134 | #ifdef CONFIG_PCI | ||
135 | static void __init | ||
136 | mpc85xx_setup_pci1(struct pci_controller *hose) | ||
137 | { | ||
138 | volatile struct ccsr_pci *pci; | ||
139 | volatile struct ccsr_guts *guts; | ||
140 | unsigned short temps; | ||
141 | bd_t *binfo = (bd_t *) __res; | ||
142 | |||
143 | pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI1_OFFSET, | ||
144 | MPC85xx_PCI1_SIZE); | ||
145 | |||
146 | guts = ioremap(binfo->bi_immr_base + MPC85xx_GUTS_OFFSET, | ||
147 | MPC85xx_GUTS_SIZE); | ||
148 | |||
149 | early_read_config_word(hose, 0, 0, PCI_COMMAND, &temps); | ||
150 | temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; | ||
151 | early_write_config_word(hose, 0, 0, PCI_COMMAND, temps); | ||
152 | |||
153 | #define PORDEVSR_PCI (0x00800000) /* PCI Mode */ | ||
154 | if (guts->pordevsr & PORDEVSR_PCI) { | ||
155 | early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0x80); | ||
156 | } else { | ||
157 | /* PCI-X init */ | ||
158 | temps = PCI_X_CMD_MAX_SPLIT | PCI_X_CMD_MAX_READ | ||
159 | | PCI_X_CMD_ERO | PCI_X_CMD_DPERR_E; | ||
160 | early_write_config_word(hose, 0, 0, PCIX_COMMAND, temps); | ||
161 | } | ||
162 | |||
163 | /* Disable all windows (except powar0 since its ignored) */ | ||
164 | pci->powar1 = 0; | ||
165 | pci->powar2 = 0; | ||
166 | pci->powar3 = 0; | ||
167 | pci->powar4 = 0; | ||
168 | pci->piwar1 = 0; | ||
169 | pci->piwar2 = 0; | ||
170 | pci->piwar3 = 0; | ||
171 | |||
172 | /* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI1_LOWER_MEM */ | ||
173 | pci->potar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff; | ||
174 | pci->potear1 = 0x00000000; | ||
175 | pci->powbar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff; | ||
176 | /* Enable, Mem R/W */ | ||
177 | pci->powar1 = 0x80044000 | | ||
178 | (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1); | ||
179 | |||
180 | /* Setup outboud IO windows @ MPC85XX_PCI1_IO_BASE */ | ||
181 | pci->potar2 = 0x00000000; | ||
182 | pci->potear2 = 0x00000000; | ||
183 | pci->powbar2 = (MPC85XX_PCI1_IO_BASE >> 12) & 0x000fffff; | ||
184 | /* Enable, IO R/W */ | ||
185 | pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1); | ||
186 | |||
187 | /* Setup 2G inbound Memory Window @ 0 */ | ||
188 | pci->pitar1 = 0x00000000; | ||
189 | pci->piwbar1 = 0x00000000; | ||
190 | pci->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local | ||
191 | Mem, Snoop R/W, 2G */ | ||
192 | } | ||
193 | |||
194 | |||
195 | extern int mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin); | ||
196 | extern int mpc85xx_exclude_device(u_char bus, u_char devfn); | ||
197 | |||
198 | #ifdef CONFIG_85xx_PCI2 | ||
199 | static void __init | ||
200 | mpc85xx_setup_pci2(struct pci_controller *hose) | ||
201 | { | ||
202 | volatile struct ccsr_pci *pci; | ||
203 | unsigned short temps; | ||
204 | bd_t *binfo = (bd_t *) __res; | ||
205 | |||
206 | pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI2_OFFSET, | ||
207 | MPC85xx_PCI2_SIZE); | ||
208 | |||
209 | early_read_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, &temps); | ||
210 | temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; | ||
211 | early_write_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, temps); | ||
212 | early_write_config_byte(hose, hose->bus_offset, 0, PCI_LATENCY_TIMER, 0x80); | ||
213 | |||
214 | /* Disable all windows (except powar0 since its ignored) */ | ||
215 | pci->powar1 = 0; | ||
216 | pci->powar2 = 0; | ||
217 | pci->powar3 = 0; | ||
218 | pci->powar4 = 0; | ||
219 | pci->piwar1 = 0; | ||
220 | pci->piwar2 = 0; | ||
221 | pci->piwar3 = 0; | ||
222 | |||
223 | /* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI2_LOWER_MEM */ | ||
224 | pci->potar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff; | ||
225 | pci->potear1 = 0x00000000; | ||
226 | pci->powbar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff; | ||
227 | /* Enable, Mem R/W */ | ||
228 | pci->powar1 = 0x80044000 | | ||
229 | (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1); | ||
230 | |||
231 | /* Setup outboud IO windows @ MPC85XX_PCI2_IO_BASE */ | ||
232 | pci->potar2 = 0x00000000; | ||
233 | pci->potear2 = 0x00000000; | ||
234 | pci->powbar2 = (MPC85XX_PCI2_IO_BASE >> 12) & 0x000fffff; | ||
235 | /* Enable, IO R/W */ | ||
236 | pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1); | ||
237 | |||
238 | /* Setup 2G inbound Memory Window @ 0 */ | ||
239 | pci->pitar1 = 0x00000000; | ||
240 | pci->piwbar1 = 0x00000000; | ||
241 | pci->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local | ||
242 | Mem, Snoop R/W, 2G */ | ||
243 | } | ||
244 | #endif /* CONFIG_85xx_PCI2 */ | ||
245 | |||
246 | int mpc85xx_pci1_last_busno = 0; | ||
247 | |||
248 | void __init | ||
249 | mpc85xx_setup_hose(void) | ||
250 | { | ||
251 | struct pci_controller *hose_a; | ||
252 | #ifdef CONFIG_85xx_PCI2 | ||
253 | struct pci_controller *hose_b; | ||
254 | #endif | ||
255 | bd_t *binfo = (bd_t *) __res; | ||
256 | |||
257 | hose_a = pcibios_alloc_controller(); | ||
258 | |||
259 | if (!hose_a) | ||
260 | return; | ||
261 | |||
262 | ppc_md.pci_swizzle = common_swizzle; | ||
263 | ppc_md.pci_map_irq = mpc85xx_map_irq; | ||
264 | |||
265 | hose_a->first_busno = 0; | ||
266 | hose_a->bus_offset = 0; | ||
267 | hose_a->last_busno = 0xff; | ||
268 | |||
269 | setup_indirect_pci(hose_a, binfo->bi_immr_base + PCI1_CFG_ADDR_OFFSET, | ||
270 | binfo->bi_immr_base + PCI1_CFG_DATA_OFFSET); | ||
271 | hose_a->set_cfg_type = 1; | ||
272 | |||
273 | mpc85xx_setup_pci1(hose_a); | ||
274 | |||
275 | hose_a->pci_mem_offset = MPC85XX_PCI1_MEM_OFFSET; | ||
276 | hose_a->mem_space.start = MPC85XX_PCI1_LOWER_MEM; | ||
277 | hose_a->mem_space.end = MPC85XX_PCI1_UPPER_MEM; | ||
278 | |||
279 | hose_a->io_space.start = MPC85XX_PCI1_LOWER_IO; | ||
280 | hose_a->io_space.end = MPC85XX_PCI1_UPPER_IO; | ||
281 | hose_a->io_base_phys = MPC85XX_PCI1_IO_BASE; | ||
282 | #ifdef CONFIG_85xx_PCI2 | ||
283 | isa_io_base = | ||
284 | (unsigned long) ioremap(MPC85XX_PCI1_IO_BASE, | ||
285 | MPC85XX_PCI1_IO_SIZE + | ||
286 | MPC85XX_PCI2_IO_SIZE); | ||
287 | #else | ||
288 | isa_io_base = | ||
289 | (unsigned long) ioremap(MPC85XX_PCI1_IO_BASE, | ||
290 | MPC85XX_PCI1_IO_SIZE); | ||
291 | #endif | ||
292 | hose_a->io_base_virt = (void *) isa_io_base; | ||
293 | |||
294 | /* setup resources */ | ||
295 | pci_init_resource(&hose_a->mem_resources[0], | ||
296 | MPC85XX_PCI1_LOWER_MEM, | ||
297 | MPC85XX_PCI1_UPPER_MEM, | ||
298 | IORESOURCE_MEM, "PCI1 host bridge"); | ||
299 | |||
300 | pci_init_resource(&hose_a->io_resource, | ||
301 | MPC85XX_PCI1_LOWER_IO, | ||
302 | MPC85XX_PCI1_UPPER_IO, | ||
303 | IORESOURCE_IO, "PCI1 host bridge"); | ||
304 | |||
305 | ppc_md.pci_exclude_device = mpc85xx_exclude_device; | ||
306 | |||
307 | hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno); | ||
308 | |||
309 | #ifdef CONFIG_85xx_PCI2 | ||
310 | hose_b = pcibios_alloc_controller(); | ||
311 | |||
312 | if (!hose_b) | ||
313 | return; | ||
314 | |||
315 | hose_b->bus_offset = hose_a->last_busno + 1; | ||
316 | hose_b->first_busno = hose_a->last_busno + 1; | ||
317 | hose_b->last_busno = 0xff; | ||
318 | |||
319 | setup_indirect_pci(hose_b, binfo->bi_immr_base + PCI2_CFG_ADDR_OFFSET, | ||
320 | binfo->bi_immr_base + PCI2_CFG_DATA_OFFSET); | ||
321 | hose_b->set_cfg_type = 1; | ||
322 | |||
323 | mpc85xx_setup_pci2(hose_b); | ||
324 | |||
325 | hose_b->pci_mem_offset = MPC85XX_PCI2_MEM_OFFSET; | ||
326 | hose_b->mem_space.start = MPC85XX_PCI2_LOWER_MEM; | ||
327 | hose_b->mem_space.end = MPC85XX_PCI2_UPPER_MEM; | ||
328 | |||
329 | hose_b->io_space.start = MPC85XX_PCI2_LOWER_IO; | ||
330 | hose_b->io_space.end = MPC85XX_PCI2_UPPER_IO; | ||
331 | hose_b->io_base_phys = MPC85XX_PCI2_IO_BASE; | ||
332 | hose_b->io_base_virt = (void *) isa_io_base + MPC85XX_PCI1_IO_SIZE; | ||
333 | |||
334 | /* setup resources */ | ||
335 | pci_init_resource(&hose_b->mem_resources[0], | ||
336 | MPC85XX_PCI2_LOWER_MEM, | ||
337 | MPC85XX_PCI2_UPPER_MEM, | ||
338 | IORESOURCE_MEM, "PCI2 host bridge"); | ||
339 | |||
340 | pci_init_resource(&hose_b->io_resource, | ||
341 | MPC85XX_PCI2_LOWER_IO, | ||
342 | MPC85XX_PCI2_UPPER_IO, | ||
343 | IORESOURCE_IO, "PCI2 host bridge"); | ||
344 | |||
345 | hose_b->last_busno = pciauto_bus_scan(hose_b, hose_b->first_busno); | ||
346 | |||
347 | /* let board code know what the last bus number was on PCI1 */ | ||
348 | mpc85xx_pci1_last_busno = hose_a->last_busno; | ||
349 | #endif | ||
350 | return; | ||
351 | } | ||
352 | #endif /* CONFIG_PCI */ | ||
353 | |||
354 | |||
diff --git a/arch/ppc/syslib/ppc85xx_setup.h b/arch/ppc/syslib/ppc85xx_setup.h new file mode 100644 index 000000000000..6e6cfe162faf --- /dev/null +++ b/arch/ppc/syslib/ppc85xx_setup.h | |||
@@ -0,0 +1,59 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ppc85xx_setup.h | ||
3 | * | ||
4 | * MPC85XX common board definitions | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2004 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #ifndef __PPC_SYSLIB_PPC85XX_SETUP_H | ||
18 | #define __PPC_SYSLIB_PPC85XX_SETUP_H | ||
19 | |||
20 | #include <linux/config.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <asm/ppcboot.h> | ||
23 | |||
24 | extern unsigned long mpc85xx_find_end_of_memory(void) __init; | ||
25 | extern void mpc85xx_calibrate_decr(void) __init; | ||
26 | extern void mpc85xx_early_serial_map(void) __init; | ||
27 | extern void mpc85xx_restart(char *cmd); | ||
28 | extern void mpc85xx_power_off(void); | ||
29 | extern void mpc85xx_halt(void); | ||
30 | extern void mpc85xx_setup_hose(void) __init; | ||
31 | |||
32 | /* PCI config */ | ||
33 | #define PCI1_CFG_ADDR_OFFSET (0x8000) | ||
34 | #define PCI1_CFG_DATA_OFFSET (0x8004) | ||
35 | |||
36 | #define PCI2_CFG_ADDR_OFFSET (0x9000) | ||
37 | #define PCI2_CFG_DATA_OFFSET (0x9004) | ||
38 | |||
39 | /* Additional register for PCI-X configuration */ | ||
40 | #define PCIX_NEXT_CAP 0x60 | ||
41 | #define PCIX_CAP_ID 0x61 | ||
42 | #define PCIX_COMMAND 0x62 | ||
43 | #define PCIX_STATUS 0x64 | ||
44 | |||
45 | /* Serial Config */ | ||
46 | #ifdef CONFIG_SERIAL_MANY_PORTS | ||
47 | #define RS_TABLE_SIZE 64 | ||
48 | #else | ||
49 | #define RS_TABLE_SIZE 2 | ||
50 | #endif | ||
51 | |||
52 | #ifndef BASE_BAUD | ||
53 | #define BASE_BAUD 115200 | ||
54 | #endif | ||
55 | |||
56 | /* Offset of CPM register space */ | ||
57 | #define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET) | ||
58 | |||
59 | #endif /* __PPC_SYSLIB_PPC85XX_SETUP_H */ | ||
diff --git a/arch/ppc/syslib/ppc8xx_pic.c b/arch/ppc/syslib/ppc8xx_pic.c new file mode 100644 index 000000000000..d3b01c6c97de --- /dev/null +++ b/arch/ppc/syslib/ppc8xx_pic.c | |||
@@ -0,0 +1,130 @@ | |||
1 | #include <linux/config.h> | ||
2 | #include <linux/module.h> | ||
3 | #include <linux/stddef.h> | ||
4 | #include <linux/init.h> | ||
5 | #include <linux/sched.h> | ||
6 | #include <linux/signal.h> | ||
7 | #include <linux/interrupt.h> | ||
8 | #include <asm/irq.h> | ||
9 | #include <asm/8xx_immap.h> | ||
10 | #include <asm/mpc8xx.h> | ||
11 | #include "ppc8xx_pic.h" | ||
12 | |||
13 | extern int cpm_get_irq(struct pt_regs *regs); | ||
14 | |||
15 | /* The 8xx internal interrupt controller. It is usually | ||
16 | * the only interrupt controller. Some boards, like the MBX and | ||
17 | * Sandpoint have the 8259 as a secondary controller. Depending | ||
18 | * upon the processor type, the internal controller can have as | ||
19 | * few as 16 interrups or as many as 64. We could use the | ||
20 | * "clear_bit()" and "set_bit()" functions like other platforms, | ||
21 | * but they are overkill for us. | ||
22 | */ | ||
23 | |||
24 | static void m8xx_mask_irq(unsigned int irq_nr) | ||
25 | { | ||
26 | int bit, word; | ||
27 | |||
28 | bit = irq_nr & 0x1f; | ||
29 | word = irq_nr >> 5; | ||
30 | |||
31 | ppc_cached_irq_mask[word] &= ~(1 << (31-bit)); | ||
32 | ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = | ||
33 | ppc_cached_irq_mask[word]; | ||
34 | } | ||
35 | |||
36 | static void m8xx_unmask_irq(unsigned int irq_nr) | ||
37 | { | ||
38 | int bit, word; | ||
39 | |||
40 | bit = irq_nr & 0x1f; | ||
41 | word = irq_nr >> 5; | ||
42 | |||
43 | ppc_cached_irq_mask[word] |= (1 << (31-bit)); | ||
44 | ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = | ||
45 | ppc_cached_irq_mask[word]; | ||
46 | } | ||
47 | |||
48 | static void m8xx_end_irq(unsigned int irq_nr) | ||
49 | { | ||
50 | if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)) | ||
51 | && irq_desc[irq_nr].action) { | ||
52 | int bit, word; | ||
53 | |||
54 | bit = irq_nr & 0x1f; | ||
55 | word = irq_nr >> 5; | ||
56 | |||
57 | ppc_cached_irq_mask[word] |= (1 << (31-bit)); | ||
58 | ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = | ||
59 | ppc_cached_irq_mask[word]; | ||
60 | } | ||
61 | } | ||
62 | |||
63 | |||
64 | static void m8xx_mask_and_ack(unsigned int irq_nr) | ||
65 | { | ||
66 | int bit, word; | ||
67 | |||
68 | bit = irq_nr & 0x1f; | ||
69 | word = irq_nr >> 5; | ||
70 | |||
71 | ppc_cached_irq_mask[word] &= ~(1 << (31-bit)); | ||
72 | ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask = | ||
73 | ppc_cached_irq_mask[word]; | ||
74 | ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend = 1 << (31-bit); | ||
75 | } | ||
76 | |||
77 | struct hw_interrupt_type ppc8xx_pic = { | ||
78 | .typename = " 8xx SIU ", | ||
79 | .enable = m8xx_unmask_irq, | ||
80 | .disable = m8xx_mask_irq, | ||
81 | .ack = m8xx_mask_and_ack, | ||
82 | .end = m8xx_end_irq, | ||
83 | }; | ||
84 | |||
85 | /* | ||
86 | * We either return a valid interrupt or -1 if there is nothing pending | ||
87 | */ | ||
88 | int | ||
89 | m8xx_get_irq(struct pt_regs *regs) | ||
90 | { | ||
91 | int irq; | ||
92 | |||
93 | /* For MPC8xx, read the SIVEC register and shift the bits down | ||
94 | * to get the irq number. | ||
95 | */ | ||
96 | irq = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec >> 26; | ||
97 | |||
98 | /* | ||
99 | * When we read the sivec without an interrupt to process, we will | ||
100 | * get back SIU_LEVEL7. In this case, return -1 | ||
101 | */ | ||
102 | if (irq == CPM_INTERRUPT) | ||
103 | irq = CPM_IRQ_OFFSET + cpm_get_irq(regs); | ||
104 | #if defined(CONFIG_PCI) | ||
105 | else if (irq == ISA_BRIDGE_INT) { | ||
106 | int isa_irq; | ||
107 | |||
108 | if ((isa_irq = i8259_poll(regs)) >= 0) | ||
109 | irq = I8259_IRQ_OFFSET + isa_irq; | ||
110 | } | ||
111 | #endif /* CONFIG_PCI */ | ||
112 | else if (irq == SIU_LEVEL7) | ||
113 | irq = -1; | ||
114 | |||
115 | return irq; | ||
116 | } | ||
117 | |||
118 | #if defined(CONFIG_MBX) && defined(CONFIG_PCI) | ||
119 | /* Only the MBX uses the external 8259. This allows us to catch standard | ||
120 | * drivers that may mess up the internal interrupt controllers, and also | ||
121 | * allow them to run without modification on the MBX. | ||
122 | */ | ||
123 | void mbx_i8259_action(int irq, void *dev_id, struct pt_regs *regs) | ||
124 | { | ||
125 | /* This interrupt handler never actually gets called. It is | ||
126 | * installed only to unmask the 8259 cascade interrupt in the SIU | ||
127 | * and to make the 8259 cascade interrupt visible in /proc/interrupts. | ||
128 | */ | ||
129 | } | ||
130 | #endif /* CONFIG_PCI */ | ||
diff --git a/arch/ppc/syslib/ppc8xx_pic.h b/arch/ppc/syslib/ppc8xx_pic.h new file mode 100644 index 000000000000..784935eac365 --- /dev/null +++ b/arch/ppc/syslib/ppc8xx_pic.h | |||
@@ -0,0 +1,21 @@ | |||
1 | #ifndef _PPC_KERNEL_PPC8xx_H | ||
2 | #define _PPC_KERNEL_PPC8xx_H | ||
3 | |||
4 | #include <linux/config.h> | ||
5 | #include <linux/irq.h> | ||
6 | #include <linux/interrupt.h> | ||
7 | |||
8 | extern struct hw_interrupt_type ppc8xx_pic; | ||
9 | |||
10 | void m8xx_pic_init(void); | ||
11 | void m8xx_do_IRQ(struct pt_regs *regs, | ||
12 | int cpu); | ||
13 | int m8xx_get_irq(struct pt_regs *regs); | ||
14 | |||
15 | #ifdef CONFIG_MBX | ||
16 | #include <asm/i8259.h> | ||
17 | #include <asm/io.h> | ||
18 | void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs); | ||
19 | #endif | ||
20 | |||
21 | #endif /* _PPC_KERNEL_PPC8xx_H */ | ||
diff --git a/arch/ppc/syslib/ppc_sys.c b/arch/ppc/syslib/ppc_sys.c new file mode 100644 index 000000000000..879202352560 --- /dev/null +++ b/arch/ppc/syslib/ppc_sys.c | |||
@@ -0,0 +1,103 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/ppc_sys.c | ||
3 | * | ||
4 | * PPC System library functions | ||
5 | * | ||
6 | * Maintainer: Kumar Gala <kumar.gala@freescale.com> | ||
7 | * | ||
8 | * Copyright 2005 Freescale Semiconductor Inc. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the | ||
12 | * Free Software Foundation; either version 2 of the License, or (at your | ||
13 | * option) any later version. | ||
14 | */ | ||
15 | |||
16 | #include <asm/ppc_sys.h> | ||
17 | |||
18 | int (*ppc_sys_device_fixup) (struct platform_device * pdev); | ||
19 | |||
20 | static int ppc_sys_inited; | ||
21 | |||
22 | void __init identify_ppc_sys_by_id(u32 id) | ||
23 | { | ||
24 | unsigned int i = 0; | ||
25 | while (1) { | ||
26 | if ((ppc_sys_specs[i].mask & id) == ppc_sys_specs[i].value) | ||
27 | break; | ||
28 | i++; | ||
29 | } | ||
30 | |||
31 | cur_ppc_sys_spec = &ppc_sys_specs[i]; | ||
32 | |||
33 | return; | ||
34 | } | ||
35 | |||
36 | void __init identify_ppc_sys_by_name(char *name) | ||
37 | { | ||
38 | /* TODO */ | ||
39 | return; | ||
40 | } | ||
41 | |||
42 | /* Update all memory resources by paddr, call before platform_device_register */ | ||
43 | void __init | ||
44 | ppc_sys_fixup_mem_resource(struct platform_device *pdev, phys_addr_t paddr) | ||
45 | { | ||
46 | int i; | ||
47 | for (i = 0; i < pdev->num_resources; i++) { | ||
48 | struct resource *r = &pdev->resource[i]; | ||
49 | if ((r->flags & IORESOURCE_MEM) == IORESOURCE_MEM) { | ||
50 | r->start += paddr; | ||
51 | r->end += paddr; | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
56 | /* Get platform_data pointer out of platform device, call before platform_device_register */ | ||
57 | void *__init ppc_sys_get_pdata(enum ppc_sys_devices dev) | ||
58 | { | ||
59 | return ppc_sys_platform_devices[dev].dev.platform_data; | ||
60 | } | ||
61 | |||
62 | void ppc_sys_device_remove(enum ppc_sys_devices dev) | ||
63 | { | ||
64 | unsigned int i; | ||
65 | |||
66 | if (ppc_sys_inited) { | ||
67 | platform_device_unregister(&ppc_sys_platform_devices[dev]); | ||
68 | } else { | ||
69 | if (cur_ppc_sys_spec == NULL) | ||
70 | return; | ||
71 | for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) | ||
72 | if (cur_ppc_sys_spec->device_list[i] == dev) | ||
73 | cur_ppc_sys_spec->device_list[i] = -1; | ||
74 | } | ||
75 | } | ||
76 | |||
77 | static int __init ppc_sys_init(void) | ||
78 | { | ||
79 | unsigned int i, dev_id, ret = 0; | ||
80 | |||
81 | BUG_ON(cur_ppc_sys_spec == NULL); | ||
82 | |||
83 | for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) { | ||
84 | dev_id = cur_ppc_sys_spec->device_list[i]; | ||
85 | if (dev_id != -1) { | ||
86 | if (ppc_sys_device_fixup != NULL) | ||
87 | ppc_sys_device_fixup(&ppc_sys_platform_devices | ||
88 | [dev_id]); | ||
89 | if (platform_device_register | ||
90 | (&ppc_sys_platform_devices[dev_id])) { | ||
91 | ret = 1; | ||
92 | printk(KERN_ERR | ||
93 | "unable to register device %d\n", | ||
94 | dev_id); | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | ppc_sys_inited = 1; | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | subsys_initcall(ppc_sys_init); | ||
diff --git a/arch/ppc/syslib/prep_nvram.c b/arch/ppc/syslib/prep_nvram.c new file mode 100644 index 000000000000..2bcf8a16d1c9 --- /dev/null +++ b/arch/ppc/syslib/prep_nvram.c | |||
@@ -0,0 +1,141 @@ | |||
1 | /* | ||
2 | * arch/ppc/kernel/prep_nvram.c | ||
3 | * | ||
4 | * Copyright (C) 1998 Corey Minyard | ||
5 | * | ||
6 | * This reads the NvRAM on PReP compliant machines (generally from IBM or | ||
7 | * Motorola). Motorola kept the format of NvRAM in their ROM, PPCBUG, the | ||
8 | * same, long after they had stopped producing PReP compliant machines. So | ||
9 | * this code is useful in those cases as well. | ||
10 | * | ||
11 | */ | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/ioport.h> | ||
16 | |||
17 | #include <asm/sections.h> | ||
18 | #include <asm/segment.h> | ||
19 | #include <asm/io.h> | ||
20 | #include <asm/machdep.h> | ||
21 | #include <asm/prep_nvram.h> | ||
22 | |||
23 | static char nvramData[MAX_PREP_NVRAM]; | ||
24 | static NVRAM_MAP *nvram=(NVRAM_MAP *)&nvramData[0]; | ||
25 | |||
26 | unsigned char __prep prep_nvram_read_val(int addr) | ||
27 | { | ||
28 | outb(addr, PREP_NVRAM_AS0); | ||
29 | outb(addr>>8, PREP_NVRAM_AS1); | ||
30 | return inb(PREP_NVRAM_DATA); | ||
31 | } | ||
32 | |||
33 | void __prep prep_nvram_write_val(int addr, | ||
34 | unsigned char val) | ||
35 | { | ||
36 | outb(addr, PREP_NVRAM_AS0); | ||
37 | outb(addr>>8, PREP_NVRAM_AS1); | ||
38 | outb(val, PREP_NVRAM_DATA); | ||
39 | } | ||
40 | |||
41 | void __init init_prep_nvram(void) | ||
42 | { | ||
43 | unsigned char *nvp; | ||
44 | int i; | ||
45 | int nvramSize; | ||
46 | |||
47 | /* | ||
48 | * The following could fail if the NvRAM were corrupt but | ||
49 | * we expect the boot firmware to have checked its checksum | ||
50 | * before boot | ||
51 | */ | ||
52 | nvp = (char *) &nvram->Header; | ||
53 | for (i=0; i<sizeof(HEADER); i++) | ||
54 | { | ||
55 | *nvp = ppc_md.nvram_read_val(i); | ||
56 | nvp++; | ||
57 | } | ||
58 | |||
59 | /* | ||
60 | * The PReP NvRAM may be any size so read in the header to | ||
61 | * determine how much we must read in order to get the complete | ||
62 | * GE area | ||
63 | */ | ||
64 | nvramSize=(int)nvram->Header.GEAddress+nvram->Header.GELength; | ||
65 | if(nvramSize>MAX_PREP_NVRAM) | ||
66 | { | ||
67 | /* | ||
68 | * NvRAM is too large | ||
69 | */ | ||
70 | nvram->Header.GELength=0; | ||
71 | return; | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | * Read the remainder of the PReP NvRAM | ||
76 | */ | ||
77 | nvp = (char *) &nvram->GEArea[0]; | ||
78 | for (i=sizeof(HEADER); i<nvramSize; i++) | ||
79 | { | ||
80 | *nvp = ppc_md.nvram_read_val(i); | ||
81 | nvp++; | ||
82 | } | ||
83 | } | ||
84 | |||
85 | __prep | ||
86 | char __prep *prep_nvram_get_var(const char *name) | ||
87 | { | ||
88 | char *cp; | ||
89 | int namelen; | ||
90 | |||
91 | namelen = strlen(name); | ||
92 | cp = prep_nvram_first_var(); | ||
93 | while (cp != NULL) { | ||
94 | if ((strncmp(name, cp, namelen) == 0) | ||
95 | && (cp[namelen] == '=')) | ||
96 | { | ||
97 | return cp+namelen+1; | ||
98 | } | ||
99 | cp = prep_nvram_next_var(cp); | ||
100 | } | ||
101 | |||
102 | return NULL; | ||
103 | } | ||
104 | |||
105 | __prep | ||
106 | char __prep *prep_nvram_first_var(void) | ||
107 | { | ||
108 | if (nvram->Header.GELength == 0) { | ||
109 | return NULL; | ||
110 | } else { | ||
111 | return (((char *)nvram) | ||
112 | + ((unsigned int) nvram->Header.GEAddress)); | ||
113 | } | ||
114 | } | ||
115 | |||
116 | __prep | ||
117 | char __prep *prep_nvram_next_var(char *name) | ||
118 | { | ||
119 | char *cp; | ||
120 | |||
121 | |||
122 | cp = name; | ||
123 | while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) | ||
124 | && (*cp != '\0')) | ||
125 | { | ||
126 | cp++; | ||
127 | } | ||
128 | |||
129 | /* Skip over any null characters. */ | ||
130 | while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) | ||
131 | && (*cp == '\0')) | ||
132 | { | ||
133 | cp++; | ||
134 | } | ||
135 | |||
136 | if ((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) { | ||
137 | return cp; | ||
138 | } else { | ||
139 | return NULL; | ||
140 | } | ||
141 | } | ||
diff --git a/arch/ppc/syslib/prom.c b/arch/ppc/syslib/prom.c new file mode 100644 index 000000000000..2c64ed627475 --- /dev/null +++ b/arch/ppc/syslib/prom.c | |||
@@ -0,0 +1,1447 @@ | |||
1 | /* | ||
2 | * Procedures for interfacing to the Open Firmware PROM on | ||
3 | * Power Macintosh computers. | ||
4 | * | ||
5 | * In particular, we are interested in the device tree | ||
6 | * and in using some of its services (exit, write to stdout). | ||
7 | * | ||
8 | * Paul Mackerras August 1996. | ||
9 | * Copyright (C) 1996 Paul Mackerras. | ||
10 | */ | ||
11 | #include <stdarg.h> | ||
12 | #include <linux/config.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/version.h> | ||
17 | #include <linux/threads.h> | ||
18 | #include <linux/spinlock.h> | ||
19 | #include <linux/ioport.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/bitops.h> | ||
23 | |||
24 | #include <asm/sections.h> | ||
25 | #include <asm/prom.h> | ||
26 | #include <asm/page.h> | ||
27 | #include <asm/processor.h> | ||
28 | #include <asm/irq.h> | ||
29 | #include <asm/io.h> | ||
30 | #include <asm/smp.h> | ||
31 | #include <asm/bootx.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/mmu.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | #include <asm/bootinfo.h> | ||
36 | #include <asm/btext.h> | ||
37 | #include <asm/pci-bridge.h> | ||
38 | #include <asm/open_pic.h> | ||
39 | |||
40 | |||
41 | struct pci_address { | ||
42 | unsigned a_hi; | ||
43 | unsigned a_mid; | ||
44 | unsigned a_lo; | ||
45 | }; | ||
46 | |||
47 | struct pci_reg_property { | ||
48 | struct pci_address addr; | ||
49 | unsigned size_hi; | ||
50 | unsigned size_lo; | ||
51 | }; | ||
52 | |||
53 | struct isa_reg_property { | ||
54 | unsigned space; | ||
55 | unsigned address; | ||
56 | unsigned size; | ||
57 | }; | ||
58 | |||
59 | typedef unsigned long interpret_func(struct device_node *, unsigned long, | ||
60 | int, int); | ||
61 | static interpret_func interpret_pci_props; | ||
62 | static interpret_func interpret_dbdma_props; | ||
63 | static interpret_func interpret_isa_props; | ||
64 | static interpret_func interpret_macio_props; | ||
65 | static interpret_func interpret_root_props; | ||
66 | |||
67 | extern char *klimit; | ||
68 | |||
69 | /* Set for a newworld or CHRP machine */ | ||
70 | int use_of_interrupt_tree; | ||
71 | struct device_node *dflt_interrupt_controller; | ||
72 | int num_interrupt_controllers; | ||
73 | |||
74 | int pmac_newworld; | ||
75 | |||
76 | extern unsigned int rtas_entry; /* physical pointer */ | ||
77 | |||
78 | extern struct device_node *allnodes; | ||
79 | |||
80 | static unsigned long finish_node(struct device_node *, unsigned long, | ||
81 | interpret_func *, int, int); | ||
82 | static unsigned long finish_node_interrupts(struct device_node *, unsigned long); | ||
83 | static struct device_node *find_phandle(phandle); | ||
84 | |||
85 | extern void enter_rtas(void *); | ||
86 | void phys_call_rtas(int, int, int, ...); | ||
87 | |||
88 | extern char cmd_line[512]; /* XXX */ | ||
89 | extern boot_infos_t *boot_infos; | ||
90 | unsigned long dev_tree_size; | ||
91 | |||
92 | void __openfirmware | ||
93 | phys_call_rtas(int service, int nargs, int nret, ...) | ||
94 | { | ||
95 | va_list list; | ||
96 | union { | ||
97 | unsigned long words[16]; | ||
98 | double align; | ||
99 | } u; | ||
100 | void (*rtas)(void *, unsigned long); | ||
101 | int i; | ||
102 | |||
103 | u.words[0] = service; | ||
104 | u.words[1] = nargs; | ||
105 | u.words[2] = nret; | ||
106 | va_start(list, nret); | ||
107 | for (i = 0; i < nargs; ++i) | ||
108 | u.words[i+3] = va_arg(list, unsigned long); | ||
109 | va_end(list); | ||
110 | |||
111 | rtas = (void (*)(void *, unsigned long)) rtas_entry; | ||
112 | rtas(&u, rtas_data); | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * finish_device_tree is called once things are running normally | ||
117 | * (i.e. with text and data mapped to the address they were linked at). | ||
118 | * It traverses the device tree and fills in the name, type, | ||
119 | * {n_}addrs and {n_}intrs fields of each node. | ||
120 | */ | ||
121 | void __init | ||
122 | finish_device_tree(void) | ||
123 | { | ||
124 | unsigned long mem = (unsigned long) klimit; | ||
125 | struct device_node *np; | ||
126 | |||
127 | /* All newworld pmac machines and CHRPs now use the interrupt tree */ | ||
128 | for (np = allnodes; np != NULL; np = np->allnext) { | ||
129 | if (get_property(np, "interrupt-parent", NULL)) { | ||
130 | use_of_interrupt_tree = 1; | ||
131 | break; | ||
132 | } | ||
133 | } | ||
134 | if (_machine == _MACH_Pmac && use_of_interrupt_tree) | ||
135 | pmac_newworld = 1; | ||
136 | |||
137 | #ifdef CONFIG_BOOTX_TEXT | ||
138 | if (boot_infos && pmac_newworld) { | ||
139 | prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n"); | ||
140 | prom_print(" You should use an Open Firmware bootloader\n"); | ||
141 | } | ||
142 | #endif /* CONFIG_BOOTX_TEXT */ | ||
143 | |||
144 | if (use_of_interrupt_tree) { | ||
145 | /* | ||
146 | * We want to find out here how many interrupt-controller | ||
147 | * nodes there are, and if we are booted from BootX, | ||
148 | * we need a pointer to the first (and hopefully only) | ||
149 | * such node. But we can't use find_devices here since | ||
150 | * np->name has not been set yet. -- paulus | ||
151 | */ | ||
152 | int n = 0; | ||
153 | char *name, *ic; | ||
154 | int iclen; | ||
155 | |||
156 | for (np = allnodes; np != NULL; np = np->allnext) { | ||
157 | ic = get_property(np, "interrupt-controller", &iclen); | ||
158 | name = get_property(np, "name", NULL); | ||
159 | /* checking iclen makes sure we don't get a false | ||
160 | match on /chosen.interrupt_controller */ | ||
161 | if ((name != NULL | ||
162 | && strcmp(name, "interrupt-controller") == 0) | ||
163 | || (ic != NULL && iclen == 0 && strcmp(name, "AppleKiwi"))) { | ||
164 | if (n == 0) | ||
165 | dflt_interrupt_controller = np; | ||
166 | ++n; | ||
167 | } | ||
168 | } | ||
169 | num_interrupt_controllers = n; | ||
170 | } | ||
171 | |||
172 | mem = finish_node(allnodes, mem, NULL, 1, 1); | ||
173 | dev_tree_size = mem - (unsigned long) allnodes; | ||
174 | klimit = (char *) mem; | ||
175 | } | ||
176 | |||
177 | static unsigned long __init | ||
178 | finish_node(struct device_node *np, unsigned long mem_start, | ||
179 | interpret_func *ifunc, int naddrc, int nsizec) | ||
180 | { | ||
181 | struct device_node *child; | ||
182 | int *ip; | ||
183 | |||
184 | np->name = get_property(np, "name", NULL); | ||
185 | np->type = get_property(np, "device_type", NULL); | ||
186 | |||
187 | if (!np->name) | ||
188 | np->name = "<NULL>"; | ||
189 | if (!np->type) | ||
190 | np->type = "<NULL>"; | ||
191 | |||
192 | /* get the device addresses and interrupts */ | ||
193 | if (ifunc != NULL) | ||
194 | mem_start = ifunc(np, mem_start, naddrc, nsizec); | ||
195 | |||
196 | if (use_of_interrupt_tree) | ||
197 | mem_start = finish_node_interrupts(np, mem_start); | ||
198 | |||
199 | /* Look for #address-cells and #size-cells properties. */ | ||
200 | ip = (int *) get_property(np, "#address-cells", NULL); | ||
201 | if (ip != NULL) | ||
202 | naddrc = *ip; | ||
203 | ip = (int *) get_property(np, "#size-cells", NULL); | ||
204 | if (ip != NULL) | ||
205 | nsizec = *ip; | ||
206 | |||
207 | if (np->parent == NULL) | ||
208 | ifunc = interpret_root_props; | ||
209 | else if (np->type == 0) | ||
210 | ifunc = NULL; | ||
211 | else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) | ||
212 | ifunc = interpret_pci_props; | ||
213 | else if (!strcmp(np->type, "dbdma")) | ||
214 | ifunc = interpret_dbdma_props; | ||
215 | else if (!strcmp(np->type, "mac-io") | ||
216 | || ifunc == interpret_macio_props) | ||
217 | ifunc = interpret_macio_props; | ||
218 | else if (!strcmp(np->type, "isa")) | ||
219 | ifunc = interpret_isa_props; | ||
220 | else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3")) | ||
221 | ifunc = interpret_root_props; | ||
222 | else if (!((ifunc == interpret_dbdma_props | ||
223 | || ifunc == interpret_macio_props) | ||
224 | && (!strcmp(np->type, "escc") | ||
225 | || !strcmp(np->type, "media-bay")))) | ||
226 | ifunc = NULL; | ||
227 | |||
228 | /* if we were booted from BootX, convert the full name */ | ||
229 | if (boot_infos | ||
230 | && strncmp(np->full_name, "Devices:device-tree", 19) == 0) { | ||
231 | if (np->full_name[19] == 0) { | ||
232 | strcpy(np->full_name, "/"); | ||
233 | } else if (np->full_name[19] == ':') { | ||
234 | char *p = np->full_name + 19; | ||
235 | np->full_name = p; | ||
236 | for (; *p; ++p) | ||
237 | if (*p == ':') | ||
238 | *p = '/'; | ||
239 | } | ||
240 | } | ||
241 | |||
242 | for (child = np->child; child != NULL; child = child->sibling) | ||
243 | mem_start = finish_node(child, mem_start, ifunc, | ||
244 | naddrc, nsizec); | ||
245 | |||
246 | return mem_start; | ||
247 | } | ||
248 | |||
249 | /* | ||
250 | * Find the interrupt parent of a node. | ||
251 | */ | ||
252 | static struct device_node * __init | ||
253 | intr_parent(struct device_node *p) | ||
254 | { | ||
255 | phandle *parp; | ||
256 | |||
257 | parp = (phandle *) get_property(p, "interrupt-parent", NULL); | ||
258 | if (parp == NULL) | ||
259 | return p->parent; | ||
260 | p = find_phandle(*parp); | ||
261 | if (p != NULL) | ||
262 | return p; | ||
263 | /* | ||
264 | * On a powermac booted with BootX, we don't get to know the | ||
265 | * phandles for any nodes, so find_phandle will return NULL. | ||
266 | * Fortunately these machines only have one interrupt controller | ||
267 | * so there isn't in fact any ambiguity. -- paulus | ||
268 | */ | ||
269 | if (num_interrupt_controllers == 1) | ||
270 | p = dflt_interrupt_controller; | ||
271 | return p; | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * Find out the size of each entry of the interrupts property | ||
276 | * for a node. | ||
277 | */ | ||
278 | static int __init | ||
279 | prom_n_intr_cells(struct device_node *np) | ||
280 | { | ||
281 | struct device_node *p; | ||
282 | unsigned int *icp; | ||
283 | |||
284 | for (p = np; (p = intr_parent(p)) != NULL; ) { | ||
285 | icp = (unsigned int *) | ||
286 | get_property(p, "#interrupt-cells", NULL); | ||
287 | if (icp != NULL) | ||
288 | return *icp; | ||
289 | if (get_property(p, "interrupt-controller", NULL) != NULL | ||
290 | || get_property(p, "interrupt-map", NULL) != NULL) { | ||
291 | printk("oops, node %s doesn't have #interrupt-cells\n", | ||
292 | p->full_name); | ||
293 | return 1; | ||
294 | } | ||
295 | } | ||
296 | printk("prom_n_intr_cells failed for %s\n", np->full_name); | ||
297 | return 1; | ||
298 | } | ||
299 | |||
300 | /* | ||
301 | * Map an interrupt from a device up to the platform interrupt | ||
302 | * descriptor. | ||
303 | */ | ||
304 | static int __init | ||
305 | map_interrupt(unsigned int **irq, struct device_node **ictrler, | ||
306 | struct device_node *np, unsigned int *ints, int nintrc) | ||
307 | { | ||
308 | struct device_node *p, *ipar; | ||
309 | unsigned int *imap, *imask, *ip; | ||
310 | int i, imaplen, match; | ||
311 | int newintrc = 1, newaddrc = 1; | ||
312 | unsigned int *reg; | ||
313 | int naddrc; | ||
314 | |||
315 | reg = (unsigned int *) get_property(np, "reg", NULL); | ||
316 | naddrc = prom_n_addr_cells(np); | ||
317 | p = intr_parent(np); | ||
318 | while (p != NULL) { | ||
319 | if (get_property(p, "interrupt-controller", NULL) != NULL) | ||
320 | /* this node is an interrupt controller, stop here */ | ||
321 | break; | ||
322 | imap = (unsigned int *) | ||
323 | get_property(p, "interrupt-map", &imaplen); | ||
324 | if (imap == NULL) { | ||
325 | p = intr_parent(p); | ||
326 | continue; | ||
327 | } | ||
328 | imask = (unsigned int *) | ||
329 | get_property(p, "interrupt-map-mask", NULL); | ||
330 | if (imask == NULL) { | ||
331 | printk("oops, %s has interrupt-map but no mask\n", | ||
332 | p->full_name); | ||
333 | return 0; | ||
334 | } | ||
335 | imaplen /= sizeof(unsigned int); | ||
336 | match = 0; | ||
337 | ipar = NULL; | ||
338 | while (imaplen > 0 && !match) { | ||
339 | /* check the child-interrupt field */ | ||
340 | match = 1; | ||
341 | for (i = 0; i < naddrc && match; ++i) | ||
342 | match = ((reg[i] ^ imap[i]) & imask[i]) == 0; | ||
343 | for (; i < naddrc + nintrc && match; ++i) | ||
344 | match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0; | ||
345 | imap += naddrc + nintrc; | ||
346 | imaplen -= naddrc + nintrc; | ||
347 | /* grab the interrupt parent */ | ||
348 | ipar = find_phandle((phandle) *imap++); | ||
349 | --imaplen; | ||
350 | if (ipar == NULL && num_interrupt_controllers == 1) | ||
351 | /* cope with BootX not giving us phandles */ | ||
352 | ipar = dflt_interrupt_controller; | ||
353 | if (ipar == NULL) { | ||
354 | printk("oops, no int parent %x in map of %s\n", | ||
355 | imap[-1], p->full_name); | ||
356 | return 0; | ||
357 | } | ||
358 | /* find the parent's # addr and intr cells */ | ||
359 | ip = (unsigned int *) | ||
360 | get_property(ipar, "#interrupt-cells", NULL); | ||
361 | if (ip == NULL) { | ||
362 | printk("oops, no #interrupt-cells on %s\n", | ||
363 | ipar->full_name); | ||
364 | return 0; | ||
365 | } | ||
366 | newintrc = *ip; | ||
367 | ip = (unsigned int *) | ||
368 | get_property(ipar, "#address-cells", NULL); | ||
369 | newaddrc = (ip == NULL)? 0: *ip; | ||
370 | imap += newaddrc + newintrc; | ||
371 | imaplen -= newaddrc + newintrc; | ||
372 | } | ||
373 | if (imaplen < 0) { | ||
374 | printk("oops, error decoding int-map on %s, len=%d\n", | ||
375 | p->full_name, imaplen); | ||
376 | return 0; | ||
377 | } | ||
378 | if (!match) { | ||
379 | printk("oops, no match in %s int-map for %s\n", | ||
380 | p->full_name, np->full_name); | ||
381 | return 0; | ||
382 | } | ||
383 | p = ipar; | ||
384 | naddrc = newaddrc; | ||
385 | nintrc = newintrc; | ||
386 | ints = imap - nintrc; | ||
387 | reg = ints - naddrc; | ||
388 | } | ||
389 | if (p == NULL) | ||
390 | printk("hmmm, int tree for %s doesn't have ctrler\n", | ||
391 | np->full_name); | ||
392 | *irq = ints; | ||
393 | *ictrler = p; | ||
394 | return nintrc; | ||
395 | } | ||
396 | |||
397 | /* | ||
398 | * New version of finish_node_interrupts. | ||
399 | */ | ||
400 | static unsigned long __init | ||
401 | finish_node_interrupts(struct device_node *np, unsigned long mem_start) | ||
402 | { | ||
403 | unsigned int *ints; | ||
404 | int intlen, intrcells; | ||
405 | int i, j, n, offset; | ||
406 | unsigned int *irq; | ||
407 | struct device_node *ic; | ||
408 | |||
409 | ints = (unsigned int *) get_property(np, "interrupts", &intlen); | ||
410 | if (ints == NULL) | ||
411 | return mem_start; | ||
412 | intrcells = prom_n_intr_cells(np); | ||
413 | intlen /= intrcells * sizeof(unsigned int); | ||
414 | np->n_intrs = intlen; | ||
415 | np->intrs = (struct interrupt_info *) mem_start; | ||
416 | mem_start += intlen * sizeof(struct interrupt_info); | ||
417 | |||
418 | for (i = 0; i < intlen; ++i) { | ||
419 | np->intrs[i].line = 0; | ||
420 | np->intrs[i].sense = 1; | ||
421 | n = map_interrupt(&irq, &ic, np, ints, intrcells); | ||
422 | if (n <= 0) | ||
423 | continue; | ||
424 | offset = 0; | ||
425 | /* | ||
426 | * On a CHRP we have an 8259 which is subordinate to | ||
427 | * the openpic in the interrupt tree, but we want the | ||
428 | * openpic's interrupt numbers offsetted, not the 8259's. | ||
429 | * So we apply the offset if the controller is at the | ||
430 | * root of the interrupt tree, i.e. has no interrupt-parent. | ||
431 | * This doesn't cope with the general case of multiple | ||
432 | * cascaded interrupt controllers, but then neither will | ||
433 | * irq.c at the moment either. -- paulus | ||
434 | * The G5 triggers that code, I add a machine test. On | ||
435 | * those machines, we want to offset interrupts from the | ||
436 | * second openpic by 128 -- BenH | ||
437 | */ | ||
438 | if (_machine != _MACH_Pmac && num_interrupt_controllers > 1 | ||
439 | && ic != NULL | ||
440 | && get_property(ic, "interrupt-parent", NULL) == NULL) | ||
441 | offset = 16; | ||
442 | else if (_machine == _MACH_Pmac && num_interrupt_controllers > 1 | ||
443 | && ic != NULL && ic->parent != NULL) { | ||
444 | char *name = get_property(ic->parent, "name", NULL); | ||
445 | if (name && !strcmp(name, "u3")) | ||
446 | offset = 128; | ||
447 | } | ||
448 | |||
449 | np->intrs[i].line = irq[0] + offset; | ||
450 | if (n > 1) | ||
451 | np->intrs[i].sense = irq[1]; | ||
452 | if (n > 2) { | ||
453 | printk("hmmm, got %d intr cells for %s:", n, | ||
454 | np->full_name); | ||
455 | for (j = 0; j < n; ++j) | ||
456 | printk(" %d", irq[j]); | ||
457 | printk("\n"); | ||
458 | } | ||
459 | ints += intrcells; | ||
460 | } | ||
461 | |||
462 | return mem_start; | ||
463 | } | ||
464 | |||
465 | /* | ||
466 | * When BootX makes a copy of the device tree from the MacOS | ||
467 | * Name Registry, it is in the format we use but all of the pointers | ||
468 | * are offsets from the start of the tree. | ||
469 | * This procedure updates the pointers. | ||
470 | */ | ||
471 | void __init | ||
472 | relocate_nodes(void) | ||
473 | { | ||
474 | unsigned long base; | ||
475 | struct device_node *np; | ||
476 | struct property *pp; | ||
477 | |||
478 | #define ADDBASE(x) (x = (typeof (x))((x)? ((unsigned long)(x) + base): 0)) | ||
479 | |||
480 | base = (unsigned long) boot_infos + boot_infos->deviceTreeOffset; | ||
481 | allnodes = (struct device_node *)(base + 4); | ||
482 | for (np = allnodes; np != 0; np = np->allnext) { | ||
483 | ADDBASE(np->full_name); | ||
484 | ADDBASE(np->properties); | ||
485 | ADDBASE(np->parent); | ||
486 | ADDBASE(np->child); | ||
487 | ADDBASE(np->sibling); | ||
488 | ADDBASE(np->allnext); | ||
489 | for (pp = np->properties; pp != 0; pp = pp->next) { | ||
490 | ADDBASE(pp->name); | ||
491 | ADDBASE(pp->value); | ||
492 | ADDBASE(pp->next); | ||
493 | } | ||
494 | } | ||
495 | } | ||
496 | |||
497 | int | ||
498 | prom_n_addr_cells(struct device_node* np) | ||
499 | { | ||
500 | int* ip; | ||
501 | do { | ||
502 | if (np->parent) | ||
503 | np = np->parent; | ||
504 | ip = (int *) get_property(np, "#address-cells", NULL); | ||
505 | if (ip != NULL) | ||
506 | return *ip; | ||
507 | } while (np->parent); | ||
508 | /* No #address-cells property for the root node, default to 1 */ | ||
509 | return 1; | ||
510 | } | ||
511 | |||
512 | int | ||
513 | prom_n_size_cells(struct device_node* np) | ||
514 | { | ||
515 | int* ip; | ||
516 | do { | ||
517 | if (np->parent) | ||
518 | np = np->parent; | ||
519 | ip = (int *) get_property(np, "#size-cells", NULL); | ||
520 | if (ip != NULL) | ||
521 | return *ip; | ||
522 | } while (np->parent); | ||
523 | /* No #size-cells property for the root node, default to 1 */ | ||
524 | return 1; | ||
525 | } | ||
526 | |||
527 | static unsigned long __init | ||
528 | map_addr(struct device_node *np, unsigned long space, unsigned long addr) | ||
529 | { | ||
530 | int na; | ||
531 | unsigned int *ranges; | ||
532 | int rlen = 0; | ||
533 | unsigned int type; | ||
534 | |||
535 | type = (space >> 24) & 3; | ||
536 | if (type == 0) | ||
537 | return addr; | ||
538 | |||
539 | while ((np = np->parent) != NULL) { | ||
540 | if (strcmp(np->type, "pci") != 0) | ||
541 | continue; | ||
542 | /* PCI bridge: map the address through the ranges property */ | ||
543 | na = prom_n_addr_cells(np); | ||
544 | ranges = (unsigned int *) get_property(np, "ranges", &rlen); | ||
545 | while ((rlen -= (na + 5) * sizeof(unsigned int)) >= 0) { | ||
546 | if (((ranges[0] >> 24) & 3) == type | ||
547 | && ranges[2] <= addr | ||
548 | && addr - ranges[2] < ranges[na+4]) { | ||
549 | /* ok, this matches, translate it */ | ||
550 | addr += ranges[na+2] - ranges[2]; | ||
551 | break; | ||
552 | } | ||
553 | ranges += na + 5; | ||
554 | } | ||
555 | } | ||
556 | return addr; | ||
557 | } | ||
558 | |||
559 | static unsigned long __init | ||
560 | interpret_pci_props(struct device_node *np, unsigned long mem_start, | ||
561 | int naddrc, int nsizec) | ||
562 | { | ||
563 | struct address_range *adr; | ||
564 | struct pci_reg_property *pci_addrs; | ||
565 | int i, l, *ip; | ||
566 | |||
567 | pci_addrs = (struct pci_reg_property *) | ||
568 | get_property(np, "assigned-addresses", &l); | ||
569 | if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) { | ||
570 | i = 0; | ||
571 | adr = (struct address_range *) mem_start; | ||
572 | while ((l -= sizeof(struct pci_reg_property)) >= 0) { | ||
573 | adr[i].space = pci_addrs[i].addr.a_hi; | ||
574 | adr[i].address = map_addr(np, pci_addrs[i].addr.a_hi, | ||
575 | pci_addrs[i].addr.a_lo); | ||
576 | adr[i].size = pci_addrs[i].size_lo; | ||
577 | ++i; | ||
578 | } | ||
579 | np->addrs = adr; | ||
580 | np->n_addrs = i; | ||
581 | mem_start += i * sizeof(struct address_range); | ||
582 | } | ||
583 | |||
584 | if (use_of_interrupt_tree) | ||
585 | return mem_start; | ||
586 | |||
587 | ip = (int *) get_property(np, "AAPL,interrupts", &l); | ||
588 | if (ip == 0 && np->parent) | ||
589 | ip = (int *) get_property(np->parent, "AAPL,interrupts", &l); | ||
590 | if (ip == 0) | ||
591 | ip = (int *) get_property(np, "interrupts", &l); | ||
592 | if (ip != 0) { | ||
593 | np->intrs = (struct interrupt_info *) mem_start; | ||
594 | np->n_intrs = l / sizeof(int); | ||
595 | mem_start += np->n_intrs * sizeof(struct interrupt_info); | ||
596 | for (i = 0; i < np->n_intrs; ++i) { | ||
597 | np->intrs[i].line = *ip++; | ||
598 | np->intrs[i].sense = 1; | ||
599 | } | ||
600 | } | ||
601 | |||
602 | return mem_start; | ||
603 | } | ||
604 | |||
605 | static unsigned long __init | ||
606 | interpret_dbdma_props(struct device_node *np, unsigned long mem_start, | ||
607 | int naddrc, int nsizec) | ||
608 | { | ||
609 | struct reg_property *rp; | ||
610 | struct address_range *adr; | ||
611 | unsigned long base_address; | ||
612 | int i, l, *ip; | ||
613 | struct device_node *db; | ||
614 | |||
615 | base_address = 0; | ||
616 | for (db = np->parent; db != NULL; db = db->parent) { | ||
617 | if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { | ||
618 | base_address = db->addrs[0].address; | ||
619 | break; | ||
620 | } | ||
621 | } | ||
622 | |||
623 | rp = (struct reg_property *) get_property(np, "reg", &l); | ||
624 | if (rp != 0 && l >= sizeof(struct reg_property)) { | ||
625 | i = 0; | ||
626 | adr = (struct address_range *) mem_start; | ||
627 | while ((l -= sizeof(struct reg_property)) >= 0) { | ||
628 | adr[i].space = 2; | ||
629 | adr[i].address = rp[i].address + base_address; | ||
630 | adr[i].size = rp[i].size; | ||
631 | ++i; | ||
632 | } | ||
633 | np->addrs = adr; | ||
634 | np->n_addrs = i; | ||
635 | mem_start += i * sizeof(struct address_range); | ||
636 | } | ||
637 | |||
638 | if (use_of_interrupt_tree) | ||
639 | return mem_start; | ||
640 | |||
641 | ip = (int *) get_property(np, "AAPL,interrupts", &l); | ||
642 | if (ip == 0) | ||
643 | ip = (int *) get_property(np, "interrupts", &l); | ||
644 | if (ip != 0) { | ||
645 | np->intrs = (struct interrupt_info *) mem_start; | ||
646 | np->n_intrs = l / sizeof(int); | ||
647 | mem_start += np->n_intrs * sizeof(struct interrupt_info); | ||
648 | for (i = 0; i < np->n_intrs; ++i) { | ||
649 | np->intrs[i].line = *ip++; | ||
650 | np->intrs[i].sense = 1; | ||
651 | } | ||
652 | } | ||
653 | |||
654 | return mem_start; | ||
655 | } | ||
656 | |||
657 | static unsigned long __init | ||
658 | interpret_macio_props(struct device_node *np, unsigned long mem_start, | ||
659 | int naddrc, int nsizec) | ||
660 | { | ||
661 | struct reg_property *rp; | ||
662 | struct address_range *adr; | ||
663 | unsigned long base_address; | ||
664 | int i, l, *ip; | ||
665 | struct device_node *db; | ||
666 | |||
667 | base_address = 0; | ||
668 | for (db = np->parent; db != NULL; db = db->parent) { | ||
669 | if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { | ||
670 | base_address = db->addrs[0].address; | ||
671 | break; | ||
672 | } | ||
673 | } | ||
674 | |||
675 | rp = (struct reg_property *) get_property(np, "reg", &l); | ||
676 | if (rp != 0 && l >= sizeof(struct reg_property)) { | ||
677 | i = 0; | ||
678 | adr = (struct address_range *) mem_start; | ||
679 | while ((l -= sizeof(struct reg_property)) >= 0) { | ||
680 | adr[i].space = 2; | ||
681 | adr[i].address = rp[i].address + base_address; | ||
682 | adr[i].size = rp[i].size; | ||
683 | ++i; | ||
684 | } | ||
685 | np->addrs = adr; | ||
686 | np->n_addrs = i; | ||
687 | mem_start += i * sizeof(struct address_range); | ||
688 | } | ||
689 | |||
690 | if (use_of_interrupt_tree) | ||
691 | return mem_start; | ||
692 | |||
693 | ip = (int *) get_property(np, "interrupts", &l); | ||
694 | if (ip == 0) | ||
695 | ip = (int *) get_property(np, "AAPL,interrupts", &l); | ||
696 | if (ip != 0) { | ||
697 | np->intrs = (struct interrupt_info *) mem_start; | ||
698 | np->n_intrs = l / sizeof(int); | ||
699 | for (i = 0; i < np->n_intrs; ++i) { | ||
700 | np->intrs[i].line = *ip++; | ||
701 | np->intrs[i].sense = 1; | ||
702 | } | ||
703 | mem_start += np->n_intrs * sizeof(struct interrupt_info); | ||
704 | } | ||
705 | |||
706 | return mem_start; | ||
707 | } | ||
708 | |||
709 | static unsigned long __init | ||
710 | interpret_isa_props(struct device_node *np, unsigned long mem_start, | ||
711 | int naddrc, int nsizec) | ||
712 | { | ||
713 | struct isa_reg_property *rp; | ||
714 | struct address_range *adr; | ||
715 | int i, l, *ip; | ||
716 | |||
717 | rp = (struct isa_reg_property *) get_property(np, "reg", &l); | ||
718 | if (rp != 0 && l >= sizeof(struct isa_reg_property)) { | ||
719 | i = 0; | ||
720 | adr = (struct address_range *) mem_start; | ||
721 | while ((l -= sizeof(struct reg_property)) >= 0) { | ||
722 | adr[i].space = rp[i].space; | ||
723 | adr[i].address = rp[i].address | ||
724 | + (adr[i].space? 0: _ISA_MEM_BASE); | ||
725 | adr[i].size = rp[i].size; | ||
726 | ++i; | ||
727 | } | ||
728 | np->addrs = adr; | ||
729 | np->n_addrs = i; | ||
730 | mem_start += i * sizeof(struct address_range); | ||
731 | } | ||
732 | |||
733 | if (use_of_interrupt_tree) | ||
734 | return mem_start; | ||
735 | |||
736 | ip = (int *) get_property(np, "interrupts", &l); | ||
737 | if (ip != 0) { | ||
738 | np->intrs = (struct interrupt_info *) mem_start; | ||
739 | np->n_intrs = l / (2 * sizeof(int)); | ||
740 | mem_start += np->n_intrs * sizeof(struct interrupt_info); | ||
741 | for (i = 0; i < np->n_intrs; ++i) { | ||
742 | np->intrs[i].line = *ip++; | ||
743 | np->intrs[i].sense = *ip++; | ||
744 | } | ||
745 | } | ||
746 | |||
747 | return mem_start; | ||
748 | } | ||
749 | |||
750 | static unsigned long __init | ||
751 | interpret_root_props(struct device_node *np, unsigned long mem_start, | ||
752 | int naddrc, int nsizec) | ||
753 | { | ||
754 | struct address_range *adr; | ||
755 | int i, l, *ip; | ||
756 | unsigned int *rp; | ||
757 | int rpsize = (naddrc + nsizec) * sizeof(unsigned int); | ||
758 | |||
759 | rp = (unsigned int *) get_property(np, "reg", &l); | ||
760 | if (rp != 0 && l >= rpsize) { | ||
761 | i = 0; | ||
762 | adr = (struct address_range *) mem_start; | ||
763 | while ((l -= rpsize) >= 0) { | ||
764 | adr[i].space = (naddrc >= 2? rp[naddrc-2]: 2); | ||
765 | adr[i].address = rp[naddrc - 1]; | ||
766 | adr[i].size = rp[naddrc + nsizec - 1]; | ||
767 | ++i; | ||
768 | rp += naddrc + nsizec; | ||
769 | } | ||
770 | np->addrs = adr; | ||
771 | np->n_addrs = i; | ||
772 | mem_start += i * sizeof(struct address_range); | ||
773 | } | ||
774 | |||
775 | if (use_of_interrupt_tree) | ||
776 | return mem_start; | ||
777 | |||
778 | ip = (int *) get_property(np, "AAPL,interrupts", &l); | ||
779 | if (ip == 0) | ||
780 | ip = (int *) get_property(np, "interrupts", &l); | ||
781 | if (ip != 0) { | ||
782 | np->intrs = (struct interrupt_info *) mem_start; | ||
783 | np->n_intrs = l / sizeof(int); | ||
784 | mem_start += np->n_intrs * sizeof(struct interrupt_info); | ||
785 | for (i = 0; i < np->n_intrs; ++i) { | ||
786 | np->intrs[i].line = *ip++; | ||
787 | np->intrs[i].sense = 1; | ||
788 | } | ||
789 | } | ||
790 | |||
791 | return mem_start; | ||
792 | } | ||
793 | |||
794 | /* | ||
795 | * Work out the sense (active-low level / active-high edge) | ||
796 | * of each interrupt from the device tree. | ||
797 | */ | ||
798 | void __init | ||
799 | prom_get_irq_senses(unsigned char *senses, int off, int max) | ||
800 | { | ||
801 | struct device_node *np; | ||
802 | int i, j; | ||
803 | |||
804 | /* default to level-triggered */ | ||
805 | memset(senses, 1, max - off); | ||
806 | if (!use_of_interrupt_tree) | ||
807 | return; | ||
808 | |||
809 | for (np = allnodes; np != 0; np = np->allnext) { | ||
810 | for (j = 0; j < np->n_intrs; j++) { | ||
811 | i = np->intrs[j].line; | ||
812 | if (i >= off && i < max) { | ||
813 | if (np->intrs[j].sense == 1) | ||
814 | senses[i-off] = (IRQ_SENSE_LEVEL | ||
815 | | IRQ_POLARITY_NEGATIVE); | ||
816 | else | ||
817 | senses[i-off] = (IRQ_SENSE_EDGE | ||
818 | | IRQ_POLARITY_POSITIVE); | ||
819 | } | ||
820 | } | ||
821 | } | ||
822 | } | ||
823 | |||
824 | /* | ||
825 | * Construct and return a list of the device_nodes with a given name. | ||
826 | */ | ||
827 | struct device_node * | ||
828 | find_devices(const char *name) | ||
829 | { | ||
830 | struct device_node *head, **prevp, *np; | ||
831 | |||
832 | prevp = &head; | ||
833 | for (np = allnodes; np != 0; np = np->allnext) { | ||
834 | if (np->name != 0 && strcasecmp(np->name, name) == 0) { | ||
835 | *prevp = np; | ||
836 | prevp = &np->next; | ||
837 | } | ||
838 | } | ||
839 | *prevp = NULL; | ||
840 | return head; | ||
841 | } | ||
842 | |||
843 | /* | ||
844 | * Construct and return a list of the device_nodes with a given type. | ||
845 | */ | ||
846 | struct device_node * | ||
847 | find_type_devices(const char *type) | ||
848 | { | ||
849 | struct device_node *head, **prevp, *np; | ||
850 | |||
851 | prevp = &head; | ||
852 | for (np = allnodes; np != 0; np = np->allnext) { | ||
853 | if (np->type != 0 && strcasecmp(np->type, type) == 0) { | ||
854 | *prevp = np; | ||
855 | prevp = &np->next; | ||
856 | } | ||
857 | } | ||
858 | *prevp = NULL; | ||
859 | return head; | ||
860 | } | ||
861 | |||
862 | /* | ||
863 | * Returns all nodes linked together | ||
864 | */ | ||
865 | struct device_node * __openfirmware | ||
866 | find_all_nodes(void) | ||
867 | { | ||
868 | struct device_node *head, **prevp, *np; | ||
869 | |||
870 | prevp = &head; | ||
871 | for (np = allnodes; np != 0; np = np->allnext) { | ||
872 | *prevp = np; | ||
873 | prevp = &np->next; | ||
874 | } | ||
875 | *prevp = NULL; | ||
876 | return head; | ||
877 | } | ||
878 | |||
879 | /* Checks if the given "compat" string matches one of the strings in | ||
880 | * the device's "compatible" property | ||
881 | */ | ||
882 | int | ||
883 | device_is_compatible(struct device_node *device, const char *compat) | ||
884 | { | ||
885 | const char* cp; | ||
886 | int cplen, l; | ||
887 | |||
888 | cp = (char *) get_property(device, "compatible", &cplen); | ||
889 | if (cp == NULL) | ||
890 | return 0; | ||
891 | while (cplen > 0) { | ||
892 | if (strncasecmp(cp, compat, strlen(compat)) == 0) | ||
893 | return 1; | ||
894 | l = strlen(cp) + 1; | ||
895 | cp += l; | ||
896 | cplen -= l; | ||
897 | } | ||
898 | |||
899 | return 0; | ||
900 | } | ||
901 | |||
902 | |||
903 | /* | ||
904 | * Indicates whether the root node has a given value in its | ||
905 | * compatible property. | ||
906 | */ | ||
907 | int | ||
908 | machine_is_compatible(const char *compat) | ||
909 | { | ||
910 | struct device_node *root; | ||
911 | |||
912 | root = find_path_device("/"); | ||
913 | if (root == 0) | ||
914 | return 0; | ||
915 | return device_is_compatible(root, compat); | ||
916 | } | ||
917 | |||
918 | /* | ||
919 | * Construct and return a list of the device_nodes with a given type | ||
920 | * and compatible property. | ||
921 | */ | ||
922 | struct device_node * | ||
923 | find_compatible_devices(const char *type, const char *compat) | ||
924 | { | ||
925 | struct device_node *head, **prevp, *np; | ||
926 | |||
927 | prevp = &head; | ||
928 | for (np = allnodes; np != 0; np = np->allnext) { | ||
929 | if (type != NULL | ||
930 | && !(np->type != 0 && strcasecmp(np->type, type) == 0)) | ||
931 | continue; | ||
932 | if (device_is_compatible(np, compat)) { | ||
933 | *prevp = np; | ||
934 | prevp = &np->next; | ||
935 | } | ||
936 | } | ||
937 | *prevp = NULL; | ||
938 | return head; | ||
939 | } | ||
940 | |||
941 | /* | ||
942 | * Find the device_node with a given full_name. | ||
943 | */ | ||
944 | struct device_node * | ||
945 | find_path_device(const char *path) | ||
946 | { | ||
947 | struct device_node *np; | ||
948 | |||
949 | for (np = allnodes; np != 0; np = np->allnext) | ||
950 | if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) | ||
951 | return np; | ||
952 | return NULL; | ||
953 | } | ||
954 | |||
955 | /******* | ||
956 | * | ||
957 | * New implementation of the OF "find" APIs, return a refcounted | ||
958 | * object, call of_node_put() when done. Currently, still lacks | ||
959 | * locking as old implementation, this is beeing done for ppc64. | ||
960 | * | ||
961 | * Note that property management will need some locking as well, | ||
962 | * this isn't dealt with yet | ||
963 | * | ||
964 | *******/ | ||
965 | |||
966 | /** | ||
967 | * of_find_node_by_name - Find a node by it's "name" property | ||
968 | * @from: The node to start searching from or NULL, the node | ||
969 | * you pass will not be searched, only the next one | ||
970 | * will; typically, you pass what the previous call | ||
971 | * returned. of_node_put() will be called on it | ||
972 | * @name: The name string to match against | ||
973 | * | ||
974 | * Returns a node pointer with refcount incremented, use | ||
975 | * of_node_put() on it when done. | ||
976 | */ | ||
977 | struct device_node *of_find_node_by_name(struct device_node *from, | ||
978 | const char *name) | ||
979 | { | ||
980 | struct device_node *np = from ? from->allnext : allnodes; | ||
981 | |||
982 | for (; np != 0; np = np->allnext) | ||
983 | if (np->name != 0 && strcasecmp(np->name, name) == 0) | ||
984 | break; | ||
985 | if (from) | ||
986 | of_node_put(from); | ||
987 | return of_node_get(np); | ||
988 | } | ||
989 | |||
990 | /** | ||
991 | * of_find_node_by_type - Find a node by it's "device_type" property | ||
992 | * @from: The node to start searching from or NULL, the node | ||
993 | * you pass will not be searched, only the next one | ||
994 | * will; typically, you pass what the previous call | ||
995 | * returned. of_node_put() will be called on it | ||
996 | * @name: The type string to match against | ||
997 | * | ||
998 | * Returns a node pointer with refcount incremented, use | ||
999 | * of_node_put() on it when done. | ||
1000 | */ | ||
1001 | struct device_node *of_find_node_by_type(struct device_node *from, | ||
1002 | const char *type) | ||
1003 | { | ||
1004 | struct device_node *np = from ? from->allnext : allnodes; | ||
1005 | |||
1006 | for (; np != 0; np = np->allnext) | ||
1007 | if (np->type != 0 && strcasecmp(np->type, type) == 0) | ||
1008 | break; | ||
1009 | if (from) | ||
1010 | of_node_put(from); | ||
1011 | return of_node_get(np); | ||
1012 | } | ||
1013 | |||
1014 | /** | ||
1015 | * of_find_compatible_node - Find a node based on type and one of the | ||
1016 | * tokens in it's "compatible" property | ||
1017 | * @from: The node to start searching from or NULL, the node | ||
1018 | * you pass will not be searched, only the next one | ||
1019 | * will; typically, you pass what the previous call | ||
1020 | * returned. of_node_put() will be called on it | ||
1021 | * @type: The type string to match "device_type" or NULL to ignore | ||
1022 | * @compatible: The string to match to one of the tokens in the device | ||
1023 | * "compatible" list. | ||
1024 | * | ||
1025 | * Returns a node pointer with refcount incremented, use | ||
1026 | * of_node_put() on it when done. | ||
1027 | */ | ||
1028 | struct device_node *of_find_compatible_node(struct device_node *from, | ||
1029 | const char *type, const char *compatible) | ||
1030 | { | ||
1031 | struct device_node *np = from ? from->allnext : allnodes; | ||
1032 | |||
1033 | for (; np != 0; np = np->allnext) { | ||
1034 | if (type != NULL | ||
1035 | && !(np->type != 0 && strcasecmp(np->type, type) == 0)) | ||
1036 | continue; | ||
1037 | if (device_is_compatible(np, compatible)) | ||
1038 | break; | ||
1039 | } | ||
1040 | if (from) | ||
1041 | of_node_put(from); | ||
1042 | return of_node_get(np); | ||
1043 | } | ||
1044 | |||
1045 | /** | ||
1046 | * of_find_node_by_path - Find a node matching a full OF path | ||
1047 | * @path: The full path to match | ||
1048 | * | ||
1049 | * Returns a node pointer with refcount incremented, use | ||
1050 | * of_node_put() on it when done. | ||
1051 | */ | ||
1052 | struct device_node *of_find_node_by_path(const char *path) | ||
1053 | { | ||
1054 | struct device_node *np = allnodes; | ||
1055 | |||
1056 | for (; np != 0; np = np->allnext) | ||
1057 | if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0) | ||
1058 | break; | ||
1059 | return of_node_get(np); | ||
1060 | } | ||
1061 | |||
1062 | /** | ||
1063 | * of_find_all_nodes - Get next node in global list | ||
1064 | * @prev: Previous node or NULL to start iteration | ||
1065 | * of_node_put() will be called on it | ||
1066 | * | ||
1067 | * Returns a node pointer with refcount incremented, use | ||
1068 | * of_node_put() on it when done. | ||
1069 | */ | ||
1070 | struct device_node *of_find_all_nodes(struct device_node *prev) | ||
1071 | { | ||
1072 | return of_node_get(prev ? prev->allnext : allnodes); | ||
1073 | } | ||
1074 | |||
1075 | /** | ||
1076 | * of_get_parent - Get a node's parent if any | ||
1077 | * @node: Node to get parent | ||
1078 | * | ||
1079 | * Returns a node pointer with refcount incremented, use | ||
1080 | * of_node_put() on it when done. | ||
1081 | */ | ||
1082 | struct device_node *of_get_parent(const struct device_node *node) | ||
1083 | { | ||
1084 | return node ? of_node_get(node->parent) : NULL; | ||
1085 | } | ||
1086 | |||
1087 | /** | ||
1088 | * of_get_next_child - Iterate a node childs | ||
1089 | * @node: parent node | ||
1090 | * @prev: previous child of the parent node, or NULL to get first | ||
1091 | * | ||
1092 | * Returns a node pointer with refcount incremented, use | ||
1093 | * of_node_put() on it when done. | ||
1094 | */ | ||
1095 | struct device_node *of_get_next_child(const struct device_node *node, | ||
1096 | struct device_node *prev) | ||
1097 | { | ||
1098 | struct device_node *next = prev ? prev->sibling : node->child; | ||
1099 | |||
1100 | for (; next != 0; next = next->sibling) | ||
1101 | if (of_node_get(next)) | ||
1102 | break; | ||
1103 | if (prev) | ||
1104 | of_node_put(prev); | ||
1105 | return next; | ||
1106 | } | ||
1107 | |||
1108 | /** | ||
1109 | * of_node_get - Increment refcount of a node | ||
1110 | * @node: Node to inc refcount, NULL is supported to | ||
1111 | * simplify writing of callers | ||
1112 | * | ||
1113 | * Returns the node itself or NULL if gone. Current implementation | ||
1114 | * does nothing as we don't yet do dynamic node allocation on ppc32 | ||
1115 | */ | ||
1116 | struct device_node *of_node_get(struct device_node *node) | ||
1117 | { | ||
1118 | return node; | ||
1119 | } | ||
1120 | |||
1121 | /** | ||
1122 | * of_node_put - Decrement refcount of a node | ||
1123 | * @node: Node to dec refcount, NULL is supported to | ||
1124 | * simplify writing of callers | ||
1125 | * | ||
1126 | * Current implementation does nothing as we don't yet do dynamic node | ||
1127 | * allocation on ppc32 | ||
1128 | */ | ||
1129 | void of_node_put(struct device_node *node) | ||
1130 | { | ||
1131 | } | ||
1132 | |||
1133 | /* | ||
1134 | * Find the device_node with a given phandle. | ||
1135 | */ | ||
1136 | static struct device_node * __init | ||
1137 | find_phandle(phandle ph) | ||
1138 | { | ||
1139 | struct device_node *np; | ||
1140 | |||
1141 | for (np = allnodes; np != 0; np = np->allnext) | ||
1142 | if (np->node == ph) | ||
1143 | return np; | ||
1144 | return NULL; | ||
1145 | } | ||
1146 | |||
1147 | /* | ||
1148 | * Find a property with a given name for a given node | ||
1149 | * and return the value. | ||
1150 | */ | ||
1151 | unsigned char * | ||
1152 | get_property(struct device_node *np, const char *name, int *lenp) | ||
1153 | { | ||
1154 | struct property *pp; | ||
1155 | |||
1156 | for (pp = np->properties; pp != 0; pp = pp->next) | ||
1157 | if (pp->name != NULL && strcmp(pp->name, name) == 0) { | ||
1158 | if (lenp != 0) | ||
1159 | *lenp = pp->length; | ||
1160 | return pp->value; | ||
1161 | } | ||
1162 | return NULL; | ||
1163 | } | ||
1164 | |||
1165 | /* | ||
1166 | * Add a property to a node | ||
1167 | */ | ||
1168 | void __openfirmware | ||
1169 | prom_add_property(struct device_node* np, struct property* prop) | ||
1170 | { | ||
1171 | struct property **next = &np->properties; | ||
1172 | |||
1173 | prop->next = NULL; | ||
1174 | while (*next) | ||
1175 | next = &(*next)->next; | ||
1176 | *next = prop; | ||
1177 | } | ||
1178 | |||
1179 | /* I quickly hacked that one, check against spec ! */ | ||
1180 | static inline unsigned long __openfirmware | ||
1181 | bus_space_to_resource_flags(unsigned int bus_space) | ||
1182 | { | ||
1183 | u8 space = (bus_space >> 24) & 0xf; | ||
1184 | if (space == 0) | ||
1185 | space = 0x02; | ||
1186 | if (space == 0x02) | ||
1187 | return IORESOURCE_MEM; | ||
1188 | else if (space == 0x01) | ||
1189 | return IORESOURCE_IO; | ||
1190 | else { | ||
1191 | printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n", | ||
1192 | bus_space); | ||
1193 | return 0; | ||
1194 | } | ||
1195 | } | ||
1196 | |||
1197 | static struct resource* __openfirmware | ||
1198 | find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range) | ||
1199 | { | ||
1200 | unsigned long mask; | ||
1201 | int i; | ||
1202 | |||
1203 | /* Check this one */ | ||
1204 | mask = bus_space_to_resource_flags(range->space); | ||
1205 | for (i=0; i<DEVICE_COUNT_RESOURCE; i++) { | ||
1206 | if ((pdev->resource[i].flags & mask) == mask && | ||
1207 | pdev->resource[i].start <= range->address && | ||
1208 | pdev->resource[i].end > range->address) { | ||
1209 | if ((range->address + range->size - 1) > pdev->resource[i].end) { | ||
1210 | /* Add better message */ | ||
1211 | printk(KERN_WARNING "PCI/OF resource overlap !\n"); | ||
1212 | return NULL; | ||
1213 | } | ||
1214 | break; | ||
1215 | } | ||
1216 | } | ||
1217 | if (i == DEVICE_COUNT_RESOURCE) | ||
1218 | return NULL; | ||
1219 | return &pdev->resource[i]; | ||
1220 | } | ||
1221 | |||
1222 | /* | ||
1223 | * Request an OF device resource. Currently handles child of PCI devices, | ||
1224 | * or other nodes attached to the root node. Ultimately, put some | ||
1225 | * link to resources in the OF node. | ||
1226 | */ | ||
1227 | struct resource* __openfirmware | ||
1228 | request_OF_resource(struct device_node* node, int index, const char* name_postfix) | ||
1229 | { | ||
1230 | struct pci_dev* pcidev; | ||
1231 | u8 pci_bus, pci_devfn; | ||
1232 | unsigned long iomask; | ||
1233 | struct device_node* nd; | ||
1234 | struct resource* parent; | ||
1235 | struct resource *res = NULL; | ||
1236 | int nlen, plen; | ||
1237 | |||
1238 | if (index >= node->n_addrs) | ||
1239 | goto fail; | ||
1240 | |||
1241 | /* Sanity check on bus space */ | ||
1242 | iomask = bus_space_to_resource_flags(node->addrs[index].space); | ||
1243 | if (iomask & IORESOURCE_MEM) | ||
1244 | parent = &iomem_resource; | ||
1245 | else if (iomask & IORESOURCE_IO) | ||
1246 | parent = &ioport_resource; | ||
1247 | else | ||
1248 | goto fail; | ||
1249 | |||
1250 | /* Find a PCI parent if any */ | ||
1251 | nd = node; | ||
1252 | pcidev = NULL; | ||
1253 | while(nd) { | ||
1254 | if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) | ||
1255 | pcidev = pci_find_slot(pci_bus, pci_devfn); | ||
1256 | if (pcidev) break; | ||
1257 | nd = nd->parent; | ||
1258 | } | ||
1259 | if (pcidev) | ||
1260 | parent = find_parent_pci_resource(pcidev, &node->addrs[index]); | ||
1261 | if (!parent) { | ||
1262 | printk(KERN_WARNING "request_OF_resource(%s), parent not found\n", | ||
1263 | node->name); | ||
1264 | goto fail; | ||
1265 | } | ||
1266 | |||
1267 | res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL); | ||
1268 | if (!res) | ||
1269 | goto fail; | ||
1270 | nlen = strlen(node->name); | ||
1271 | plen = name_postfix ? strlen(name_postfix) : 0; | ||
1272 | res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL); | ||
1273 | if (res->name) { | ||
1274 | strcpy((char *)res->name, node->name); | ||
1275 | if (plen) | ||
1276 | strcpy((char *)res->name+nlen, name_postfix); | ||
1277 | } | ||
1278 | return res; | ||
1279 | fail: | ||
1280 | return NULL; | ||
1281 | } | ||
1282 | |||
1283 | int __openfirmware | ||
1284 | release_OF_resource(struct device_node* node, int index) | ||
1285 | { | ||
1286 | struct pci_dev* pcidev; | ||
1287 | u8 pci_bus, pci_devfn; | ||
1288 | unsigned long iomask, start, end; | ||
1289 | struct device_node* nd; | ||
1290 | struct resource* parent; | ||
1291 | struct resource *res = NULL; | ||
1292 | |||
1293 | if (index >= node->n_addrs) | ||
1294 | return -EINVAL; | ||
1295 | |||
1296 | /* Sanity check on bus space */ | ||
1297 | iomask = bus_space_to_resource_flags(node->addrs[index].space); | ||
1298 | if (iomask & IORESOURCE_MEM) | ||
1299 | parent = &iomem_resource; | ||
1300 | else if (iomask & IORESOURCE_IO) | ||
1301 | parent = &ioport_resource; | ||
1302 | else | ||
1303 | return -EINVAL; | ||
1304 | |||
1305 | /* Find a PCI parent if any */ | ||
1306 | nd = node; | ||
1307 | pcidev = NULL; | ||
1308 | while(nd) { | ||
1309 | if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn)) | ||
1310 | pcidev = pci_find_slot(pci_bus, pci_devfn); | ||
1311 | if (pcidev) break; | ||
1312 | nd = nd->parent; | ||
1313 | } | ||
1314 | if (pcidev) | ||
1315 | parent = find_parent_pci_resource(pcidev, &node->addrs[index]); | ||
1316 | if (!parent) { | ||
1317 | printk(KERN_WARNING "release_OF_resource(%s), parent not found\n", | ||
1318 | node->name); | ||
1319 | return -ENODEV; | ||
1320 | } | ||
1321 | |||
1322 | /* Find us in the parent and its childs */ | ||
1323 | res = parent->child; | ||
1324 | start = node->addrs[index].address; | ||
1325 | end = start + node->addrs[index].size - 1; | ||
1326 | while (res) { | ||
1327 | if (res->start == start && res->end == end && | ||
1328 | (res->flags & IORESOURCE_BUSY)) | ||
1329 | break; | ||
1330 | if (res->start <= start && res->end >= end) | ||
1331 | res = res->child; | ||
1332 | else | ||
1333 | res = res->sibling; | ||
1334 | } | ||
1335 | if (!res) | ||
1336 | return -ENODEV; | ||
1337 | |||
1338 | if (res->name) { | ||
1339 | kfree(res->name); | ||
1340 | res->name = NULL; | ||
1341 | } | ||
1342 | release_resource(res); | ||
1343 | kfree(res); | ||
1344 | |||
1345 | return 0; | ||
1346 | } | ||
1347 | |||
1348 | #if 0 | ||
1349 | void __openfirmware | ||
1350 | print_properties(struct device_node *np) | ||
1351 | { | ||
1352 | struct property *pp; | ||
1353 | char *cp; | ||
1354 | int i, n; | ||
1355 | |||
1356 | for (pp = np->properties; pp != 0; pp = pp->next) { | ||
1357 | printk(KERN_INFO "%s", pp->name); | ||
1358 | for (i = strlen(pp->name); i < 16; ++i) | ||
1359 | printk(" "); | ||
1360 | cp = (char *) pp->value; | ||
1361 | for (i = pp->length; i > 0; --i, ++cp) | ||
1362 | if ((i > 1 && (*cp < 0x20 || *cp > 0x7e)) | ||
1363 | || (i == 1 && *cp != 0)) | ||
1364 | break; | ||
1365 | if (i == 0 && pp->length > 1) { | ||
1366 | /* looks like a string */ | ||
1367 | printk(" %s\n", (char *) pp->value); | ||
1368 | } else { | ||
1369 | /* dump it in hex */ | ||
1370 | n = pp->length; | ||
1371 | if (n > 64) | ||
1372 | n = 64; | ||
1373 | if (pp->length % 4 == 0) { | ||
1374 | unsigned int *p = (unsigned int *) pp->value; | ||
1375 | |||
1376 | n /= 4; | ||
1377 | for (i = 0; i < n; ++i) { | ||
1378 | if (i != 0 && (i % 4) == 0) | ||
1379 | printk("\n "); | ||
1380 | printk(" %08x", *p++); | ||
1381 | } | ||
1382 | } else { | ||
1383 | unsigned char *bp = pp->value; | ||
1384 | |||
1385 | for (i = 0; i < n; ++i) { | ||
1386 | if (i != 0 && (i % 16) == 0) | ||
1387 | printk("\n "); | ||
1388 | printk(" %02x", *bp++); | ||
1389 | } | ||
1390 | } | ||
1391 | printk("\n"); | ||
1392 | if (pp->length > 64) | ||
1393 | printk(" ... (length = %d)\n", | ||
1394 | pp->length); | ||
1395 | } | ||
1396 | } | ||
1397 | } | ||
1398 | #endif | ||
1399 | |||
1400 | static DEFINE_SPINLOCK(rtas_lock); | ||
1401 | |||
1402 | /* this can be called after setup -- Cort */ | ||
1403 | int __openfirmware | ||
1404 | call_rtas(const char *service, int nargs, int nret, | ||
1405 | unsigned long *outputs, ...) | ||
1406 | { | ||
1407 | va_list list; | ||
1408 | int i; | ||
1409 | unsigned long s; | ||
1410 | struct device_node *rtas; | ||
1411 | int *tokp; | ||
1412 | union { | ||
1413 | unsigned long words[16]; | ||
1414 | double align; | ||
1415 | } u; | ||
1416 | |||
1417 | rtas = find_devices("rtas"); | ||
1418 | if (rtas == NULL) | ||
1419 | return -1; | ||
1420 | tokp = (int *) get_property(rtas, service, NULL); | ||
1421 | if (tokp == NULL) { | ||
1422 | printk(KERN_ERR "No RTAS service called %s\n", service); | ||
1423 | return -1; | ||
1424 | } | ||
1425 | u.words[0] = *tokp; | ||
1426 | u.words[1] = nargs; | ||
1427 | u.words[2] = nret; | ||
1428 | va_start(list, outputs); | ||
1429 | for (i = 0; i < nargs; ++i) | ||
1430 | u.words[i+3] = va_arg(list, unsigned long); | ||
1431 | va_end(list); | ||
1432 | |||
1433 | /* | ||
1434 | * RTAS doesn't use floating point. | ||
1435 | * Or at least, according to the CHRP spec we enter RTAS | ||
1436 | * with FP disabled, and it doesn't change the FP registers. | ||
1437 | * -- paulus. | ||
1438 | */ | ||
1439 | spin_lock_irqsave(&rtas_lock, s); | ||
1440 | enter_rtas((void *)__pa(&u)); | ||
1441 | spin_unlock_irqrestore(&rtas_lock, s); | ||
1442 | |||
1443 | if (nret > 1 && outputs != NULL) | ||
1444 | for (i = 0; i < nret-1; ++i) | ||
1445 | outputs[i] = u.words[i+nargs+4]; | ||
1446 | return u.words[nargs+3]; | ||
1447 | } | ||
diff --git a/arch/ppc/syslib/prom_init.c b/arch/ppc/syslib/prom_init.c new file mode 100644 index 000000000000..2cee87137f2e --- /dev/null +++ b/arch/ppc/syslib/prom_init.c | |||
@@ -0,0 +1,1002 @@ | |||
1 | /* | ||
2 | * Note that prom_init() and anything called from prom_init() | ||
3 | * may be running at an address that is different from the address | ||
4 | * that it was linked at. References to static data items are | ||
5 | * handled by compiling this file with -mrelocatable-lib. | ||
6 | */ | ||
7 | |||
8 | #include <linux/config.h> | ||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/string.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/version.h> | ||
13 | #include <linux/threads.h> | ||
14 | #include <linux/spinlock.h> | ||
15 | #include <linux/ioport.h> | ||
16 | #include <linux/pci.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/bitops.h> | ||
19 | |||
20 | #include <asm/sections.h> | ||
21 | #include <asm/prom.h> | ||
22 | #include <asm/page.h> | ||
23 | #include <asm/irq.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/smp.h> | ||
26 | #include <asm/bootx.h> | ||
27 | #include <asm/system.h> | ||
28 | #include <asm/mmu.h> | ||
29 | #include <asm/pgtable.h> | ||
30 | #include <asm/bootinfo.h> | ||
31 | #include <asm/btext.h> | ||
32 | #include <asm/pci-bridge.h> | ||
33 | #include <asm/open_pic.h> | ||
34 | #include <asm/cacheflush.h> | ||
35 | |||
36 | #ifdef CONFIG_LOGO_LINUX_CLUT224 | ||
37 | #include <linux/linux_logo.h> | ||
38 | extern const struct linux_logo logo_linux_clut224; | ||
39 | #endif | ||
40 | |||
41 | /* | ||
42 | * Properties whose value is longer than this get excluded from our | ||
43 | * copy of the device tree. This way we don't waste space storing | ||
44 | * things like "driver,AAPL,MacOS,PowerPC" properties. But this value | ||
45 | * does need to be big enough to ensure that we don't lose things | ||
46 | * like the interrupt-map property on a PCI-PCI bridge. | ||
47 | */ | ||
48 | #define MAX_PROPERTY_LENGTH 4096 | ||
49 | |||
50 | #ifndef FB_MAX /* avoid pulling in all of the fb stuff */ | ||
51 | #define FB_MAX 8 | ||
52 | #endif | ||
53 | |||
54 | #define ALIGNUL(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long)) | ||
55 | |||
56 | typedef u32 prom_arg_t; | ||
57 | |||
58 | struct prom_args { | ||
59 | const char *service; | ||
60 | int nargs; | ||
61 | int nret; | ||
62 | prom_arg_t args[10]; | ||
63 | }; | ||
64 | |||
65 | struct pci_address { | ||
66 | unsigned a_hi; | ||
67 | unsigned a_mid; | ||
68 | unsigned a_lo; | ||
69 | }; | ||
70 | |||
71 | struct pci_reg_property { | ||
72 | struct pci_address addr; | ||
73 | unsigned size_hi; | ||
74 | unsigned size_lo; | ||
75 | }; | ||
76 | |||
77 | struct pci_range { | ||
78 | struct pci_address addr; | ||
79 | unsigned phys; | ||
80 | unsigned size_hi; | ||
81 | unsigned size_lo; | ||
82 | }; | ||
83 | |||
84 | struct isa_reg_property { | ||
85 | unsigned space; | ||
86 | unsigned address; | ||
87 | unsigned size; | ||
88 | }; | ||
89 | |||
90 | struct pci_intr_map { | ||
91 | struct pci_address addr; | ||
92 | unsigned dunno; | ||
93 | phandle int_ctrler; | ||
94 | unsigned intr; | ||
95 | }; | ||
96 | |||
97 | static void prom_exit(void); | ||
98 | static int call_prom(const char *service, int nargs, int nret, ...); | ||
99 | static int call_prom_ret(const char *service, int nargs, int nret, | ||
100 | prom_arg_t *rets, ...); | ||
101 | static void prom_print_hex(unsigned int v); | ||
102 | static int prom_set_color(ihandle ih, int i, int r, int g, int b); | ||
103 | static int prom_next_node(phandle *nodep); | ||
104 | static unsigned long check_display(unsigned long mem); | ||
105 | static void setup_disp_fake_bi(ihandle dp); | ||
106 | static unsigned long copy_device_tree(unsigned long mem_start, | ||
107 | unsigned long mem_end); | ||
108 | static unsigned long inspect_node(phandle node, struct device_node *dad, | ||
109 | unsigned long mem_start, unsigned long mem_end, | ||
110 | struct device_node ***allnextpp); | ||
111 | static void prom_hold_cpus(unsigned long mem); | ||
112 | static void prom_instantiate_rtas(void); | ||
113 | static void * early_get_property(unsigned long base, unsigned long node, | ||
114 | char *prop); | ||
115 | |||
116 | prom_entry prom __initdata; | ||
117 | ihandle prom_chosen __initdata; | ||
118 | ihandle prom_stdout __initdata; | ||
119 | |||
120 | static char *prom_display_paths[FB_MAX] __initdata; | ||
121 | static phandle prom_display_nodes[FB_MAX] __initdata; | ||
122 | static unsigned int prom_num_displays __initdata; | ||
123 | static ihandle prom_disp_node __initdata; | ||
124 | char *of_stdout_device __initdata; | ||
125 | |||
126 | unsigned int rtas_data; /* physical pointer */ | ||
127 | unsigned int rtas_entry; /* physical pointer */ | ||
128 | unsigned int rtas_size; | ||
129 | unsigned int old_rtas; | ||
130 | |||
131 | boot_infos_t *boot_infos; | ||
132 | char *bootpath; | ||
133 | char *bootdevice; | ||
134 | struct device_node *allnodes; | ||
135 | |||
136 | extern char *klimit; | ||
137 | |||
138 | static void __init | ||
139 | prom_exit(void) | ||
140 | { | ||
141 | struct prom_args args; | ||
142 | |||
143 | args.service = "exit"; | ||
144 | args.nargs = 0; | ||
145 | args.nret = 0; | ||
146 | prom(&args); | ||
147 | for (;;) /* should never get here */ | ||
148 | ; | ||
149 | } | ||
150 | |||
151 | static int __init | ||
152 | call_prom(const char *service, int nargs, int nret, ...) | ||
153 | { | ||
154 | va_list list; | ||
155 | int i; | ||
156 | struct prom_args prom_args; | ||
157 | |||
158 | prom_args.service = service; | ||
159 | prom_args.nargs = nargs; | ||
160 | prom_args.nret = nret; | ||
161 | va_start(list, nret); | ||
162 | for (i = 0; i < nargs; ++i) | ||
163 | prom_args.args[i] = va_arg(list, prom_arg_t); | ||
164 | va_end(list); | ||
165 | for (i = 0; i < nret; ++i) | ||
166 | prom_args.args[i + nargs] = 0; | ||
167 | prom(&prom_args); | ||
168 | return prom_args.args[nargs]; | ||
169 | } | ||
170 | |||
171 | static int __init | ||
172 | call_prom_ret(const char *service, int nargs, int nret, prom_arg_t *rets, ...) | ||
173 | { | ||
174 | va_list list; | ||
175 | int i; | ||
176 | struct prom_args prom_args; | ||
177 | |||
178 | prom_args.service = service; | ||
179 | prom_args.nargs = nargs; | ||
180 | prom_args.nret = nret; | ||
181 | va_start(list, rets); | ||
182 | for (i = 0; i < nargs; ++i) | ||
183 | prom_args.args[i] = va_arg(list, int); | ||
184 | va_end(list); | ||
185 | for (i = 0; i < nret; ++i) | ||
186 | prom_args.args[i + nargs] = 0; | ||
187 | prom(&prom_args); | ||
188 | for (i = 1; i < nret; ++i) | ||
189 | rets[i-1] = prom_args.args[nargs + i]; | ||
190 | return prom_args.args[nargs]; | ||
191 | } | ||
192 | |||
193 | void __init | ||
194 | prom_print(const char *msg) | ||
195 | { | ||
196 | const char *p, *q; | ||
197 | |||
198 | if (prom_stdout == 0) | ||
199 | return; | ||
200 | |||
201 | for (p = msg; *p != 0; p = q) { | ||
202 | for (q = p; *q != 0 && *q != '\n'; ++q) | ||
203 | ; | ||
204 | if (q > p) | ||
205 | call_prom("write", 3, 1, prom_stdout, p, q - p); | ||
206 | if (*q != 0) { | ||
207 | ++q; | ||
208 | call_prom("write", 3, 1, prom_stdout, "\r\n", 2); | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | static void __init | ||
214 | prom_print_hex(unsigned int v) | ||
215 | { | ||
216 | char buf[16]; | ||
217 | int i, c; | ||
218 | |||
219 | for (i = 0; i < 8; ++i) { | ||
220 | c = (v >> ((7-i)*4)) & 0xf; | ||
221 | c += (c >= 10)? ('a' - 10): '0'; | ||
222 | buf[i] = c; | ||
223 | } | ||
224 | buf[i] = ' '; | ||
225 | buf[i+1] = 0; | ||
226 | prom_print(buf); | ||
227 | } | ||
228 | |||
229 | static int __init | ||
230 | prom_set_color(ihandle ih, int i, int r, int g, int b) | ||
231 | { | ||
232 | return call_prom("call-method", 6, 1, "color!", ih, i, b, g, r); | ||
233 | } | ||
234 | |||
235 | static int __init | ||
236 | prom_next_node(phandle *nodep) | ||
237 | { | ||
238 | phandle node; | ||
239 | |||
240 | if ((node = *nodep) != 0 | ||
241 | && (*nodep = call_prom("child", 1, 1, node)) != 0) | ||
242 | return 1; | ||
243 | if ((*nodep = call_prom("peer", 1, 1, node)) != 0) | ||
244 | return 1; | ||
245 | for (;;) { | ||
246 | if ((node = call_prom("parent", 1, 1, node)) == 0) | ||
247 | return 0; | ||
248 | if ((*nodep = call_prom("peer", 1, 1, node)) != 0) | ||
249 | return 1; | ||
250 | } | ||
251 | } | ||
252 | |||
253 | #ifdef CONFIG_POWER4 | ||
254 | /* | ||
255 | * Set up a hash table with a set of entries in it to map the | ||
256 | * first 64MB of RAM. This is used on 64-bit machines since | ||
257 | * some of them don't have BATs. | ||
258 | */ | ||
259 | |||
260 | static inline void make_pte(unsigned long htab, unsigned int hsize, | ||
261 | unsigned int va, unsigned int pa, int mode) | ||
262 | { | ||
263 | unsigned int *pteg; | ||
264 | unsigned int hash, i, vsid; | ||
265 | |||
266 | vsid = ((va >> 28) * 0x111) << 12; | ||
267 | hash = ((va ^ vsid) >> 5) & 0x7fff80; | ||
268 | pteg = (unsigned int *)(htab + (hash & (hsize - 1))); | ||
269 | for (i = 0; i < 8; ++i, pteg += 4) { | ||
270 | if ((pteg[1] & 1) == 0) { | ||
271 | pteg[1] = vsid | ((va >> 16) & 0xf80) | 1; | ||
272 | pteg[3] = pa | mode; | ||
273 | break; | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | |||
278 | extern unsigned long _SDR1; | ||
279 | extern PTE *Hash; | ||
280 | extern unsigned long Hash_size; | ||
281 | |||
282 | static void __init | ||
283 | prom_alloc_htab(void) | ||
284 | { | ||
285 | unsigned int hsize; | ||
286 | unsigned long htab; | ||
287 | unsigned int addr; | ||
288 | |||
289 | /* | ||
290 | * Because of OF bugs we can't use the "claim" client | ||
291 | * interface to allocate memory for the hash table. | ||
292 | * This code is only used on 64-bit PPCs, and the only | ||
293 | * 64-bit PPCs at the moment are RS/6000s, and their | ||
294 | * OF is based at 0xc00000 (the 12M point), so we just | ||
295 | * arbitrarily use the 0x800000 - 0xc00000 region for the | ||
296 | * hash table. | ||
297 | * -- paulus. | ||
298 | */ | ||
299 | hsize = 4 << 20; /* POWER4 has no BATs */ | ||
300 | htab = (8 << 20); | ||
301 | call_prom("claim", 3, 1, htab, hsize, 0); | ||
302 | Hash = (void *)(htab + KERNELBASE); | ||
303 | Hash_size = hsize; | ||
304 | _SDR1 = htab + __ilog2(hsize) - 18; | ||
305 | |||
306 | /* | ||
307 | * Put in PTEs for the first 64MB of RAM | ||
308 | */ | ||
309 | memset((void *)htab, 0, hsize); | ||
310 | for (addr = 0; addr < 0x4000000; addr += 0x1000) | ||
311 | make_pte(htab, hsize, addr + KERNELBASE, addr, | ||
312 | _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX); | ||
313 | #if 0 /* DEBUG stuff mapping the SCC */ | ||
314 | make_pte(htab, hsize, 0x80013000, 0x80013000, | ||
315 | _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX); | ||
316 | #endif | ||
317 | } | ||
318 | #endif /* CONFIG_POWER4 */ | ||
319 | |||
320 | |||
321 | /* | ||
322 | * If we have a display that we don't know how to drive, | ||
323 | * we will want to try to execute OF's open method for it | ||
324 | * later. However, OF will probably fall over if we do that | ||
325 | * we've taken over the MMU. | ||
326 | * So we check whether we will need to open the display, | ||
327 | * and if so, open it now. | ||
328 | */ | ||
329 | static unsigned long __init | ||
330 | check_display(unsigned long mem) | ||
331 | { | ||
332 | phandle node; | ||
333 | ihandle ih; | ||
334 | int i, j; | ||
335 | char type[16], *path; | ||
336 | static unsigned char default_colors[] = { | ||
337 | 0x00, 0x00, 0x00, | ||
338 | 0x00, 0x00, 0xaa, | ||
339 | 0x00, 0xaa, 0x00, | ||
340 | 0x00, 0xaa, 0xaa, | ||
341 | 0xaa, 0x00, 0x00, | ||
342 | 0xaa, 0x00, 0xaa, | ||
343 | 0xaa, 0xaa, 0x00, | ||
344 | 0xaa, 0xaa, 0xaa, | ||
345 | 0x55, 0x55, 0x55, | ||
346 | 0x55, 0x55, 0xff, | ||
347 | 0x55, 0xff, 0x55, | ||
348 | 0x55, 0xff, 0xff, | ||
349 | 0xff, 0x55, 0x55, | ||
350 | 0xff, 0x55, 0xff, | ||
351 | 0xff, 0xff, 0x55, | ||
352 | 0xff, 0xff, 0xff | ||
353 | }; | ||
354 | const unsigned char *clut; | ||
355 | |||
356 | prom_disp_node = 0; | ||
357 | |||
358 | for (node = 0; prom_next_node(&node); ) { | ||
359 | type[0] = 0; | ||
360 | call_prom("getprop", 4, 1, node, "device_type", | ||
361 | type, sizeof(type)); | ||
362 | if (strcmp(type, "display") != 0) | ||
363 | continue; | ||
364 | /* It seems OF doesn't null-terminate the path :-( */ | ||
365 | path = (char *) mem; | ||
366 | memset(path, 0, 256); | ||
367 | if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) | ||
368 | continue; | ||
369 | |||
370 | /* | ||
371 | * If this display is the device that OF is using for stdout, | ||
372 | * move it to the front of the list. | ||
373 | */ | ||
374 | mem += strlen(path) + 1; | ||
375 | i = prom_num_displays++; | ||
376 | if (of_stdout_device != 0 && i > 0 | ||
377 | && strcmp(of_stdout_device, path) == 0) { | ||
378 | for (; i > 0; --i) { | ||
379 | prom_display_paths[i] | ||
380 | = prom_display_paths[i-1]; | ||
381 | prom_display_nodes[i] | ||
382 | = prom_display_nodes[i-1]; | ||
383 | } | ||
384 | } | ||
385 | prom_display_paths[i] = path; | ||
386 | prom_display_nodes[i] = node; | ||
387 | if (i == 0) | ||
388 | prom_disp_node = node; | ||
389 | if (prom_num_displays >= FB_MAX) | ||
390 | break; | ||
391 | } | ||
392 | |||
393 | for (j=0; j<prom_num_displays; j++) { | ||
394 | path = prom_display_paths[j]; | ||
395 | node = prom_display_nodes[j]; | ||
396 | prom_print("opening display "); | ||
397 | prom_print(path); | ||
398 | ih = call_prom("open", 1, 1, path); | ||
399 | if (ih == 0 || ih == (ihandle) -1) { | ||
400 | prom_print("... failed\n"); | ||
401 | for (i=j+1; i<prom_num_displays; i++) { | ||
402 | prom_display_paths[i-1] = prom_display_paths[i]; | ||
403 | prom_display_nodes[i-1] = prom_display_nodes[i]; | ||
404 | } | ||
405 | if (--prom_num_displays > 0) { | ||
406 | prom_disp_node = prom_display_nodes[j]; | ||
407 | j--; | ||
408 | } else | ||
409 | prom_disp_node = 0; | ||
410 | continue; | ||
411 | } else { | ||
412 | prom_print("... ok\n"); | ||
413 | call_prom("setprop", 4, 1, node, "linux,opened", 0, 0); | ||
414 | |||
415 | /* | ||
416 | * Setup a usable color table when the appropriate | ||
417 | * method is available. | ||
418 | * Should update this to use set-colors. | ||
419 | */ | ||
420 | clut = default_colors; | ||
421 | for (i = 0; i < 32; i++, clut += 3) | ||
422 | if (prom_set_color(ih, i, clut[0], clut[1], | ||
423 | clut[2]) != 0) | ||
424 | break; | ||
425 | |||
426 | #ifdef CONFIG_LOGO_LINUX_CLUT224 | ||
427 | clut = PTRRELOC(logo_linux_clut224.clut); | ||
428 | for (i = 0; i < logo_linux_clut224.clutsize; | ||
429 | i++, clut += 3) | ||
430 | if (prom_set_color(ih, i + 32, clut[0], | ||
431 | clut[1], clut[2]) != 0) | ||
432 | break; | ||
433 | #endif /* CONFIG_LOGO_LINUX_CLUT224 */ | ||
434 | } | ||
435 | } | ||
436 | |||
437 | if (prom_stdout) { | ||
438 | phandle p; | ||
439 | p = call_prom("instance-to-package", 1, 1, prom_stdout); | ||
440 | if (p && p != -1) { | ||
441 | type[0] = 0; | ||
442 | call_prom("getprop", 4, 1, p, "device_type", | ||
443 | type, sizeof(type)); | ||
444 | if (strcmp(type, "display") == 0) | ||
445 | call_prom("setprop", 4, 1, p, "linux,boot-display", | ||
446 | 0, 0); | ||
447 | } | ||
448 | } | ||
449 | |||
450 | return ALIGNUL(mem); | ||
451 | } | ||
452 | |||
453 | /* This function will enable the early boot text when doing OF booting. This | ||
454 | * way, xmon output should work too | ||
455 | */ | ||
456 | static void __init | ||
457 | setup_disp_fake_bi(ihandle dp) | ||
458 | { | ||
459 | #ifdef CONFIG_BOOTX_TEXT | ||
460 | int width = 640, height = 480, depth = 8, pitch; | ||
461 | unsigned address; | ||
462 | struct pci_reg_property addrs[8]; | ||
463 | int i, naddrs; | ||
464 | char name[32]; | ||
465 | char *getprop = "getprop"; | ||
466 | |||
467 | prom_print("Initializing fake screen: "); | ||
468 | |||
469 | memset(name, 0, sizeof(name)); | ||
470 | call_prom(getprop, 4, 1, dp, "name", name, sizeof(name)); | ||
471 | name[sizeof(name)-1] = 0; | ||
472 | prom_print(name); | ||
473 | prom_print("\n"); | ||
474 | call_prom(getprop, 4, 1, dp, "width", &width, sizeof(width)); | ||
475 | call_prom(getprop, 4, 1, dp, "height", &height, sizeof(height)); | ||
476 | call_prom(getprop, 4, 1, dp, "depth", &depth, sizeof(depth)); | ||
477 | pitch = width * ((depth + 7) / 8); | ||
478 | call_prom(getprop, 4, 1, dp, "linebytes", | ||
479 | &pitch, sizeof(pitch)); | ||
480 | if (pitch == 1) | ||
481 | pitch = 0x1000; /* for strange IBM display */ | ||
482 | address = 0; | ||
483 | call_prom(getprop, 4, 1, dp, "address", | ||
484 | &address, sizeof(address)); | ||
485 | if (address == 0) { | ||
486 | /* look for an assigned address with a size of >= 1MB */ | ||
487 | naddrs = call_prom(getprop, 4, 1, dp, "assigned-addresses", | ||
488 | addrs, sizeof(addrs)); | ||
489 | naddrs /= sizeof(struct pci_reg_property); | ||
490 | for (i = 0; i < naddrs; ++i) { | ||
491 | if (addrs[i].size_lo >= (1 << 20)) { | ||
492 | address = addrs[i].addr.a_lo; | ||
493 | /* use the BE aperture if possible */ | ||
494 | if (addrs[i].size_lo >= (16 << 20)) | ||
495 | address += (8 << 20); | ||
496 | break; | ||
497 | } | ||
498 | } | ||
499 | if (address == 0) { | ||
500 | prom_print("Failed to get address\n"); | ||
501 | return; | ||
502 | } | ||
503 | } | ||
504 | /* kludge for valkyrie */ | ||
505 | if (strcmp(name, "valkyrie") == 0) | ||
506 | address += 0x1000; | ||
507 | |||
508 | #ifdef CONFIG_POWER4 | ||
509 | #if CONFIG_TASK_SIZE > 0x80000000 | ||
510 | #error CONFIG_TASK_SIZE cannot be above 0x80000000 with BOOTX_TEXT on G5 | ||
511 | #endif | ||
512 | { | ||
513 | extern boot_infos_t disp_bi; | ||
514 | unsigned long va, pa, i, offset; | ||
515 | va = 0x90000000; | ||
516 | pa = address & 0xfffff000ul; | ||
517 | offset = address & 0x00000fff; | ||
518 | |||
519 | for (i=0; i<0x4000; i++) { | ||
520 | make_pte((unsigned long)Hash - KERNELBASE, Hash_size, va, pa, | ||
521 | _PAGE_ACCESSED | _PAGE_NO_CACHE | | ||
522 | _PAGE_GUARDED | PP_RWXX); | ||
523 | va += 0x1000; | ||
524 | pa += 0x1000; | ||
525 | } | ||
526 | btext_setup_display(width, height, depth, pitch, 0x90000000 | offset); | ||
527 | disp_bi.dispDeviceBase = (u8 *)address; | ||
528 | } | ||
529 | #else /* CONFIG_POWER4 */ | ||
530 | btext_setup_display(width, height, depth, pitch, address); | ||
531 | btext_prepare_BAT(); | ||
532 | #endif /* CONFIG_POWER4 */ | ||
533 | #endif /* CONFIG_BOOTX_TEXT */ | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | * Make a copy of the device tree from the PROM. | ||
538 | */ | ||
539 | static unsigned long __init | ||
540 | copy_device_tree(unsigned long mem_start, unsigned long mem_end) | ||
541 | { | ||
542 | phandle root; | ||
543 | unsigned long new_start; | ||
544 | struct device_node **allnextp; | ||
545 | |||
546 | root = call_prom("peer", 1, 1, (phandle)0); | ||
547 | if (root == (phandle)0) { | ||
548 | prom_print("couldn't get device tree root\n"); | ||
549 | prom_exit(); | ||
550 | } | ||
551 | allnextp = &allnodes; | ||
552 | mem_start = ALIGNUL(mem_start); | ||
553 | new_start = inspect_node(root, NULL, mem_start, mem_end, &allnextp); | ||
554 | *allnextp = NULL; | ||
555 | return new_start; | ||
556 | } | ||
557 | |||
558 | static unsigned long __init | ||
559 | inspect_node(phandle node, struct device_node *dad, | ||
560 | unsigned long mem_start, unsigned long mem_end, | ||
561 | struct device_node ***allnextpp) | ||
562 | { | ||
563 | int l; | ||
564 | phandle child; | ||
565 | struct device_node *np; | ||
566 | struct property *pp, **prev_propp; | ||
567 | char *prev_name, *namep; | ||
568 | unsigned char *valp; | ||
569 | |||
570 | np = (struct device_node *) mem_start; | ||
571 | mem_start += sizeof(struct device_node); | ||
572 | memset(np, 0, sizeof(*np)); | ||
573 | np->node = node; | ||
574 | **allnextpp = PTRUNRELOC(np); | ||
575 | *allnextpp = &np->allnext; | ||
576 | if (dad != 0) { | ||
577 | np->parent = PTRUNRELOC(dad); | ||
578 | /* we temporarily use the `next' field as `last_child'. */ | ||
579 | if (dad->next == 0) | ||
580 | dad->child = PTRUNRELOC(np); | ||
581 | else | ||
582 | dad->next->sibling = PTRUNRELOC(np); | ||
583 | dad->next = np; | ||
584 | } | ||
585 | |||
586 | /* get and store all properties */ | ||
587 | prev_propp = &np->properties; | ||
588 | prev_name = ""; | ||
589 | for (;;) { | ||
590 | pp = (struct property *) mem_start; | ||
591 | namep = (char *) (pp + 1); | ||
592 | pp->name = PTRUNRELOC(namep); | ||
593 | if (call_prom("nextprop", 3, 1, node, prev_name, namep) <= 0) | ||
594 | break; | ||
595 | mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); | ||
596 | prev_name = namep; | ||
597 | valp = (unsigned char *) mem_start; | ||
598 | pp->value = PTRUNRELOC(valp); | ||
599 | pp->length = call_prom("getprop", 4, 1, node, namep, | ||
600 | valp, mem_end - mem_start); | ||
601 | if (pp->length < 0) | ||
602 | continue; | ||
603 | #ifdef MAX_PROPERTY_LENGTH | ||
604 | if (pp->length > MAX_PROPERTY_LENGTH) | ||
605 | continue; /* ignore this property */ | ||
606 | #endif | ||
607 | mem_start = ALIGNUL(mem_start + pp->length); | ||
608 | *prev_propp = PTRUNRELOC(pp); | ||
609 | prev_propp = &pp->next; | ||
610 | } | ||
611 | if (np->node != 0) { | ||
612 | /* Add a "linux,phandle" property" */ | ||
613 | pp = (struct property *) mem_start; | ||
614 | *prev_propp = PTRUNRELOC(pp); | ||
615 | prev_propp = &pp->next; | ||
616 | namep = (char *) (pp + 1); | ||
617 | pp->name = PTRUNRELOC(namep); | ||
618 | strcpy(namep, "linux,phandle"); | ||
619 | mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1); | ||
620 | pp->value = (unsigned char *) PTRUNRELOC(&np->node); | ||
621 | pp->length = sizeof(np->node); | ||
622 | } | ||
623 | *prev_propp = NULL; | ||
624 | |||
625 | /* get the node's full name */ | ||
626 | l = call_prom("package-to-path", 3, 1, node, | ||
627 | mem_start, mem_end - mem_start); | ||
628 | if (l >= 0) { | ||
629 | np->full_name = PTRUNRELOC((char *) mem_start); | ||
630 | *(char *)(mem_start + l) = 0; | ||
631 | mem_start = ALIGNUL(mem_start + l + 1); | ||
632 | } | ||
633 | |||
634 | /* do all our children */ | ||
635 | child = call_prom("child", 1, 1, node); | ||
636 | while (child != 0) { | ||
637 | mem_start = inspect_node(child, np, mem_start, mem_end, | ||
638 | allnextpp); | ||
639 | child = call_prom("peer", 1, 1, child); | ||
640 | } | ||
641 | |||
642 | return mem_start; | ||
643 | } | ||
644 | |||
645 | unsigned long smp_chrp_cpu_nr __initdata = 0; | ||
646 | |||
647 | /* | ||
648 | * With CHRP SMP we need to use the OF to start the other | ||
649 | * processors so we can't wait until smp_boot_cpus (the OF is | ||
650 | * trashed by then) so we have to put the processors into | ||
651 | * a holding pattern controlled by the kernel (not OF) before | ||
652 | * we destroy the OF. | ||
653 | * | ||
654 | * This uses a chunk of high memory, puts some holding pattern | ||
655 | * code there and sends the other processors off to there until | ||
656 | * smp_boot_cpus tells them to do something. We do that by using | ||
657 | * physical address 0x0. The holding pattern checks that address | ||
658 | * until its cpu # is there, when it is that cpu jumps to | ||
659 | * __secondary_start(). smp_boot_cpus() takes care of setting those | ||
660 | * values. | ||
661 | * | ||
662 | * We also use physical address 0x4 here to tell when a cpu | ||
663 | * is in its holding pattern code. | ||
664 | * | ||
665 | * -- Cort | ||
666 | * | ||
667 | * Note that we have to do this if we have more than one CPU, | ||
668 | * even if this is a UP kernel. Otherwise when we trash OF | ||
669 | * the other CPUs will start executing some random instructions | ||
670 | * and crash the system. -- paulus | ||
671 | */ | ||
672 | static void __init | ||
673 | prom_hold_cpus(unsigned long mem) | ||
674 | { | ||
675 | extern void __secondary_hold(void); | ||
676 | unsigned long i; | ||
677 | int cpu; | ||
678 | phandle node; | ||
679 | char type[16], *path; | ||
680 | unsigned int reg; | ||
681 | |||
682 | /* | ||
683 | * XXX: hack to make sure we're chrp, assume that if we're | ||
684 | * chrp we have a device_type property -- Cort | ||
685 | */ | ||
686 | node = call_prom("finddevice", 1, 1, "/"); | ||
687 | if (call_prom("getprop", 4, 1, node, | ||
688 | "device_type", type, sizeof(type)) <= 0) | ||
689 | return; | ||
690 | |||
691 | /* copy the holding pattern code to someplace safe (0) */ | ||
692 | /* the holding pattern is now within the first 0x100 | ||
693 | bytes of the kernel image -- paulus */ | ||
694 | memcpy((void *)0, _stext, 0x100); | ||
695 | flush_icache_range(0, 0x100); | ||
696 | |||
697 | /* look for cpus */ | ||
698 | *(unsigned long *)(0x0) = 0; | ||
699 | asm volatile("dcbf 0,%0": : "r" (0) : "memory"); | ||
700 | for (node = 0; prom_next_node(&node); ) { | ||
701 | type[0] = 0; | ||
702 | call_prom("getprop", 4, 1, node, "device_type", | ||
703 | type, sizeof(type)); | ||
704 | if (strcmp(type, "cpu") != 0) | ||
705 | continue; | ||
706 | path = (char *) mem; | ||
707 | memset(path, 0, 256); | ||
708 | if (call_prom("package-to-path", 3, 1, node, path, 255) < 0) | ||
709 | continue; | ||
710 | reg = -1; | ||
711 | call_prom("getprop", 4, 1, node, "reg", ®, sizeof(reg)); | ||
712 | cpu = smp_chrp_cpu_nr++; | ||
713 | #ifdef CONFIG_SMP | ||
714 | smp_hw_index[cpu] = reg; | ||
715 | #endif /* CONFIG_SMP */ | ||
716 | /* XXX: hack - don't start cpu 0, this cpu -- Cort */ | ||
717 | if (cpu == 0) | ||
718 | continue; | ||
719 | prom_print("starting cpu "); | ||
720 | prom_print(path); | ||
721 | *(ulong *)(0x4) = 0; | ||
722 | call_prom("start-cpu", 3, 0, node, | ||
723 | (char *)__secondary_hold - _stext, cpu); | ||
724 | prom_print("..."); | ||
725 | for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ ) | ||
726 | ; | ||
727 | if (*(ulong *)(0x4) == cpu) | ||
728 | prom_print("ok\n"); | ||
729 | else { | ||
730 | prom_print("failed: "); | ||
731 | prom_print_hex(*(ulong *)0x4); | ||
732 | prom_print("\n"); | ||
733 | } | ||
734 | } | ||
735 | } | ||
736 | |||
737 | static void __init | ||
738 | prom_instantiate_rtas(void) | ||
739 | { | ||
740 | ihandle prom_rtas; | ||
741 | prom_arg_t result; | ||
742 | |||
743 | prom_rtas = call_prom("finddevice", 1, 1, "/rtas"); | ||
744 | if (prom_rtas == -1) | ||
745 | return; | ||
746 | |||
747 | rtas_size = 0; | ||
748 | call_prom("getprop", 4, 1, prom_rtas, | ||
749 | "rtas-size", &rtas_size, sizeof(rtas_size)); | ||
750 | prom_print("instantiating rtas"); | ||
751 | if (rtas_size == 0) { | ||
752 | rtas_data = 0; | ||
753 | } else { | ||
754 | /* | ||
755 | * Ask OF for some space for RTAS. | ||
756 | * Actually OF has bugs so we just arbitrarily | ||
757 | * use memory at the 6MB point. | ||
758 | */ | ||
759 | rtas_data = 6 << 20; | ||
760 | prom_print(" at "); | ||
761 | prom_print_hex(rtas_data); | ||
762 | } | ||
763 | |||
764 | prom_rtas = call_prom("open", 1, 1, "/rtas"); | ||
765 | prom_print("..."); | ||
766 | rtas_entry = 0; | ||
767 | if (call_prom_ret("call-method", 3, 2, &result, | ||
768 | "instantiate-rtas", prom_rtas, rtas_data) == 0) | ||
769 | rtas_entry = result; | ||
770 | if ((rtas_entry == -1) || (rtas_entry == 0)) | ||
771 | prom_print(" failed\n"); | ||
772 | else | ||
773 | prom_print(" done\n"); | ||
774 | } | ||
775 | |||
776 | /* | ||
777 | * We enter here early on, when the Open Firmware prom is still | ||
778 | * handling exceptions and the MMU hash table for us. | ||
779 | */ | ||
780 | unsigned long __init | ||
781 | prom_init(int r3, int r4, prom_entry pp) | ||
782 | { | ||
783 | unsigned long mem; | ||
784 | ihandle prom_mmu; | ||
785 | unsigned long offset = reloc_offset(); | ||
786 | int i, l; | ||
787 | char *p, *d; | ||
788 | unsigned long phys; | ||
789 | prom_arg_t result[3]; | ||
790 | char model[32]; | ||
791 | phandle node; | ||
792 | int rc; | ||
793 | |||
794 | /* Default */ | ||
795 | phys = (unsigned long) &_stext; | ||
796 | |||
797 | /* First get a handle for the stdout device */ | ||
798 | prom = pp; | ||
799 | prom_chosen = call_prom("finddevice", 1, 1, "/chosen"); | ||
800 | if (prom_chosen == -1) | ||
801 | prom_exit(); | ||
802 | if (call_prom("getprop", 4, 1, prom_chosen, "stdout", | ||
803 | &prom_stdout, sizeof(prom_stdout)) <= 0) | ||
804 | prom_exit(); | ||
805 | |||
806 | /* Get the full OF pathname of the stdout device */ | ||
807 | mem = (unsigned long) klimit + offset; | ||
808 | p = (char *) mem; | ||
809 | memset(p, 0, 256); | ||
810 | call_prom("instance-to-path", 3, 1, prom_stdout, p, 255); | ||
811 | of_stdout_device = p; | ||
812 | mem += strlen(p) + 1; | ||
813 | |||
814 | /* Get the boot device and translate it to a full OF pathname. */ | ||
815 | p = (char *) mem; | ||
816 | l = call_prom("getprop", 4, 1, prom_chosen, "bootpath", p, 1<<20); | ||
817 | if (l > 0) { | ||
818 | p[l] = 0; /* should already be null-terminated */ | ||
819 | bootpath = PTRUNRELOC(p); | ||
820 | mem += l + 1; | ||
821 | d = (char *) mem; | ||
822 | *d = 0; | ||
823 | call_prom("canon", 3, 1, p, d, 1<<20); | ||
824 | bootdevice = PTRUNRELOC(d); | ||
825 | mem = ALIGNUL(mem + strlen(d) + 1); | ||
826 | } | ||
827 | |||
828 | prom_instantiate_rtas(); | ||
829 | |||
830 | #ifdef CONFIG_POWER4 | ||
831 | /* | ||
832 | * Find out how much memory we have and allocate a | ||
833 | * suitably-sized hash table. | ||
834 | */ | ||
835 | prom_alloc_htab(); | ||
836 | #endif | ||
837 | mem = check_display(mem); | ||
838 | |||
839 | prom_print("copying OF device tree..."); | ||
840 | mem = copy_device_tree(mem, mem + (1<<20)); | ||
841 | prom_print("done\n"); | ||
842 | |||
843 | prom_hold_cpus(mem); | ||
844 | |||
845 | klimit = (char *) (mem - offset); | ||
846 | |||
847 | node = call_prom("finddevice", 1, 1, "/"); | ||
848 | rc = call_prom("getprop", 4, 1, node, "model", model, sizeof(model)); | ||
849 | if (rc > 0 && !strncmp (model, "Pegasos", 7) | ||
850 | && strncmp (model, "Pegasos2", 8)) { | ||
851 | /* Pegasos 1 has a broken translate method in the OF, | ||
852 | * and furthermore the BATs are mapped 1:1 so the phys | ||
853 | * address calculated above is correct, so let's use | ||
854 | * it directly. | ||
855 | */ | ||
856 | } else if (offset == 0) { | ||
857 | /* If we are already running at 0xc0000000, we assume we were | ||
858 | * loaded by an OF bootloader which did set a BAT for us. | ||
859 | * This breaks OF translate so we force phys to be 0. | ||
860 | */ | ||
861 | prom_print("(already at 0xc0000000) phys=0\n"); | ||
862 | phys = 0; | ||
863 | } else if (call_prom("getprop", 4, 1, prom_chosen, "mmu", | ||
864 | &prom_mmu, sizeof(prom_mmu)) <= 0) { | ||
865 | prom_print(" no MMU found\n"); | ||
866 | } else if (call_prom_ret("call-method", 4, 4, result, "translate", | ||
867 | prom_mmu, &_stext, 1) != 0) { | ||
868 | prom_print(" (translate failed)\n"); | ||
869 | } else { | ||
870 | /* We assume the phys. address size is 3 cells */ | ||
871 | phys = result[2]; | ||
872 | } | ||
873 | |||
874 | if (prom_disp_node != 0) | ||
875 | setup_disp_fake_bi(prom_disp_node); | ||
876 | |||
877 | /* Use quiesce call to get OF to shut down any devices it's using */ | ||
878 | prom_print("Calling quiesce ...\n"); | ||
879 | call_prom("quiesce", 0, 0); | ||
880 | |||
881 | /* Relocate various pointers which will be used once the | ||
882 | kernel is running at the address it was linked at. */ | ||
883 | for (i = 0; i < prom_num_displays; ++i) | ||
884 | prom_display_paths[i] = PTRUNRELOC(prom_display_paths[i]); | ||
885 | |||
886 | #ifdef CONFIG_SERIAL_CORE_CONSOLE | ||
887 | /* Relocate the of stdout for console autodetection */ | ||
888 | of_stdout_device = PTRUNRELOC(of_stdout_device); | ||
889 | #endif | ||
890 | |||
891 | prom_print("returning 0x"); | ||
892 | prom_print_hex(phys); | ||
893 | prom_print("from prom_init\n"); | ||
894 | prom_stdout = 0; | ||
895 | |||
896 | return phys; | ||
897 | } | ||
898 | |||
899 | /* | ||
900 | * early_get_property is used to access the device tree image prepared | ||
901 | * by BootX very early on, before the pointers in it have been relocated. | ||
902 | */ | ||
903 | static void * __init | ||
904 | early_get_property(unsigned long base, unsigned long node, char *prop) | ||
905 | { | ||
906 | struct device_node *np = (struct device_node *)(base + node); | ||
907 | struct property *pp; | ||
908 | |||
909 | for (pp = np->properties; pp != 0; pp = pp->next) { | ||
910 | pp = (struct property *) (base + (unsigned long)pp); | ||
911 | if (strcmp((char *)((unsigned long)pp->name + base), | ||
912 | prop) == 0) { | ||
913 | return (void *)((unsigned long)pp->value + base); | ||
914 | } | ||
915 | } | ||
916 | return NULL; | ||
917 | } | ||
918 | |||
919 | /* Is boot-info compatible ? */ | ||
920 | #define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION) | ||
921 | #define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2) | ||
922 | #define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4) | ||
923 | |||
924 | void __init | ||
925 | bootx_init(unsigned long r4, unsigned long phys) | ||
926 | { | ||
927 | boot_infos_t *bi = (boot_infos_t *) r4; | ||
928 | unsigned long space; | ||
929 | unsigned long ptr, x; | ||
930 | char *model; | ||
931 | |||
932 | boot_infos = PTRUNRELOC(bi); | ||
933 | if (!BOOT_INFO_IS_V2_COMPATIBLE(bi)) | ||
934 | bi->logicalDisplayBase = NULL; | ||
935 | |||
936 | #ifdef CONFIG_BOOTX_TEXT | ||
937 | btext_init(bi); | ||
938 | |||
939 | /* | ||
940 | * Test if boot-info is compatible. Done only in config | ||
941 | * CONFIG_BOOTX_TEXT since there is nothing much we can do | ||
942 | * with an incompatible version, except display a message | ||
943 | * and eventually hang the processor... | ||
944 | * | ||
945 | * I'll try to keep enough of boot-info compatible in the | ||
946 | * future to always allow display of this message; | ||
947 | */ | ||
948 | if (!BOOT_INFO_IS_COMPATIBLE(bi)) { | ||
949 | btext_drawstring(" !!! WARNING - Incompatible version of BootX !!!\n\n\n"); | ||
950 | btext_flushscreen(); | ||
951 | } | ||
952 | #endif /* CONFIG_BOOTX_TEXT */ | ||
953 | |||
954 | /* New BootX enters kernel with MMU off, i/os are not allowed | ||
955 | here. This hack will have been done by the boostrap anyway. | ||
956 | */ | ||
957 | if (bi->version < 4) { | ||
958 | /* | ||
959 | * XXX If this is an iMac, turn off the USB controller. | ||
960 | */ | ||
961 | model = (char *) early_get_property | ||
962 | (r4 + bi->deviceTreeOffset, 4, "model"); | ||
963 | if (model | ||
964 | && (strcmp(model, "iMac,1") == 0 | ||
965 | || strcmp(model, "PowerMac1,1") == 0)) { | ||
966 | out_le32((unsigned *)0x80880008, 1); /* XXX */ | ||
967 | } | ||
968 | } | ||
969 | |||
970 | /* Move klimit to enclose device tree, args, ramdisk, etc... */ | ||
971 | if (bi->version < 5) { | ||
972 | space = bi->deviceTreeOffset + bi->deviceTreeSize; | ||
973 | if (bi->ramDisk) | ||
974 | space = bi->ramDisk + bi->ramDiskSize; | ||
975 | } else | ||
976 | space = bi->totalParamsSize; | ||
977 | klimit = PTRUNRELOC((char *) bi + space); | ||
978 | |||
979 | /* New BootX will have flushed all TLBs and enters kernel with | ||
980 | MMU switched OFF, so this should not be useful anymore. | ||
981 | */ | ||
982 | if (bi->version < 4) { | ||
983 | /* | ||
984 | * Touch each page to make sure the PTEs for them | ||
985 | * are in the hash table - the aim is to try to avoid | ||
986 | * getting DSI exceptions while copying the kernel image. | ||
987 | */ | ||
988 | for (ptr = ((unsigned long) &_stext) & PAGE_MASK; | ||
989 | ptr < (unsigned long)bi + space; ptr += PAGE_SIZE) | ||
990 | x = *(volatile unsigned long *)ptr; | ||
991 | } | ||
992 | |||
993 | #ifdef CONFIG_BOOTX_TEXT | ||
994 | /* | ||
995 | * Note that after we call btext_prepare_BAT, we can't do | ||
996 | * prom_draw*, flushscreen or clearscreen until we turn the MMU | ||
997 | * on, since btext_prepare_BAT sets disp_bi.logicalDisplayBase | ||
998 | * to a virtual address. | ||
999 | */ | ||
1000 | btext_prepare_BAT(); | ||
1001 | #endif | ||
1002 | } | ||
diff --git a/arch/ppc/syslib/qspan_pci.c b/arch/ppc/syslib/qspan_pci.c new file mode 100644 index 000000000000..57f4ed5e5ae1 --- /dev/null +++ b/arch/ppc/syslib/qspan_pci.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /* | ||
2 | * QSpan pci routines. | ||
3 | * Most 8xx boards use the QSpan PCI bridge. The config address register | ||
4 | * is located 0x500 from the base of the bridge control/status registers. | ||
5 | * The data register is located at 0x504. | ||
6 | * This is a two step operation. First, the address register is written, | ||
7 | * then the data register is read/written as required. | ||
8 | * I don't know what to do about interrupts (yet). | ||
9 | * | ||
10 | * The RPX Classic implementation shares a chip select for normal | ||
11 | * PCI access and QSpan control register addresses. The selection is | ||
12 | * further selected by a bit setting in a board control register. | ||
13 | * Although it should happen, we disable interrupts during this operation | ||
14 | * to make sure some driver doesn't accidentally access the PCI while | ||
15 | * we have switched the chip select. | ||
16 | */ | ||
17 | |||
18 | #include <linux/config.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/string.h> | ||
23 | #include <linux/init.h> | ||
24 | |||
25 | #include <asm/io.h> | ||
26 | #include <asm/mpc8xx.h> | ||
27 | #include <asm/system.h> | ||
28 | #include <asm/machdep.h> | ||
29 | #include <asm/pci-bridge.h> | ||
30 | |||
31 | |||
32 | /* | ||
33 | * This blows...... | ||
34 | * When reading the configuration space, if something does not respond | ||
35 | * the bus times out and we get a machine check interrupt. So, the | ||
36 | * good ol' exception tables come to mind to trap it and return some | ||
37 | * value. | ||
38 | * | ||
39 | * On an error we just return a -1, since that is what the caller wants | ||
40 | * returned if nothing is present. I copied this from __get_user_asm, | ||
41 | * with the only difference of returning -1 instead of EFAULT. | ||
42 | * There is an associated hack in the machine check trap code. | ||
43 | * | ||
44 | * The QSPAN is also a big endian device, that is it makes the PCI | ||
45 | * look big endian to us. This presents a problem for the Linux PCI | ||
46 | * functions, which assume little endian. For example, we see the | ||
47 | * first 32-bit word like this: | ||
48 | * ------------------------ | ||
49 | * | Device ID | Vendor ID | | ||
50 | * ------------------------ | ||
51 | * If we read/write as a double word, that's OK. But in our world, | ||
52 | * when read as a word, device ID is at location 0, not location 2 as | ||
53 | * the little endian PCI would believe. We have to switch bits in | ||
54 | * the PCI addresses given to us to get the data to/from the correct | ||
55 | * byte lanes. | ||
56 | * | ||
57 | * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5. | ||
58 | * It always forces the MS bit to zero. Therefore, dev_fn values | ||
59 | * greater than 128 are returned as "no device found" errors. | ||
60 | * | ||
61 | * The QSPAN can only perform long word (32-bit) configuration cycles. | ||
62 | * The "offset" must have the two LS bits set to zero. Read operations | ||
63 | * require we read the entire word and then sort out what should be | ||
64 | * returned. Write operations other than long word require that we | ||
65 | * read the long word, update the proper word or byte, then write the | ||
66 | * entire long word back. | ||
67 | * | ||
68 | * PCI Bridge hack. We assume (correctly) that bus 0 is the primary | ||
69 | * PCI bus from the QSPAN. If we are called with a bus number other | ||
70 | * than zero, we create a Type 1 configuration access that a downstream | ||
71 | * PCI bridge will interpret. | ||
72 | */ | ||
73 | |||
74 | #define __get_qspan_pci_config(x, addr, op) \ | ||
75 | __asm__ __volatile__( \ | ||
76 | "1: "op" %0,0(%1)\n" \ | ||
77 | " eieio\n" \ | ||
78 | "2:\n" \ | ||
79 | ".section .fixup,\"ax\"\n" \ | ||
80 | "3: li %0,-1\n" \ | ||
81 | " b 2b\n" \ | ||
82 | ".section __ex_table,\"a\"\n" \ | ||
83 | " .align 2\n" \ | ||
84 | " .long 1b,3b\n" \ | ||
85 | ".text" \ | ||
86 | : "=r"(x) : "r"(addr) : " %0") | ||
87 | |||
88 | #define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500)) | ||
89 | #define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504)) | ||
90 | |||
91 | #define mk_config_addr(bus, dev, offset) \ | ||
92 | (((bus)<<16) | ((dev)<<8) | (offset & 0xfc)) | ||
93 | |||
94 | #define mk_config_type1(bus, dev, offset) \ | ||
95 | mk_config_addr(bus, dev, offset) | 1; | ||
96 | |||
97 | static spinlock_t pcibios_lock = SPIN_LOCK_UNLOCKED; | ||
98 | |||
99 | int qspan_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn, | ||
100 | unsigned char offset, unsigned char *val) | ||
101 | { | ||
102 | uint temp; | ||
103 | u_char *cp; | ||
104 | #ifdef CONFIG_RPXCLASSIC | ||
105 | unsigned long flags; | ||
106 | #endif | ||
107 | |||
108 | if ((bus > 7) || (dev_fn > 127)) { | ||
109 | *val = 0xff; | ||
110 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
111 | } | ||
112 | |||
113 | #ifdef CONFIG_RPXCLASSIC | ||
114 | /* disable interrupts */ | ||
115 | spin_lock_irqsave(&pcibios_lock, flags); | ||
116 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
117 | eieio(); | ||
118 | #endif | ||
119 | |||
120 | if (bus == 0) | ||
121 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
122 | else | ||
123 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
124 | __get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz"); | ||
125 | |||
126 | #ifdef CONFIG_RPXCLASSIC | ||
127 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
128 | eieio(); | ||
129 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
130 | #endif | ||
131 | |||
132 | offset ^= 0x03; | ||
133 | cp = ((u_char *)&temp) + (offset & 0x03); | ||
134 | *val = *cp; | ||
135 | return PCIBIOS_SUCCESSFUL; | ||
136 | } | ||
137 | |||
138 | int qspan_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn, | ||
139 | unsigned char offset, unsigned short *val) | ||
140 | { | ||
141 | uint temp; | ||
142 | ushort *sp; | ||
143 | #ifdef CONFIG_RPXCLASSIC | ||
144 | unsigned long flags; | ||
145 | #endif | ||
146 | |||
147 | if ((bus > 7) || (dev_fn > 127)) { | ||
148 | *val = 0xffff; | ||
149 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
150 | } | ||
151 | |||
152 | #ifdef CONFIG_RPXCLASSIC | ||
153 | /* disable interrupts */ | ||
154 | spin_lock_irqsave(&pcibios_lock, flags); | ||
155 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
156 | eieio(); | ||
157 | #endif | ||
158 | |||
159 | if (bus == 0) | ||
160 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
161 | else | ||
162 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
163 | __get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz"); | ||
164 | offset ^= 0x02; | ||
165 | |||
166 | #ifdef CONFIG_RPXCLASSIC | ||
167 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
168 | eieio(); | ||
169 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
170 | #endif | ||
171 | |||
172 | sp = ((ushort *)&temp) + ((offset >> 1) & 1); | ||
173 | *val = *sp; | ||
174 | return PCIBIOS_SUCCESSFUL; | ||
175 | } | ||
176 | |||
177 | int qspan_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn, | ||
178 | unsigned char offset, unsigned int *val) | ||
179 | { | ||
180 | #ifdef CONFIG_RPXCLASSIC | ||
181 | unsigned long flags; | ||
182 | #endif | ||
183 | |||
184 | if ((bus > 7) || (dev_fn > 127)) { | ||
185 | *val = 0xffffffff; | ||
186 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
187 | } | ||
188 | |||
189 | #ifdef CONFIG_RPXCLASSIC | ||
190 | /* disable interrupts */ | ||
191 | spin_lock_irqsave(&pcibios_lock, flags); | ||
192 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
193 | eieio(); | ||
194 | #endif | ||
195 | |||
196 | if (bus == 0) | ||
197 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
198 | else | ||
199 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
200 | __get_qspan_pci_config(*val, QS_CONFIG_DATA, "lwz"); | ||
201 | |||
202 | #ifdef CONFIG_RPXCLASSIC | ||
203 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
204 | eieio(); | ||
205 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
206 | #endif | ||
207 | |||
208 | return PCIBIOS_SUCCESSFUL; | ||
209 | } | ||
210 | |||
211 | int qspan_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn, | ||
212 | unsigned char offset, unsigned char val) | ||
213 | { | ||
214 | uint temp; | ||
215 | u_char *cp; | ||
216 | #ifdef CONFIG_RPXCLASSIC | ||
217 | unsigned long flags; | ||
218 | #endif | ||
219 | |||
220 | if ((bus > 7) || (dev_fn > 127)) | ||
221 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
222 | |||
223 | qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp); | ||
224 | |||
225 | offset ^= 0x03; | ||
226 | cp = ((u_char *)&temp) + (offset & 0x03); | ||
227 | *cp = val; | ||
228 | |||
229 | #ifdef CONFIG_RPXCLASSIC | ||
230 | /* disable interrupts */ | ||
231 | spin_lock_irqsave(&pcibios_lock, flags); | ||
232 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
233 | eieio(); | ||
234 | #endif | ||
235 | |||
236 | if (bus == 0) | ||
237 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
238 | else | ||
239 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
240 | *QS_CONFIG_DATA = temp; | ||
241 | |||
242 | #ifdef CONFIG_RPXCLASSIC | ||
243 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
244 | eieio(); | ||
245 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
246 | #endif | ||
247 | |||
248 | return PCIBIOS_SUCCESSFUL; | ||
249 | } | ||
250 | |||
251 | int qspan_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn, | ||
252 | unsigned char offset, unsigned short val) | ||
253 | { | ||
254 | uint temp; | ||
255 | ushort *sp; | ||
256 | #ifdef CONFIG_RPXCLASSIC | ||
257 | unsigned long flags; | ||
258 | #endif | ||
259 | |||
260 | if ((bus > 7) || (dev_fn > 127)) | ||
261 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
262 | |||
263 | qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp); | ||
264 | |||
265 | offset ^= 0x02; | ||
266 | sp = ((ushort *)&temp) + ((offset >> 1) & 1); | ||
267 | *sp = val; | ||
268 | |||
269 | #ifdef CONFIG_RPXCLASSIC | ||
270 | /* disable interrupts */ | ||
271 | spin_lock_irqsave(&pcibios_lock, flags); | ||
272 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
273 | eieio(); | ||
274 | #endif | ||
275 | |||
276 | if (bus == 0) | ||
277 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
278 | else | ||
279 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
280 | *QS_CONFIG_DATA = temp; | ||
281 | |||
282 | #ifdef CONFIG_RPXCLASSIC | ||
283 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
284 | eieio(); | ||
285 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
286 | #endif | ||
287 | |||
288 | return PCIBIOS_SUCCESSFUL; | ||
289 | } | ||
290 | |||
291 | int qspan_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn, | ||
292 | unsigned char offset, unsigned int val) | ||
293 | { | ||
294 | #ifdef CONFIG_RPXCLASSIC | ||
295 | unsigned long flags; | ||
296 | #endif | ||
297 | |||
298 | if ((bus > 7) || (dev_fn > 127)) | ||
299 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
300 | |||
301 | #ifdef CONFIG_RPXCLASSIC | ||
302 | /* disable interrupts */ | ||
303 | spin_lock_irqsave(&pcibios_lock, flags); | ||
304 | *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL; | ||
305 | eieio(); | ||
306 | #endif | ||
307 | |||
308 | if (bus == 0) | ||
309 | *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset); | ||
310 | else | ||
311 | *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset); | ||
312 | *(unsigned int *)QS_CONFIG_DATA = val; | ||
313 | |||
314 | #ifdef CONFIG_RPXCLASSIC | ||
315 | *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL; | ||
316 | eieio(); | ||
317 | spin_unlock_irqrestore(&pcibios_lock, flags); | ||
318 | #endif | ||
319 | |||
320 | return PCIBIOS_SUCCESSFUL; | ||
321 | } | ||
322 | |||
323 | int qspan_pcibios_find_device(unsigned short vendor, unsigned short dev_id, | ||
324 | unsigned short index, unsigned char *bus_ptr, | ||
325 | unsigned char *dev_fn_ptr) | ||
326 | { | ||
327 | int num, devfn; | ||
328 | unsigned int x, vendev; | ||
329 | |||
330 | if (vendor == 0xffff) | ||
331 | return PCIBIOS_BAD_VENDOR_ID; | ||
332 | vendev = (dev_id << 16) + vendor; | ||
333 | num = 0; | ||
334 | for (devfn = 0; devfn < 32; devfn++) { | ||
335 | qspan_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x); | ||
336 | if (x == vendev) { | ||
337 | if (index == num) { | ||
338 | *bus_ptr = 0; | ||
339 | *dev_fn_ptr = devfn<<3; | ||
340 | return PCIBIOS_SUCCESSFUL; | ||
341 | } | ||
342 | ++num; | ||
343 | } | ||
344 | } | ||
345 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
346 | } | ||
347 | |||
348 | int qspan_pcibios_find_class(unsigned int class_code, unsigned short index, | ||
349 | unsigned char *bus_ptr, unsigned char *dev_fn_ptr) | ||
350 | { | ||
351 | int devnr, x, num; | ||
352 | |||
353 | num = 0; | ||
354 | for (devnr = 0; devnr < 32; devnr++) { | ||
355 | qspan_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x); | ||
356 | if ((x>>8) == class_code) { | ||
357 | if (index == num) { | ||
358 | *bus_ptr = 0; | ||
359 | *dev_fn_ptr = devnr<<3; | ||
360 | return PCIBIOS_SUCCESSFUL; | ||
361 | } | ||
362 | ++num; | ||
363 | } | ||
364 | } | ||
365 | return PCIBIOS_DEVICE_NOT_FOUND; | ||
366 | } | ||
367 | |||
368 | void __init | ||
369 | m8xx_pcibios_fixup(void)) | ||
370 | { | ||
371 | /* Lots to do here, all board and configuration specific. */ | ||
372 | } | ||
373 | |||
374 | void __init | ||
375 | m8xx_setup_pci_ptrs(void)) | ||
376 | { | ||
377 | set_config_access_method(qspan); | ||
378 | |||
379 | ppc_md.pcibios_fixup = m8xx_pcibios_fixup; | ||
380 | } | ||
381 | |||
diff --git a/arch/ppc/syslib/todc_time.c b/arch/ppc/syslib/todc_time.c new file mode 100644 index 000000000000..1323c641c19d --- /dev/null +++ b/arch/ppc/syslib/todc_time.c | |||
@@ -0,0 +1,513 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/todc_time.c | ||
3 | * | ||
4 | * Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818 | ||
5 | * Real Time Clocks/Timekeepers. | ||
6 | * | ||
7 | * Author: Mark A. Greer | ||
8 | * mgreer@mvista.com | ||
9 | * | ||
10 | * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under | ||
11 | * the terms of the GNU General Public License version 2. This program | ||
12 | * is licensed "as is" without any warranty of any kind, whether express | ||
13 | * or implied. | ||
14 | */ | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/time.h> | ||
19 | #include <linux/timex.h> | ||
20 | #include <linux/bcd.h> | ||
21 | #include <linux/mc146818rtc.h> | ||
22 | |||
23 | #include <asm/machdep.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/time.h> | ||
26 | #include <asm/todc.h> | ||
27 | |||
28 | /* | ||
29 | * Depending on the hardware on your board and your board design, the | ||
30 | * RTC/NVRAM may be accessed either directly (like normal memory) or via | ||
31 | * address/data registers. If your board uses the direct method, set | ||
32 | * 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and | ||
33 | * 'nvram_as1' NULL. If your board uses address/data regs to access nvram, | ||
34 | * set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the | ||
35 | * address of the upper byte (leave NULL if using mc146818), and set | ||
36 | * 'nvram_data' to the address of the 8-bit data register. | ||
37 | * | ||
38 | * In order to break the assumption that the RTC and NVRAM are accessed by | ||
39 | * the same mechanism, you need to explicitly set 'ppc_md.rtc_read_val' and | ||
40 | * 'ppc_md.rtc_write_val', otherwise the values of 'ppc_md.rtc_read_val' | ||
41 | * and 'ppc_md.rtc_write_val' will be used. | ||
42 | * | ||
43 | * Note: Even though the documentation for the various RTC chips say that it | ||
44 | * take up to a second before it starts updating once the 'R' bit is | ||
45 | * cleared, they always seem to update even though we bang on it many | ||
46 | * times a second. This is true, except for the Dallas Semi 1746/1747 | ||
47 | * (possibly others). Those chips seem to have a real problem whenever | ||
48 | * we set the 'R' bit before reading them, they basically stop counting. | ||
49 | * --MAG | ||
50 | */ | ||
51 | |||
52 | /* | ||
53 | * 'todc_info' should be initialized in your *_setup.c file to | ||
54 | * point to a fully initialized 'todc_info_t' structure. | ||
55 | * This structure holds all the register offsets for your particular | ||
56 | * TODC/RTC chip. | ||
57 | * TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you. | ||
58 | */ | ||
59 | |||
60 | #ifdef RTC_FREQ_SELECT | ||
61 | #undef RTC_FREQ_SELECT | ||
62 | #define RTC_FREQ_SELECT control_b /* Register A */ | ||
63 | #endif | ||
64 | |||
65 | #ifdef RTC_CONTROL | ||
66 | #undef RTC_CONTROL | ||
67 | #define RTC_CONTROL control_a /* Register B */ | ||
68 | #endif | ||
69 | |||
70 | #ifdef RTC_INTR_FLAGS | ||
71 | #undef RTC_INTR_FLAGS | ||
72 | #define RTC_INTR_FLAGS watchdog /* Register C */ | ||
73 | #endif | ||
74 | |||
75 | #ifdef RTC_VALID | ||
76 | #undef RTC_VALID | ||
77 | #define RTC_VALID interrupts /* Register D */ | ||
78 | #endif | ||
79 | |||
80 | /* Access routines when RTC accessed directly (like normal memory) */ | ||
81 | u_char | ||
82 | todc_direct_read_val(int addr) | ||
83 | { | ||
84 | return readb((void __iomem *)(todc_info->nvram_data + addr)); | ||
85 | } | ||
86 | |||
87 | void | ||
88 | todc_direct_write_val(int addr, unsigned char val) | ||
89 | { | ||
90 | writeb(val, (void __iomem *)(todc_info->nvram_data + addr)); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | /* Access routines for accessing m48txx type chips via addr/data regs */ | ||
95 | u_char | ||
96 | todc_m48txx_read_val(int addr) | ||
97 | { | ||
98 | outb(addr, todc_info->nvram_as0); | ||
99 | outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); | ||
100 | return inb(todc_info->nvram_data); | ||
101 | } | ||
102 | |||
103 | void | ||
104 | todc_m48txx_write_val(int addr, unsigned char val) | ||
105 | { | ||
106 | outb(addr, todc_info->nvram_as0); | ||
107 | outb(addr>>todc_info->as0_bits, todc_info->nvram_as1); | ||
108 | outb(val, todc_info->nvram_data); | ||
109 | return; | ||
110 | } | ||
111 | |||
112 | /* Access routines for accessing mc146818 type chips via addr/data regs */ | ||
113 | u_char | ||
114 | todc_mc146818_read_val(int addr) | ||
115 | { | ||
116 | outb_p(addr, todc_info->nvram_as0); | ||
117 | return inb_p(todc_info->nvram_data); | ||
118 | } | ||
119 | |||
120 | void | ||
121 | todc_mc146818_write_val(int addr, unsigned char val) | ||
122 | { | ||
123 | outb_p(addr, todc_info->nvram_as0); | ||
124 | outb_p(val, todc_info->nvram_data); | ||
125 | } | ||
126 | |||
127 | |||
128 | /* | ||
129 | * Routines to make RTC chips with NVRAM buried behind an addr/data pair | ||
130 | * have the NVRAM and clock regs appear at the same level. | ||
131 | * The NVRAM will appear to start at addr 0 and the clock regs will appear | ||
132 | * to start immediately after the NVRAM (actually, start at offset | ||
133 | * todc_info->nvram_size). | ||
134 | */ | ||
135 | static inline u_char | ||
136 | todc_read_val(int addr) | ||
137 | { | ||
138 | u_char val; | ||
139 | |||
140 | if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) { | ||
141 | if (addr < todc_info->nvram_size) { /* NVRAM */ | ||
142 | ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr); | ||
143 | val = ppc_md.rtc_read_val(todc_info->nvram_data_reg); | ||
144 | } | ||
145 | else { /* Clock Reg */ | ||
146 | addr -= todc_info->nvram_size; | ||
147 | val = ppc_md.rtc_read_val(addr); | ||
148 | } | ||
149 | } | ||
150 | else { | ||
151 | val = ppc_md.rtc_read_val(addr); | ||
152 | } | ||
153 | |||
154 | return val; | ||
155 | } | ||
156 | |||
157 | static inline void | ||
158 | todc_write_val(int addr, u_char val) | ||
159 | { | ||
160 | if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) { | ||
161 | if (addr < todc_info->nvram_size) { /* NVRAM */ | ||
162 | ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr); | ||
163 | ppc_md.rtc_write_val(todc_info->nvram_data_reg, val); | ||
164 | } | ||
165 | else { /* Clock Reg */ | ||
166 | addr -= todc_info->nvram_size; | ||
167 | ppc_md.rtc_write_val(addr, val); | ||
168 | } | ||
169 | } | ||
170 | else { | ||
171 | ppc_md.rtc_write_val(addr, val); | ||
172 | } | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * TODC routines | ||
177 | * | ||
178 | * There is some ugly stuff in that there are assumptions for the mc146818. | ||
179 | * | ||
180 | * Assumptions: | ||
181 | * - todc_info->control_a has the offset as mc146818 Register B reg | ||
182 | * - todc_info->control_b has the offset as mc146818 Register A reg | ||
183 | * - m48txx control reg's write enable or 'W' bit is same as | ||
184 | * mc146818 Register B 'SET' bit (i.e., 0x80) | ||
185 | * | ||
186 | * These assumptions were made to make the code simpler. | ||
187 | */ | ||
188 | long __init | ||
189 | todc_time_init(void) | ||
190 | { | ||
191 | u_char cntl_b; | ||
192 | |||
193 | if (!ppc_md.rtc_read_val) | ||
194 | ppc_md.rtc_read_val = ppc_md.nvram_read_val; | ||
195 | if (!ppc_md.rtc_write_val) | ||
196 | ppc_md.rtc_write_val = ppc_md.nvram_write_val; | ||
197 | |||
198 | cntl_b = todc_read_val(todc_info->control_b); | ||
199 | |||
200 | if (todc_info->rtc_type == TODC_TYPE_MC146818) { | ||
201 | if ((cntl_b & 0x70) != 0x20) { | ||
202 | printk(KERN_INFO "TODC %s %s\n", | ||
203 | "real-time-clock was stopped.", | ||
204 | "Now starting..."); | ||
205 | cntl_b &= ~0x70; | ||
206 | cntl_b |= 0x20; | ||
207 | } | ||
208 | |||
209 | todc_write_val(todc_info->control_b, cntl_b); | ||
210 | } else if (todc_info->rtc_type == TODC_TYPE_DS17285) { | ||
211 | u_char mode; | ||
212 | |||
213 | mode = todc_read_val(TODC_TYPE_DS17285_CNTL_A); | ||
214 | /* Make sure countdown clear is not set */ | ||
215 | mode &= ~0x40; | ||
216 | /* Enable oscillator, extended register set */ | ||
217 | mode |= 0x30; | ||
218 | todc_write_val(TODC_TYPE_DS17285_CNTL_A, mode); | ||
219 | |||
220 | } else if (todc_info->rtc_type == TODC_TYPE_DS1501) { | ||
221 | u_char month; | ||
222 | |||
223 | todc_info->enable_read = TODC_DS1501_CNTL_B_TE; | ||
224 | todc_info->enable_write = TODC_DS1501_CNTL_B_TE; | ||
225 | |||
226 | month = todc_read_val(todc_info->month); | ||
227 | |||
228 | if ((month & 0x80) == 0x80) { | ||
229 | printk(KERN_INFO "TODC %s %s\n", | ||
230 | "real-time-clock was stopped.", | ||
231 | "Now starting..."); | ||
232 | month &= ~0x80; | ||
233 | todc_write_val(todc_info->month, month); | ||
234 | } | ||
235 | |||
236 | cntl_b &= ~TODC_DS1501_CNTL_B_TE; | ||
237 | todc_write_val(todc_info->control_b, cntl_b); | ||
238 | } else { /* must be a m48txx type */ | ||
239 | u_char cntl_a; | ||
240 | |||
241 | todc_info->enable_read = TODC_MK48TXX_CNTL_A_R; | ||
242 | todc_info->enable_write = TODC_MK48TXX_CNTL_A_W; | ||
243 | |||
244 | cntl_a = todc_read_val(todc_info->control_a); | ||
245 | |||
246 | /* Check & clear STOP bit in control B register */ | ||
247 | if (cntl_b & TODC_MK48TXX_DAY_CB) { | ||
248 | printk(KERN_INFO "TODC %s %s\n", | ||
249 | "real-time-clock was stopped.", | ||
250 | "Now starting..."); | ||
251 | |||
252 | cntl_a |= todc_info->enable_write; | ||
253 | cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */ | ||
254 | |||
255 | todc_write_val(todc_info->control_a, cntl_a); | ||
256 | todc_write_val(todc_info->control_b, cntl_b); | ||
257 | } | ||
258 | |||
259 | /* Make sure READ & WRITE bits are cleared. */ | ||
260 | cntl_a &= ~(todc_info->enable_write | | ||
261 | todc_info->enable_read); | ||
262 | todc_write_val(todc_info->control_a, cntl_a); | ||
263 | } | ||
264 | |||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | /* | ||
269 | * There is some ugly stuff in that there are assumptions that for a mc146818, | ||
270 | * the todc_info->control_a has the offset of the mc146818 Register B reg and | ||
271 | * that the register'ss 'SET' bit is the same as the m48txx's write enable | ||
272 | * bit in the control register of the m48txx (i.e., 0x80). | ||
273 | * | ||
274 | * It was done to make the code look simpler. | ||
275 | */ | ||
276 | ulong | ||
277 | todc_get_rtc_time(void) | ||
278 | { | ||
279 | uint year = 0, mon = 0, day = 0, hour = 0, min = 0, sec = 0; | ||
280 | uint limit, i; | ||
281 | u_char save_control, uip = 0; | ||
282 | |||
283 | spin_lock(&rtc_lock); | ||
284 | save_control = todc_read_val(todc_info->control_a); | ||
285 | |||
286 | if (todc_info->rtc_type != TODC_TYPE_MC146818) { | ||
287 | limit = 1; | ||
288 | |||
289 | switch (todc_info->rtc_type) { | ||
290 | case TODC_TYPE_DS1553: | ||
291 | case TODC_TYPE_DS1557: | ||
292 | case TODC_TYPE_DS1743: | ||
293 | case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ | ||
294 | case TODC_TYPE_DS1747: | ||
295 | case TODC_TYPE_DS17285: | ||
296 | break; | ||
297 | default: | ||
298 | todc_write_val(todc_info->control_a, | ||
299 | (save_control | todc_info->enable_read)); | ||
300 | } | ||
301 | } | ||
302 | else { | ||
303 | limit = 100000000; | ||
304 | } | ||
305 | |||
306 | for (i=0; i<limit; i++) { | ||
307 | if (todc_info->rtc_type == TODC_TYPE_MC146818) { | ||
308 | uip = todc_read_val(todc_info->RTC_FREQ_SELECT); | ||
309 | } | ||
310 | |||
311 | sec = todc_read_val(todc_info->seconds) & 0x7f; | ||
312 | min = todc_read_val(todc_info->minutes) & 0x7f; | ||
313 | hour = todc_read_val(todc_info->hours) & 0x3f; | ||
314 | day = todc_read_val(todc_info->day_of_month) & 0x3f; | ||
315 | mon = todc_read_val(todc_info->month) & 0x1f; | ||
316 | year = todc_read_val(todc_info->year) & 0xff; | ||
317 | |||
318 | if (todc_info->rtc_type == TODC_TYPE_MC146818) { | ||
319 | uip |= todc_read_val(todc_info->RTC_FREQ_SELECT); | ||
320 | if ((uip & RTC_UIP) == 0) break; | ||
321 | } | ||
322 | } | ||
323 | |||
324 | if (todc_info->rtc_type != TODC_TYPE_MC146818) { | ||
325 | switch (todc_info->rtc_type) { | ||
326 | case TODC_TYPE_DS1553: | ||
327 | case TODC_TYPE_DS1557: | ||
328 | case TODC_TYPE_DS1743: | ||
329 | case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ | ||
330 | case TODC_TYPE_DS1747: | ||
331 | case TODC_TYPE_DS17285: | ||
332 | break; | ||
333 | default: | ||
334 | save_control &= ~(todc_info->enable_read); | ||
335 | todc_write_val(todc_info->control_a, | ||
336 | save_control); | ||
337 | } | ||
338 | } | ||
339 | spin_unlock(&rtc_lock); | ||
340 | |||
341 | if ((todc_info->rtc_type != TODC_TYPE_MC146818) || | ||
342 | ((save_control & RTC_DM_BINARY) == 0) || | ||
343 | RTC_ALWAYS_BCD) { | ||
344 | |||
345 | BCD_TO_BIN(sec); | ||
346 | BCD_TO_BIN(min); | ||
347 | BCD_TO_BIN(hour); | ||
348 | BCD_TO_BIN(day); | ||
349 | BCD_TO_BIN(mon); | ||
350 | BCD_TO_BIN(year); | ||
351 | } | ||
352 | |||
353 | year = year + 1900; | ||
354 | if (year < 1970) { | ||
355 | year += 100; | ||
356 | } | ||
357 | |||
358 | return mktime(year, mon, day, hour, min, sec); | ||
359 | } | ||
360 | |||
361 | int | ||
362 | todc_set_rtc_time(unsigned long nowtime) | ||
363 | { | ||
364 | struct rtc_time tm; | ||
365 | u_char save_control, save_freq_select = 0; | ||
366 | |||
367 | spin_lock(&rtc_lock); | ||
368 | to_tm(nowtime, &tm); | ||
369 | |||
370 | save_control = todc_read_val(todc_info->control_a); | ||
371 | |||
372 | /* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */ | ||
373 | todc_write_val(todc_info->control_a, | ||
374 | (save_control | todc_info->enable_write)); | ||
375 | save_control &= ~(todc_info->enable_write); /* in case it was set */ | ||
376 | |||
377 | if (todc_info->rtc_type == TODC_TYPE_MC146818) { | ||
378 | save_freq_select = todc_read_val(todc_info->RTC_FREQ_SELECT); | ||
379 | todc_write_val(todc_info->RTC_FREQ_SELECT, | ||
380 | save_freq_select | RTC_DIV_RESET2); | ||
381 | } | ||
382 | |||
383 | |||
384 | tm.tm_year = (tm.tm_year - 1900) % 100; | ||
385 | |||
386 | if ((todc_info->rtc_type != TODC_TYPE_MC146818) || | ||
387 | ((save_control & RTC_DM_BINARY) == 0) || | ||
388 | RTC_ALWAYS_BCD) { | ||
389 | |||
390 | BIN_TO_BCD(tm.tm_sec); | ||
391 | BIN_TO_BCD(tm.tm_min); | ||
392 | BIN_TO_BCD(tm.tm_hour); | ||
393 | BIN_TO_BCD(tm.tm_mon); | ||
394 | BIN_TO_BCD(tm.tm_mday); | ||
395 | BIN_TO_BCD(tm.tm_year); | ||
396 | } | ||
397 | |||
398 | todc_write_val(todc_info->seconds, tm.tm_sec); | ||
399 | todc_write_val(todc_info->minutes, tm.tm_min); | ||
400 | todc_write_val(todc_info->hours, tm.tm_hour); | ||
401 | todc_write_val(todc_info->month, tm.tm_mon); | ||
402 | todc_write_val(todc_info->day_of_month, tm.tm_mday); | ||
403 | todc_write_val(todc_info->year, tm.tm_year); | ||
404 | |||
405 | todc_write_val(todc_info->control_a, save_control); | ||
406 | |||
407 | if (todc_info->rtc_type == TODC_TYPE_MC146818) { | ||
408 | todc_write_val(todc_info->RTC_FREQ_SELECT, save_freq_select); | ||
409 | } | ||
410 | spin_unlock(&rtc_lock); | ||
411 | |||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | /* | ||
416 | * Manipulates read bit to reliably read seconds at a high rate. | ||
417 | */ | ||
418 | static unsigned char __init todc_read_timereg(int addr) | ||
419 | { | ||
420 | unsigned char save_control = 0, val; | ||
421 | |||
422 | switch (todc_info->rtc_type) { | ||
423 | case TODC_TYPE_DS1553: | ||
424 | case TODC_TYPE_DS1557: | ||
425 | case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ | ||
426 | case TODC_TYPE_DS1747: | ||
427 | case TODC_TYPE_DS17285: | ||
428 | case TODC_TYPE_MC146818: | ||
429 | break; | ||
430 | default: | ||
431 | save_control = todc_read_val(todc_info->control_a); | ||
432 | todc_write_val(todc_info->control_a, | ||
433 | (save_control | todc_info->enable_read)); | ||
434 | } | ||
435 | val = todc_read_val(addr); | ||
436 | |||
437 | switch (todc_info->rtc_type) { | ||
438 | case TODC_TYPE_DS1553: | ||
439 | case TODC_TYPE_DS1557: | ||
440 | case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */ | ||
441 | case TODC_TYPE_DS1747: | ||
442 | case TODC_TYPE_DS17285: | ||
443 | case TODC_TYPE_MC146818: | ||
444 | break; | ||
445 | default: | ||
446 | save_control &= ~(todc_info->enable_read); | ||
447 | todc_write_val(todc_info->control_a, save_control); | ||
448 | } | ||
449 | |||
450 | return val; | ||
451 | } | ||
452 | |||
453 | /* | ||
454 | * This was taken from prep_setup.c | ||
455 | * Use the NVRAM RTC to time a second to calibrate the decrementer. | ||
456 | */ | ||
457 | void __init | ||
458 | todc_calibrate_decr(void) | ||
459 | { | ||
460 | ulong freq; | ||
461 | ulong tbl, tbu; | ||
462 | long i, loop_count; | ||
463 | u_char sec; | ||
464 | |||
465 | todc_time_init(); | ||
466 | |||
467 | /* | ||
468 | * Actually this is bad for precision, we should have a loop in | ||
469 | * which we only read the seconds counter. todc_read_val writes | ||
470 | * the address bytes on every call and this takes a lot of time. | ||
471 | * Perhaps an nvram_wait_change method returning a time | ||
472 | * stamp with a loop count as parameter would be the solution. | ||
473 | */ | ||
474 | /* | ||
475 | * Need to make sure the tbl doesn't roll over so if tbu increments | ||
476 | * during this test, we need to do it again. | ||
477 | */ | ||
478 | loop_count = 0; | ||
479 | |||
480 | sec = todc_read_timereg(todc_info->seconds) & 0x7f; | ||
481 | |||
482 | do { | ||
483 | tbu = get_tbu(); | ||
484 | |||
485 | for (i = 0 ; i < 10000000 ; i++) {/* may take up to 1 second */ | ||
486 | tbl = get_tbl(); | ||
487 | |||
488 | if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) { | ||
489 | break; | ||
490 | } | ||
491 | } | ||
492 | |||
493 | sec = todc_read_timereg(todc_info->seconds) & 0x7f; | ||
494 | |||
495 | for (i = 0 ; i < 10000000 ; i++) { /* Should take 1 second */ | ||
496 | freq = get_tbl(); | ||
497 | |||
498 | if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) { | ||
499 | break; | ||
500 | } | ||
501 | } | ||
502 | |||
503 | freq -= tbl; | ||
504 | } while ((get_tbu() != tbu) && (++loop_count < 2)); | ||
505 | |||
506 | printk("time_init: decrementer frequency = %lu.%.6lu MHz\n", | ||
507 | freq/1000000, freq%1000000); | ||
508 | |||
509 | tb_ticks_per_jiffy = freq / HZ; | ||
510 | tb_to_us = mulhwu_scale_factor(freq, 1000000); | ||
511 | |||
512 | return; | ||
513 | } | ||
diff --git a/arch/ppc/syslib/xilinx_pic.c b/arch/ppc/syslib/xilinx_pic.c new file mode 100644 index 000000000000..e0bd66f0847a --- /dev/null +++ b/arch/ppc/syslib/xilinx_pic.c | |||
@@ -0,0 +1,157 @@ | |||
1 | /* | ||
2 | * arch/ppc/syslib/xilinx_pic.c | ||
3 | * | ||
4 | * Interrupt controller driver for Xilinx Virtex-II Pro. | ||
5 | * | ||
6 | * Author: MontaVista Software, Inc. | ||
7 | * source@mvista.com | ||
8 | * | ||
9 | * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under | ||
10 | * the terms of the GNU General Public License version 2. This program | ||
11 | * is licensed "as is" without any warranty of any kind, whether express | ||
12 | * or implied. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <asm/io.h> | ||
18 | #include <asm/xparameters.h> | ||
19 | #include <asm/ibm4xx.h> | ||
20 | |||
21 | /* No one else should require these constants, so define them locally here. */ | ||
22 | #define ISR 0 /* Interrupt Status Register */ | ||
23 | #define IPR 1 /* Interrupt Pending Register */ | ||
24 | #define IER 2 /* Interrupt Enable Register */ | ||
25 | #define IAR 3 /* Interrupt Acknowledge Register */ | ||
26 | #define SIE 4 /* Set Interrupt Enable bits */ | ||
27 | #define CIE 5 /* Clear Interrupt Enable bits */ | ||
28 | #define IVR 6 /* Interrupt Vector Register */ | ||
29 | #define MER 7 /* Master Enable Register */ | ||
30 | |||
31 | #if XPAR_XINTC_USE_DCR == 0 | ||
32 | static volatile u32 *intc; | ||
33 | #define intc_out_be32(addr, mask) out_be32((addr), (mask)) | ||
34 | #define intc_in_be32(addr) in_be32((addr)) | ||
35 | #else | ||
36 | #define intc XPAR_INTC_0_BASEADDR | ||
37 | #define intc_out_be32(addr, mask) mtdcr((addr), (mask)) | ||
38 | #define intc_in_be32(addr) mfdcr((addr)) | ||
39 | #endif | ||
40 | |||
41 | static void | ||
42 | xilinx_intc_enable(unsigned int irq) | ||
43 | { | ||
44 | unsigned long mask = (0x00000001 << (irq & 31)); | ||
45 | pr_debug("enable: %d\n", irq); | ||
46 | intc_out_be32(intc + SIE, mask); | ||
47 | } | ||
48 | |||
49 | static void | ||
50 | xilinx_intc_disable(unsigned int irq) | ||
51 | { | ||
52 | unsigned long mask = (0x00000001 << (irq & 31)); | ||
53 | pr_debug("disable: %d\n", irq); | ||
54 | intc_out_be32(intc + CIE, mask); | ||
55 | } | ||
56 | |||
57 | static void | ||
58 | xilinx_intc_disable_and_ack(unsigned int irq) | ||
59 | { | ||
60 | unsigned long mask = (0x00000001 << (irq & 31)); | ||
61 | pr_debug("disable_and_ack: %d\n", irq); | ||
62 | intc_out_be32(intc + CIE, mask); | ||
63 | if (!(irq_desc[irq].status & IRQ_LEVEL)) | ||
64 | intc_out_be32(intc + IAR, mask); /* ack edge triggered intr */ | ||
65 | } | ||
66 | |||
67 | static void | ||
68 | xilinx_intc_end(unsigned int irq) | ||
69 | { | ||
70 | unsigned long mask = (0x00000001 << (irq & 31)); | ||
71 | |||
72 | pr_debug("end: %d\n", irq); | ||
73 | if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { | ||
74 | intc_out_be32(intc + SIE, mask); | ||
75 | /* ack level sensitive intr */ | ||
76 | if (irq_desc[irq].status & IRQ_LEVEL) | ||
77 | intc_out_be32(intc + IAR, mask); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | static struct hw_interrupt_type xilinx_intc = { | ||
82 | "Xilinx Interrupt Controller", | ||
83 | NULL, | ||
84 | NULL, | ||
85 | xilinx_intc_enable, | ||
86 | xilinx_intc_disable, | ||
87 | xilinx_intc_disable_and_ack, | ||
88 | xilinx_intc_end, | ||
89 | 0 | ||
90 | }; | ||
91 | |||
92 | int | ||
93 | xilinx_pic_get_irq(struct pt_regs *regs) | ||
94 | { | ||
95 | int irq; | ||
96 | |||
97 | /* | ||
98 | * NOTE: This function is the one that needs to be improved in | ||
99 | * order to handle multiple interrupt controllers. It currently | ||
100 | * is hardcoded to check for interrupts only on the first INTC. | ||
101 | */ | ||
102 | |||
103 | irq = intc_in_be32(intc + IVR); | ||
104 | if (irq != -1) | ||
105 | irq = irq; | ||
106 | |||
107 | pr_debug("get_irq: %d\n", irq); | ||
108 | |||
109 | return (irq); | ||
110 | } | ||
111 | |||
112 | void __init | ||
113 | ppc4xx_pic_init(void) | ||
114 | { | ||
115 | int i; | ||
116 | |||
117 | /* | ||
118 | * NOTE: The assumption here is that NR_IRQS is 32 or less | ||
119 | * (NR_IRQS is 32 for PowerPC 405 cores by default). | ||
120 | */ | ||
121 | #if (NR_IRQS > 32) | ||
122 | #error NR_IRQS > 32 not supported | ||
123 | #endif | ||
124 | |||
125 | #if XPAR_XINTC_USE_DCR == 0 | ||
126 | intc = ioremap(XPAR_INTC_0_BASEADDR, 32); | ||
127 | |||
128 | printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n", | ||
129 | (unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc); | ||
130 | #else | ||
131 | printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n", | ||
132 | (unsigned long) XPAR_INTC_0_BASEADDR); | ||
133 | #endif | ||
134 | |||
135 | /* | ||
136 | * Disable all external interrupts until they are | ||
137 | * explicity requested. | ||
138 | */ | ||
139 | intc_out_be32(intc + IER, 0); | ||
140 | |||
141 | /* Acknowledge any pending interrupts just in case. */ | ||
142 | intc_out_be32(intc + IAR, ~(u32) 0); | ||
143 | |||
144 | /* Turn on the Master Enable. */ | ||
145 | intc_out_be32(intc + MER, 0x3UL); | ||
146 | |||
147 | ppc_md.get_irq = xilinx_pic_get_irq; | ||
148 | |||
149 | for (i = 0; i < NR_IRQS; ++i) { | ||
150 | irq_desc[i].handler = &xilinx_intc; | ||
151 | |||
152 | if (XPAR_INTC_0_KIND_OF_INTR & (0x00000001 << i)) | ||
153 | irq_desc[i].status &= ~IRQ_LEVEL; | ||
154 | else | ||
155 | irq_desc[i].status |= IRQ_LEVEL; | ||
156 | } | ||
157 | } | ||