]>
Commit | Line | Data |
---|---|---|
a541f297 FB |
1 | /* |
2 | * QEMU generic PPC hardware System Emulator | |
3 | * | |
76a66253 | 4 | * Copyright (c) 2003-2007 Jocelyn Mayer |
a541f297 FB |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
a541f297 | 24 | #include "vl.h" |
fd0bbb12 | 25 | #include "m48t59.h" |
a541f297 | 26 | |
47103572 JM |
27 | extern FILE *logfile; |
28 | extern int loglevel; | |
29 | ||
30 | /*****************************************************************************/ | |
31 | /* PowerPC internal fake IRQ controller | |
32 | * used to manage multiple sources hardware events | |
33 | */ | |
34 | /* XXX: should be protected */ | |
35 | void ppc_set_irq (void *opaque, int n_IRQ, int level) | |
36 | { | |
37 | CPUState *env; | |
38 | ||
39 | env = opaque; | |
40 | if (level) { | |
41 | env->pending_interrupts |= 1 << n_IRQ; | |
42 | cpu_interrupt(env, CPU_INTERRUPT_HARD); | |
43 | } else { | |
44 | env->pending_interrupts &= ~(1 << n_IRQ); | |
45 | if (env->pending_interrupts == 0) | |
46 | cpu_reset_interrupt(env, CPU_INTERRUPT_HARD); | |
47 | } | |
48 | #if 0 | |
49 | printf("%s: %p n_IRQ %d level %d => pending %08x req %08x\n", __func__, | |
50 | env, n_IRQ, level, env->pending_interrupts, env->interrupt_request); | |
51 | #endif | |
52 | } | |
53 | ||
54 | /* External IRQ callback from OpenPIC IRQ controller */ | |
55 | void ppc_openpic_irq (void *opaque, int n_IRQ, int level) | |
56 | { | |
57 | switch (n_IRQ) { | |
58 | case OPENPIC_EVT_INT: | |
59 | n_IRQ = PPC_INTERRUPT_EXT; | |
60 | break; | |
61 | case OPENPIC_EVT_CINT: | |
62 | /* On PowerPC BookE, critical input use vector 0 */ | |
63 | n_IRQ = PPC_INTERRUPT_RESET; | |
64 | break; | |
65 | case OPENPIC_EVT_MCK: | |
66 | n_IRQ = PPC_INTERRUPT_MCK; | |
67 | break; | |
68 | case OPENPIC_EVT_DEBUG: | |
69 | n_IRQ = PPC_INTERRUPT_DEBUG; | |
70 | break; | |
71 | case OPENPIC_EVT_RESET: | |
72 | qemu_system_reset_request(); | |
73 | return; | |
74 | } | |
75 | ppc_set_irq(opaque, n_IRQ, level); | |
76 | } | |
77 | ||
9fddaa0c FB |
78 | /*****************************************************************************/ |
79 | /* PPC time base and decrementer emulation */ | |
80 | //#define DEBUG_TB | |
81 | ||
82 | struct ppc_tb_t { | |
83 | /* Time base management */ | |
84 | int64_t tb_offset; /* Compensation */ | |
85 | uint32_t tb_freq; /* TB frequency */ | |
86 | /* Decrementer management */ | |
87 | uint64_t decr_next; /* Tick for next decr interrupt */ | |
88 | struct QEMUTimer *decr_timer; | |
47103572 | 89 | void *opaque; |
9fddaa0c FB |
90 | }; |
91 | ||
92 | static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env) | |
93 | { | |
94 | /* TB time in tb periods */ | |
95 | return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset, | |
76a66253 | 96 | tb_env->tb_freq, ticks_per_sec); |
9fddaa0c FB |
97 | } |
98 | ||
99 | uint32_t cpu_ppc_load_tbl (CPUState *env) | |
100 | { | |
101 | ppc_tb_t *tb_env = env->tb_env; | |
102 | uint64_t tb; | |
103 | ||
104 | tb = cpu_ppc_get_tb(tb_env); | |
105 | #ifdef DEBUG_TB | |
106 | { | |
76a66253 JM |
107 | static int last_time; |
108 | int now; | |
109 | now = time(NULL); | |
110 | if (last_time != now) { | |
111 | last_time = now; | |
112 | printf("%s: tb=0x%016lx %d %08lx\n", | |
113 | __func__, tb, now, tb_env->tb_offset); | |
114 | } | |
9fddaa0c FB |
115 | } |
116 | #endif | |
117 | ||
118 | return tb & 0xFFFFFFFF; | |
119 | } | |
120 | ||
121 | uint32_t cpu_ppc_load_tbu (CPUState *env) | |
122 | { | |
123 | ppc_tb_t *tb_env = env->tb_env; | |
124 | uint64_t tb; | |
125 | ||
126 | tb = cpu_ppc_get_tb(tb_env); | |
127 | #ifdef DEBUG_TB | |
128 | printf("%s: tb=0x%016lx\n", __func__, tb); | |
129 | #endif | |
76a66253 | 130 | |
9fddaa0c FB |
131 | return tb >> 32; |
132 | } | |
133 | ||
134 | static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value) | |
135 | { | |
136 | tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq) | |
137 | - qemu_get_clock(vm_clock); | |
138 | #ifdef DEBUG_TB | |
139 | printf("%s: tb=0x%016lx offset=%08x\n", __func__, value); | |
140 | #endif | |
141 | } | |
142 | ||
143 | void cpu_ppc_store_tbu (CPUState *env, uint32_t value) | |
144 | { | |
145 | ppc_tb_t *tb_env = env->tb_env; | |
146 | ||
147 | cpu_ppc_store_tb(tb_env, | |
148 | ((uint64_t)value << 32) | cpu_ppc_load_tbl(env)); | |
149 | } | |
150 | ||
151 | void cpu_ppc_store_tbl (CPUState *env, uint32_t value) | |
152 | { | |
153 | ppc_tb_t *tb_env = env->tb_env; | |
154 | ||
155 | cpu_ppc_store_tb(tb_env, | |
156 | ((uint64_t)cpu_ppc_load_tbu(env) << 32) | value); | |
157 | } | |
158 | ||
159 | uint32_t cpu_ppc_load_decr (CPUState *env) | |
160 | { | |
161 | ppc_tb_t *tb_env = env->tb_env; | |
162 | uint32_t decr; | |
4e588a4d | 163 | int64_t diff; |
9fddaa0c | 164 | |
4e588a4d FB |
165 | diff = tb_env->decr_next - qemu_get_clock(vm_clock); |
166 | if (diff >= 0) | |
167 | decr = muldiv64(diff, tb_env->tb_freq, ticks_per_sec); | |
168 | else | |
169 | decr = -muldiv64(-diff, tb_env->tb_freq, ticks_per_sec); | |
fd0bbb12 | 170 | #if defined(DEBUG_TB) |
9fddaa0c FB |
171 | printf("%s: 0x%08x\n", __func__, decr); |
172 | #endif | |
76a66253 | 173 | |
9fddaa0c FB |
174 | return decr; |
175 | } | |
176 | ||
177 | /* When decrementer expires, | |
178 | * all we need to do is generate or queue a CPU exception | |
179 | */ | |
180 | static inline void cpu_ppc_decr_excp (CPUState *env) | |
181 | { | |
182 | /* Raise it */ | |
183 | #ifdef DEBUG_TB | |
184 | printf("raise decrementer exception\n"); | |
185 | #endif | |
47103572 | 186 | ppc_set_irq(env, PPC_INTERRUPT_DECR, 1); |
9fddaa0c FB |
187 | } |
188 | ||
189 | static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr, | |
190 | uint32_t value, int is_excp) | |
191 | { | |
192 | ppc_tb_t *tb_env = env->tb_env; | |
193 | uint64_t now, next; | |
194 | ||
195 | #ifdef DEBUG_TB | |
196 | printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value); | |
197 | #endif | |
198 | now = qemu_get_clock(vm_clock); | |
199 | next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq); | |
200 | if (is_excp) | |
201 | next += tb_env->decr_next - now; | |
202 | if (next == now) | |
76a66253 | 203 | next++; |
9fddaa0c FB |
204 | tb_env->decr_next = next; |
205 | /* Adjust timer */ | |
206 | qemu_mod_timer(tb_env->decr_timer, next); | |
207 | /* If we set a negative value and the decrementer was positive, | |
208 | * raise an exception. | |
209 | */ | |
210 | if ((value & 0x80000000) && !(decr & 0x80000000)) | |
76a66253 | 211 | cpu_ppc_decr_excp(env); |
9fddaa0c FB |
212 | } |
213 | ||
214 | void cpu_ppc_store_decr (CPUState *env, uint32_t value) | |
215 | { | |
216 | _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0); | |
217 | } | |
218 | ||
219 | static void cpu_ppc_decr_cb (void *opaque) | |
220 | { | |
221 | _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1); | |
222 | } | |
223 | ||
224 | /* Set up (once) timebase frequency (in Hz) */ | |
225 | ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq) | |
226 | { | |
227 | ppc_tb_t *tb_env; | |
228 | ||
229 | tb_env = qemu_mallocz(sizeof(ppc_tb_t)); | |
230 | if (tb_env == NULL) | |
231 | return NULL; | |
232 | env->tb_env = tb_env; | |
233 | if (tb_env->tb_freq == 0 || 1) { | |
76a66253 JM |
234 | tb_env->tb_freq = freq; |
235 | /* Create new timer */ | |
236 | tb_env->decr_timer = | |
9fddaa0c | 237 | qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env); |
76a66253 JM |
238 | /* There is a bug in Linux 2.4 kernels: |
239 | * if a decrementer exception is pending when it enables msr_ee, | |
240 | * it's not ready to handle it... | |
241 | */ | |
242 | _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0); | |
9fddaa0c FB |
243 | } |
244 | ||
245 | return tb_env; | |
246 | } | |
247 | ||
76a66253 JM |
248 | /* Specific helpers for POWER & PowerPC 601 RTC */ |
249 | ppc_tb_t *cpu_ppc601_rtc_init (CPUState *env) | |
250 | { | |
251 | return cpu_ppc_tb_init(env, 7812500); | |
252 | } | |
253 | ||
254 | void cpu_ppc601_store_rtcu (CPUState *env, uint32_t value) | |
255 | __attribute__ (( alias ("cpu_ppc_store_tbu") )); | |
256 | ||
257 | uint32_t cpu_ppc601_load_rtcu (CPUState *env) | |
258 | __attribute__ (( alias ("cpu_ppc_load_tbu") )); | |
259 | ||
260 | void cpu_ppc601_store_rtcl (CPUState *env, uint32_t value) | |
261 | { | |
262 | cpu_ppc_store_tbl(env, value & 0x3FFFFF80); | |
263 | } | |
264 | ||
265 | uint32_t cpu_ppc601_load_rtcl (CPUState *env) | |
266 | { | |
267 | return cpu_ppc_load_tbl(env) & 0x3FFFFF80; | |
268 | } | |
269 | ||
636aaad7 | 270 | /*****************************************************************************/ |
76a66253 | 271 | /* Embedded PowerPC timers */ |
636aaad7 JM |
272 | |
273 | /* PIT, FIT & WDT */ | |
274 | typedef struct ppcemb_timer_t ppcemb_timer_t; | |
275 | struct ppcemb_timer_t { | |
276 | uint64_t pit_reload; /* PIT auto-reload value */ | |
277 | uint64_t fit_next; /* Tick for next FIT interrupt */ | |
278 | struct QEMUTimer *fit_timer; | |
279 | uint64_t wdt_next; /* Tick for next WDT interrupt */ | |
280 | struct QEMUTimer *wdt_timer; | |
281 | }; | |
282 | ||
283 | /* Fixed interval timer */ | |
284 | static void cpu_4xx_fit_cb (void *opaque) | |
285 | { | |
286 | CPUState *env; | |
287 | ppc_tb_t *tb_env; | |
288 | ppcemb_timer_t *ppcemb_timer; | |
289 | uint64_t now, next; | |
290 | ||
291 | env = opaque; | |
292 | tb_env = env->tb_env; | |
293 | ppcemb_timer = tb_env->opaque; | |
294 | now = qemu_get_clock(vm_clock); | |
295 | switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { | |
296 | case 0: | |
297 | next = 1 << 9; | |
298 | break; | |
299 | case 1: | |
300 | next = 1 << 13; | |
301 | break; | |
302 | case 2: | |
303 | next = 1 << 17; | |
304 | break; | |
305 | case 3: | |
306 | next = 1 << 21; | |
307 | break; | |
308 | default: | |
309 | /* Cannot occur, but makes gcc happy */ | |
310 | return; | |
311 | } | |
312 | next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq); | |
313 | if (next == now) | |
314 | next++; | |
315 | qemu_mod_timer(ppcemb_timer->fit_timer, next); | |
316 | tb_env->decr_next = next; | |
317 | env->spr[SPR_40x_TSR] |= 1 << 26; | |
318 | if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) | |
319 | ppc_set_irq(env, PPC_INTERRUPT_FIT, 1); | |
320 | if (loglevel) { | |
321 | fprintf(logfile, "%s: ir %d TCR %08x TSR %08x\n", __func__, | |
322 | (env->spr[SPR_40x_TCR] >> 23) & 0x1, | |
323 | env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); | |
324 | } | |
325 | } | |
326 | ||
327 | /* Programmable interval timer */ | |
328 | static void cpu_4xx_pit_cb (void *opaque) | |
76a66253 | 329 | { |
636aaad7 JM |
330 | CPUState *env; |
331 | ppc_tb_t *tb_env; | |
332 | ppcemb_timer_t *ppcemb_timer; | |
333 | uint64_t now, next; | |
334 | ||
335 | env = opaque; | |
336 | tb_env = env->tb_env; | |
337 | ppcemb_timer = tb_env->opaque; | |
338 | now = qemu_get_clock(vm_clock); | |
339 | if ((env->spr[SPR_40x_TCR] >> 22) & 0x1) { | |
340 | /* Auto reload */ | |
341 | next = now + muldiv64(ppcemb_timer->pit_reload, | |
342 | ticks_per_sec, tb_env->tb_freq); | |
343 | if (next == now) | |
344 | next++; | |
345 | qemu_mod_timer(tb_env->decr_timer, next); | |
346 | tb_env->decr_next = next; | |
347 | } | |
348 | env->spr[SPR_40x_TSR] |= 1 << 27; | |
349 | if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) | |
350 | ppc_set_irq(env, PPC_INTERRUPT_PIT, 1); | |
351 | if (loglevel) { | |
352 | fprintf(logfile, "%s: ar %d ir %d TCR %08x TSR %08x %08lx\n", __func__, | |
353 | (env->spr[SPR_40x_TCR] >> 22) & 0x1, | |
354 | (env->spr[SPR_40x_TCR] >> 26) & 0x1, | |
355 | env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], | |
356 | ppcemb_timer->pit_reload); | |
357 | } | |
358 | } | |
359 | ||
360 | /* Watchdog timer */ | |
361 | static void cpu_4xx_wdt_cb (void *opaque) | |
362 | { | |
363 | CPUState *env; | |
364 | ppc_tb_t *tb_env; | |
365 | ppcemb_timer_t *ppcemb_timer; | |
366 | uint64_t now, next; | |
367 | ||
368 | env = opaque; | |
369 | tb_env = env->tb_env; | |
370 | ppcemb_timer = tb_env->opaque; | |
371 | now = qemu_get_clock(vm_clock); | |
372 | switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { | |
373 | case 0: | |
374 | next = 1 << 17; | |
375 | break; | |
376 | case 1: | |
377 | next = 1 << 21; | |
378 | break; | |
379 | case 2: | |
380 | next = 1 << 25; | |
381 | break; | |
382 | case 3: | |
383 | next = 1 << 29; | |
384 | break; | |
385 | default: | |
386 | /* Cannot occur, but makes gcc happy */ | |
387 | return; | |
388 | } | |
389 | next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq); | |
390 | if (next == now) | |
391 | next++; | |
392 | if (loglevel) { | |
393 | fprintf(logfile, "%s: TCR %08x TSR %08x\n", __func__, | |
394 | env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); | |
395 | } | |
396 | switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { | |
397 | case 0x0: | |
398 | case 0x1: | |
399 | qemu_mod_timer(ppcemb_timer->wdt_timer, next); | |
400 | ppcemb_timer->wdt_next = next; | |
401 | env->spr[SPR_40x_TSR] |= 1 << 31; | |
402 | break; | |
403 | case 0x2: | |
404 | qemu_mod_timer(ppcemb_timer->wdt_timer, next); | |
405 | ppcemb_timer->wdt_next = next; | |
406 | env->spr[SPR_40x_TSR] |= 1 << 30; | |
407 | if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) | |
408 | ppc_set_irq(env, PPC_INTERRUPT_WDT, 1); | |
409 | break; | |
410 | case 0x3: | |
411 | env->spr[SPR_40x_TSR] &= ~0x30000000; | |
412 | env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000; | |
413 | switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) { | |
414 | case 0x0: | |
415 | /* No reset */ | |
416 | break; | |
417 | case 0x1: /* Core reset */ | |
418 | case 0x2: /* Chip reset */ | |
419 | case 0x3: /* System reset */ | |
420 | qemu_system_reset_request(); | |
421 | return; | |
422 | } | |
423 | } | |
76a66253 JM |
424 | } |
425 | ||
426 | void store_40x_pit (CPUState *env, target_ulong val) | |
427 | { | |
636aaad7 JM |
428 | ppc_tb_t *tb_env; |
429 | ppcemb_timer_t *ppcemb_timer; | |
430 | uint64_t now, next; | |
431 | ||
432 | tb_env = env->tb_env; | |
433 | ppcemb_timer = tb_env->opaque; | |
434 | if (loglevel) | |
435 | fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer); | |
436 | ppcemb_timer->pit_reload = val; | |
437 | if (val == 0) { | |
438 | /* Stop PIT */ | |
439 | if (loglevel) | |
440 | fprintf(logfile, "%s: stop PIT\n", __func__); | |
441 | qemu_del_timer(tb_env->decr_timer); | |
442 | } else { | |
443 | if (loglevel) | |
444 | fprintf(logfile, "%s: start PIT 0x%08x\n", __func__, val); | |
445 | now = qemu_get_clock(vm_clock); | |
446 | next = now + muldiv64(val, ticks_per_sec, tb_env->tb_freq); | |
447 | if (next == now) | |
448 | next++; | |
449 | qemu_mod_timer(tb_env->decr_timer, next); | |
450 | tb_env->decr_next = next; | |
451 | } | |
76a66253 JM |
452 | } |
453 | ||
636aaad7 | 454 | target_ulong load_40x_pit (CPUState *env) |
76a66253 | 455 | { |
636aaad7 | 456 | return cpu_ppc_load_decr(env); |
76a66253 JM |
457 | } |
458 | ||
459 | void store_booke_tsr (CPUState *env, target_ulong val) | |
460 | { | |
636aaad7 JM |
461 | env->spr[SPR_40x_TSR] = val & 0xFC000000; |
462 | } | |
463 | ||
464 | void store_booke_tcr (CPUState *env, target_ulong val) | |
465 | { | |
466 | /* We don't update timers now. Maybe we should... */ | |
467 | env->spr[SPR_40x_TCR] = val & 0xFF800000; | |
468 | } | |
469 | ||
470 | void ppc_emb_timers_init (CPUState *env) | |
471 | { | |
472 | ppc_tb_t *tb_env; | |
473 | ppcemb_timer_t *ppcemb_timer; | |
474 | ||
475 | tb_env = env->tb_env; | |
476 | ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t)); | |
477 | tb_env->opaque = ppcemb_timer; | |
478 | if (loglevel) | |
479 | fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer); | |
480 | if (ppcemb_timer != NULL) { | |
481 | /* We use decr timer for PIT */ | |
482 | tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env); | |
483 | ppcemb_timer->fit_timer = | |
484 | qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env); | |
485 | ppcemb_timer->wdt_timer = | |
486 | qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env); | |
487 | } | |
76a66253 JM |
488 | } |
489 | ||
9fddaa0c FB |
490 | #if 0 |
491 | /*****************************************************************************/ | |
492 | /* Handle system reset (for now, just stop emulation) */ | |
493 | void cpu_ppc_reset (CPUState *env) | |
494 | { | |
495 | printf("Reset asked... Stop emulation\n"); | |
496 | abort(); | |
497 | } | |
498 | #endif | |
499 | ||
64201201 FB |
500 | /*****************************************************************************/ |
501 | /* Debug port */ | |
fd0bbb12 | 502 | void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) |
64201201 FB |
503 | { |
504 | addr &= 0xF; | |
505 | switch (addr) { | |
506 | case 0: | |
507 | printf("%c", val); | |
508 | break; | |
509 | case 1: | |
510 | printf("\n"); | |
511 | fflush(stdout); | |
512 | break; | |
513 | case 2: | |
514 | printf("Set loglevel to %04x\n", val); | |
fd0bbb12 | 515 | cpu_set_log(val | 0x100); |
64201201 FB |
516 | break; |
517 | } | |
518 | } | |
519 | ||
520 | /*****************************************************************************/ | |
521 | /* NVRAM helpers */ | |
522 | void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value) | |
523 | { | |
819385c5 | 524 | m48t59_write(nvram, addr, value); |
64201201 FB |
525 | } |
526 | ||
527 | uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr) | |
528 | { | |
819385c5 | 529 | return m48t59_read(nvram, addr); |
64201201 FB |
530 | } |
531 | ||
532 | void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value) | |
533 | { | |
819385c5 FB |
534 | m48t59_write(nvram, addr, value >> 8); |
535 | m48t59_write(nvram, addr + 1, value & 0xFF); | |
64201201 FB |
536 | } |
537 | ||
538 | uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr) | |
539 | { | |
540 | uint16_t tmp; | |
541 | ||
819385c5 FB |
542 | tmp = m48t59_read(nvram, addr) << 8; |
543 | tmp |= m48t59_read(nvram, addr + 1); | |
64201201 FB |
544 | return tmp; |
545 | } | |
546 | ||
547 | void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value) | |
548 | { | |
819385c5 FB |
549 | m48t59_write(nvram, addr, value >> 24); |
550 | m48t59_write(nvram, addr + 1, (value >> 16) & 0xFF); | |
551 | m48t59_write(nvram, addr + 2, (value >> 8) & 0xFF); | |
552 | m48t59_write(nvram, addr + 3, value & 0xFF); | |
64201201 FB |
553 | } |
554 | ||
555 | uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr) | |
556 | { | |
557 | uint32_t tmp; | |
558 | ||
819385c5 FB |
559 | tmp = m48t59_read(nvram, addr) << 24; |
560 | tmp |= m48t59_read(nvram, addr + 1) << 16; | |
561 | tmp |= m48t59_read(nvram, addr + 2) << 8; | |
562 | tmp |= m48t59_read(nvram, addr + 3); | |
76a66253 | 563 | |
64201201 FB |
564 | return tmp; |
565 | } | |
566 | ||
567 | void NVRAM_set_string (m48t59_t *nvram, uint32_t addr, | |
568 | const unsigned char *str, uint32_t max) | |
569 | { | |
570 | int i; | |
571 | ||
572 | for (i = 0; i < max && str[i] != '\0'; i++) { | |
819385c5 | 573 | m48t59_write(nvram, addr + i, str[i]); |
64201201 | 574 | } |
819385c5 | 575 | m48t59_write(nvram, addr + max - 1, '\0'); |
64201201 FB |
576 | } |
577 | ||
578 | int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max) | |
579 | { | |
580 | int i; | |
581 | ||
582 | memset(dst, 0, max); | |
583 | for (i = 0; i < max; i++) { | |
584 | dst[i] = NVRAM_get_byte(nvram, addr + i); | |
585 | if (dst[i] == '\0') | |
586 | break; | |
587 | } | |
588 | ||
589 | return i; | |
590 | } | |
591 | ||
592 | static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) | |
593 | { | |
594 | uint16_t tmp; | |
595 | uint16_t pd, pd1, pd2; | |
596 | ||
597 | tmp = prev >> 8; | |
598 | pd = prev ^ value; | |
599 | pd1 = pd & 0x000F; | |
600 | pd2 = ((pd >> 4) & 0x000F) ^ pd1; | |
601 | tmp ^= (pd1 << 3) | (pd1 << 8); | |
602 | tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); | |
603 | ||
604 | return tmp; | |
605 | } | |
606 | ||
607 | uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count) | |
608 | { | |
609 | uint32_t i; | |
610 | uint16_t crc = 0xFFFF; | |
611 | int odd; | |
612 | ||
613 | odd = count & 1; | |
614 | count &= ~1; | |
615 | for (i = 0; i != count; i++) { | |
76a66253 | 616 | crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); |
64201201 FB |
617 | } |
618 | if (odd) { | |
76a66253 | 619 | crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); |
64201201 FB |
620 | } |
621 | ||
622 | return crc; | |
623 | } | |
624 | ||
fd0bbb12 FB |
625 | #define CMDLINE_ADDR 0x017ff000 |
626 | ||
64201201 FB |
627 | int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, |
628 | const unsigned char *arch, | |
629 | uint32_t RAM_size, int boot_device, | |
630 | uint32_t kernel_image, uint32_t kernel_size, | |
fd0bbb12 | 631 | const char *cmdline, |
64201201 | 632 | uint32_t initrd_image, uint32_t initrd_size, |
fd0bbb12 FB |
633 | uint32_t NVRAM_image, |
634 | int width, int height, int depth) | |
64201201 FB |
635 | { |
636 | uint16_t crc; | |
637 | ||
638 | /* Set parameters for Open Hack'Ware BIOS */ | |
639 | NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16); | |
640 | NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */ | |
641 | NVRAM_set_word(nvram, 0x14, NVRAM_size); | |
642 | NVRAM_set_string(nvram, 0x20, arch, 16); | |
643 | NVRAM_set_lword(nvram, 0x30, RAM_size); | |
644 | NVRAM_set_byte(nvram, 0x34, boot_device); | |
645 | NVRAM_set_lword(nvram, 0x38, kernel_image); | |
646 | NVRAM_set_lword(nvram, 0x3C, kernel_size); | |
fd0bbb12 FB |
647 | if (cmdline) { |
648 | /* XXX: put the cmdline in NVRAM too ? */ | |
649 | strcpy(phys_ram_base + CMDLINE_ADDR, cmdline); | |
650 | NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR); | |
651 | NVRAM_set_lword(nvram, 0x44, strlen(cmdline)); | |
652 | } else { | |
653 | NVRAM_set_lword(nvram, 0x40, 0); | |
654 | NVRAM_set_lword(nvram, 0x44, 0); | |
655 | } | |
64201201 FB |
656 | NVRAM_set_lword(nvram, 0x48, initrd_image); |
657 | NVRAM_set_lword(nvram, 0x4C, initrd_size); | |
658 | NVRAM_set_lword(nvram, 0x50, NVRAM_image); | |
fd0bbb12 FB |
659 | |
660 | NVRAM_set_word(nvram, 0x54, width); | |
661 | NVRAM_set_word(nvram, 0x56, height); | |
662 | NVRAM_set_word(nvram, 0x58, depth); | |
663 | crc = NVRAM_compute_crc(nvram, 0x00, 0xF8); | |
664 | NVRAM_set_word(nvram, 0xFC, crc); | |
64201201 FB |
665 | |
666 | return 0; | |
a541f297 | 667 | } |