diff options
Diffstat (limited to 'arch/powerpc/boot/oflib.c')
-rw-r--r-- | arch/powerpc/boot/oflib.c | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/arch/powerpc/boot/oflib.c b/arch/powerpc/boot/oflib.c new file mode 100644 index 000000000000..e9b95d8ae7ff --- /dev/null +++ b/arch/powerpc/boot/oflib.c | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * Copyright (C) Paul Mackerras 1997. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | #include <stddef.h> | ||
10 | #include "types.h" | ||
11 | #include "elf.h" | ||
12 | #include "string.h" | ||
13 | #include "stdio.h" | ||
14 | #include "page.h" | ||
15 | #include "ops.h" | ||
16 | |||
17 | #include "of.h" | ||
18 | |||
19 | static int (*prom) (void *); | ||
20 | |||
21 | void of_init(void *promptr) | ||
22 | { | ||
23 | prom = (int (*)(void *))promptr; | ||
24 | } | ||
25 | |||
26 | int of_call_prom(const char *service, int nargs, int nret, ...) | ||
27 | { | ||
28 | int i; | ||
29 | struct prom_args { | ||
30 | const char *service; | ||
31 | int nargs; | ||
32 | int nret; | ||
33 | unsigned int args[12]; | ||
34 | } args; | ||
35 | va_list list; | ||
36 | |||
37 | args.service = service; | ||
38 | args.nargs = nargs; | ||
39 | args.nret = nret; | ||
40 | |||
41 | va_start(list, nret); | ||
42 | for (i = 0; i < nargs; i++) | ||
43 | args.args[i] = va_arg(list, unsigned int); | ||
44 | va_end(list); | ||
45 | |||
46 | for (i = 0; i < nret; i++) | ||
47 | args.args[nargs+i] = 0; | ||
48 | |||
49 | if (prom(&args) < 0) | ||
50 | return -1; | ||
51 | |||
52 | return (nret > 0)? args.args[nargs]: 0; | ||
53 | } | ||
54 | |||
55 | static int of_call_prom_ret(const char *service, int nargs, int nret, | ||
56 | unsigned int *rets, ...) | ||
57 | { | ||
58 | int i; | ||
59 | struct prom_args { | ||
60 | const char *service; | ||
61 | int nargs; | ||
62 | int nret; | ||
63 | unsigned int args[12]; | ||
64 | } args; | ||
65 | va_list list; | ||
66 | |||
67 | args.service = service; | ||
68 | args.nargs = nargs; | ||
69 | args.nret = nret; | ||
70 | |||
71 | va_start(list, rets); | ||
72 | for (i = 0; i < nargs; i++) | ||
73 | args.args[i] = va_arg(list, unsigned int); | ||
74 | va_end(list); | ||
75 | |||
76 | for (i = 0; i < nret; i++) | ||
77 | args.args[nargs+i] = 0; | ||
78 | |||
79 | if (prom(&args) < 0) | ||
80 | return -1; | ||
81 | |||
82 | if (rets != (void *) 0) | ||
83 | for (i = 1; i < nret; ++i) | ||
84 | rets[i-1] = args.args[nargs+i]; | ||
85 | |||
86 | return (nret > 0)? args.args[nargs]: 0; | ||
87 | } | ||
88 | |||
89 | /* returns true if s2 is a prefix of s1 */ | ||
90 | static int string_match(const char *s1, const char *s2) | ||
91 | { | ||
92 | for (; *s2; ++s2) | ||
93 | if (*s1++ != *s2) | ||
94 | return 0; | ||
95 | return 1; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Older OF's require that when claiming a specific range of addresses, | ||
100 | * we claim the physical space in the /memory node and the virtual | ||
101 | * space in the chosen mmu node, and then do a map operation to | ||
102 | * map virtual to physical. | ||
103 | */ | ||
104 | static int need_map = -1; | ||
105 | static ihandle chosen_mmu; | ||
106 | static phandle memory; | ||
107 | |||
108 | static int check_of_version(void) | ||
109 | { | ||
110 | phandle oprom, chosen; | ||
111 | char version[64]; | ||
112 | |||
113 | oprom = finddevice("/openprom"); | ||
114 | if (oprom == (phandle) -1) | ||
115 | return 0; | ||
116 | if (getprop(oprom, "model", version, sizeof(version)) <= 0) | ||
117 | return 0; | ||
118 | version[sizeof(version)-1] = 0; | ||
119 | printf("OF version = '%s'\r\n", version); | ||
120 | if (!string_match(version, "Open Firmware, 1.") | ||
121 | && !string_match(version, "FirmWorks,3.")) | ||
122 | return 0; | ||
123 | chosen = finddevice("/chosen"); | ||
124 | if (chosen == (phandle) -1) { | ||
125 | chosen = finddevice("/chosen@0"); | ||
126 | if (chosen == (phandle) -1) { | ||
127 | printf("no chosen\n"); | ||
128 | return 0; | ||
129 | } | ||
130 | } | ||
131 | if (getprop(chosen, "mmu", &chosen_mmu, sizeof(chosen_mmu)) <= 0) { | ||
132 | printf("no mmu\n"); | ||
133 | return 0; | ||
134 | } | ||
135 | memory = (ihandle) of_call_prom("open", 1, 1, "/memory"); | ||
136 | if (memory == (ihandle) -1) { | ||
137 | memory = (ihandle) of_call_prom("open", 1, 1, "/memory@0"); | ||
138 | if (memory == (ihandle) -1) { | ||
139 | printf("no memory node\n"); | ||
140 | return 0; | ||
141 | } | ||
142 | } | ||
143 | printf("old OF detected\r\n"); | ||
144 | return 1; | ||
145 | } | ||
146 | |||
147 | void *of_claim(unsigned long virt, unsigned long size, unsigned long align) | ||
148 | { | ||
149 | int ret; | ||
150 | unsigned int result; | ||
151 | |||
152 | if (need_map < 0) | ||
153 | need_map = check_of_version(); | ||
154 | if (align || !need_map) | ||
155 | return (void *) of_call_prom("claim", 3, 1, virt, size, align); | ||
156 | |||
157 | ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", memory, | ||
158 | align, size, virt); | ||
159 | if (ret != 0 || result == -1) | ||
160 | return (void *) -1; | ||
161 | ret = of_call_prom_ret("call-method", 5, 2, &result, "claim", chosen_mmu, | ||
162 | align, size, virt); | ||
163 | /* 0x12 == coherent + read/write */ | ||
164 | ret = of_call_prom("call-method", 6, 1, "map", chosen_mmu, | ||
165 | 0x12, size, virt, virt); | ||
166 | return (void *) virt; | ||
167 | } | ||
168 | |||
169 | void of_exit(void) | ||
170 | { | ||
171 | of_call_prom("exit", 0, 0); | ||
172 | } | ||