xref: /qemu/hw/ppc/ppc.c (revision d8ed887bdcd29ce2e967f8b15a6a2b6dcaa11cd5)
1a541f297Sbellard /*
2e9df014cSj_mayer  * QEMU generic PowerPC hardware System Emulator
3a541f297Sbellard  *
476a66253Sj_mayer  * Copyright (c) 2003-2007 Jocelyn Mayer
5a541f297Sbellard  *
6a541f297Sbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
7a541f297Sbellard  * of this software and associated documentation files (the "Software"), to deal
8a541f297Sbellard  * in the Software without restriction, including without limitation the rights
9a541f297Sbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10a541f297Sbellard  * copies of the Software, and to permit persons to whom the Software is
11a541f297Sbellard  * furnished to do so, subject to the following conditions:
12a541f297Sbellard  *
13a541f297Sbellard  * The above copyright notice and this permission notice shall be included in
14a541f297Sbellard  * all copies or substantial portions of the Software.
15a541f297Sbellard  *
16a541f297Sbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17a541f297Sbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18a541f297Sbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19a541f297Sbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20a541f297Sbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21a541f297Sbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22a541f297Sbellard  * THE SOFTWARE.
23a541f297Sbellard  */
2483c9f4caSPaolo Bonzini #include "hw/hw.h"
2583c9f4caSPaolo Bonzini #include "hw/ppc.h"
261de7afc9SPaolo Bonzini #include "qemu/timer.h"
279c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
2883c9f4caSPaolo Bonzini #include "hw/nvram.h"
291de7afc9SPaolo Bonzini #include "qemu/log.h"
3083c9f4caSPaolo Bonzini #include "hw/loader.h"
319c17d615SPaolo Bonzini #include "sysemu/kvm.h"
32fc87e185SAlexander Graf #include "kvm_ppc.h"
33a541f297Sbellard 
34e9df014cSj_mayer //#define PPC_DEBUG_IRQ
354b6d0a4cSj_mayer //#define PPC_DEBUG_TB
36e9df014cSj_mayer 
37d12d51d5Saliguori #ifdef PPC_DEBUG_IRQ
3893fcfe39Saliguori #  define LOG_IRQ(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
39d12d51d5Saliguori #else
40d12d51d5Saliguori #  define LOG_IRQ(...) do { } while (0)
41d12d51d5Saliguori #endif
42d12d51d5Saliguori 
43d12d51d5Saliguori 
44d12d51d5Saliguori #ifdef PPC_DEBUG_TB
4593fcfe39Saliguori #  define LOG_TB(...) qemu_log(__VA_ARGS__)
46d12d51d5Saliguori #else
47d12d51d5Saliguori #  define LOG_TB(...) do { } while (0)
48d12d51d5Saliguori #endif
49d12d51d5Saliguori 
50e2684c0bSAndreas Färber static void cpu_ppc_tb_stop (CPUPPCState *env);
51e2684c0bSAndreas Färber static void cpu_ppc_tb_start (CPUPPCState *env);
52dbdd2506Sj_mayer 
537058581aSAndreas Färber void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
5447103572Sj_mayer {
55*d8ed887bSAndreas Färber     CPUState *cs = CPU(cpu);
567058581aSAndreas Färber     CPUPPCState *env = &cpu->env;
57fc87e185SAlexander Graf     unsigned int old_pending = env->pending_interrupts;
58fc87e185SAlexander Graf 
5947103572Sj_mayer     if (level) {
6047103572Sj_mayer         env->pending_interrupts |= 1 << n_IRQ;
6147103572Sj_mayer         cpu_interrupt(env, CPU_INTERRUPT_HARD);
6247103572Sj_mayer     } else {
6347103572Sj_mayer         env->pending_interrupts &= ~(1 << n_IRQ);
64*d8ed887bSAndreas Färber         if (env->pending_interrupts == 0) {
65*d8ed887bSAndreas Färber             cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD);
66*d8ed887bSAndreas Färber         }
6747103572Sj_mayer     }
68fc87e185SAlexander Graf 
69fc87e185SAlexander Graf     if (old_pending != env->pending_interrupts) {
70fc87e185SAlexander Graf #ifdef CONFIG_KVM
717058581aSAndreas Färber         kvmppc_set_interrupt(cpu, n_IRQ, level);
72fc87e185SAlexander Graf #endif
73fc87e185SAlexander Graf     }
74fc87e185SAlexander Graf 
75d12d51d5Saliguori     LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
76aae9366aSj_mayer                 "req %08x\n", __func__, env, n_IRQ, level,
77259186a7SAndreas Färber                 env->pending_interrupts, CPU(cpu)->interrupt_request);
78a496775fSj_mayer }
7947103572Sj_mayer 
80e9df014cSj_mayer /* PowerPC 6xx / 7xx internal IRQ controller */
81e9df014cSj_mayer static void ppc6xx_set_irq(void *opaque, int pin, int level)
82d537cf6cSpbrook {
83a0961245SAndreas Färber     PowerPCCPU *cpu = opaque;
84a0961245SAndreas Färber     CPUPPCState *env = &cpu->env;
85e9df014cSj_mayer     int cur_level;
86d537cf6cSpbrook 
87d12d51d5Saliguori     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
88a496775fSj_mayer                 env, pin, level);
89e9df014cSj_mayer     cur_level = (env->irq_input_state >> pin) & 1;
90e9df014cSj_mayer     /* Don't generate spurious events */
9124be5ae3Sj_mayer     if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
92259186a7SAndreas Färber         CPUState *cs = CPU(cpu);
93259186a7SAndreas Färber 
94e9df014cSj_mayer         switch (pin) {
95dbdd2506Sj_mayer         case PPC6xx_INPUT_TBEN:
96dbdd2506Sj_mayer             /* Level sensitive - active high */
97d12d51d5Saliguori             LOG_IRQ("%s: %s the time base\n",
98dbdd2506Sj_mayer                         __func__, level ? "start" : "stop");
99dbdd2506Sj_mayer             if (level) {
100dbdd2506Sj_mayer                 cpu_ppc_tb_start(env);
101dbdd2506Sj_mayer             } else {
102dbdd2506Sj_mayer                 cpu_ppc_tb_stop(env);
103dbdd2506Sj_mayer             }
10424be5ae3Sj_mayer         case PPC6xx_INPUT_INT:
10524be5ae3Sj_mayer             /* Level sensitive - active high */
106d12d51d5Saliguori             LOG_IRQ("%s: set the external IRQ state to %d\n",
107a496775fSj_mayer                         __func__, level);
1087058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
109e9df014cSj_mayer             break;
11024be5ae3Sj_mayer         case PPC6xx_INPUT_SMI:
111e9df014cSj_mayer             /* Level sensitive - active high */
112d12d51d5Saliguori             LOG_IRQ("%s: set the SMI IRQ state to %d\n",
113a496775fSj_mayer                         __func__, level);
1147058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_SMI, level);
115e9df014cSj_mayer             break;
11624be5ae3Sj_mayer         case PPC6xx_INPUT_MCP:
117e9df014cSj_mayer             /* Negative edge sensitive */
118e9df014cSj_mayer             /* XXX: TODO: actual reaction may depends on HID0 status
119e9df014cSj_mayer              *            603/604/740/750: check HID0[EMCP]
120e9df014cSj_mayer              */
121e9df014cSj_mayer             if (cur_level == 1 && level == 0) {
122d12d51d5Saliguori                 LOG_IRQ("%s: raise machine check state\n",
123a496775fSj_mayer                             __func__);
1247058581aSAndreas Färber                 ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1);
125d537cf6cSpbrook             }
126e9df014cSj_mayer             break;
12724be5ae3Sj_mayer         case PPC6xx_INPUT_CKSTP_IN:
128e9df014cSj_mayer             /* Level sensitive - active low */
129e9df014cSj_mayer             /* XXX: TODO: relay the signal to CKSTP_OUT pin */
130e63ecc6fSj_mayer             /* XXX: Note that the only way to restart the CPU is to reset it */
131e9df014cSj_mayer             if (level) {
132d12d51d5Saliguori                 LOG_IRQ("%s: stop the CPU\n", __func__);
133259186a7SAndreas Färber                 cs->halted = 1;
134d537cf6cSpbrook             }
13547103572Sj_mayer             break;
13624be5ae3Sj_mayer         case PPC6xx_INPUT_HRESET:
137e9df014cSj_mayer             /* Level sensitive - active low */
138e9df014cSj_mayer             if (level) {
139d12d51d5Saliguori                 LOG_IRQ("%s: reset the CPU\n", __func__);
140fc0b2c0fSAlexander Graf                 cpu_interrupt(env, CPU_INTERRUPT_RESET);
141e9df014cSj_mayer             }
14247103572Sj_mayer             break;
14324be5ae3Sj_mayer         case PPC6xx_INPUT_SRESET:
144d12d51d5Saliguori             LOG_IRQ("%s: set the RESET IRQ state to %d\n",
145a496775fSj_mayer                         __func__, level);
1467058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level);
14747103572Sj_mayer             break;
148e9df014cSj_mayer         default:
149e9df014cSj_mayer             /* Unknown pin - do nothing */
150d12d51d5Saliguori             LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
15147103572Sj_mayer             return;
15247103572Sj_mayer         }
153e9df014cSj_mayer         if (level)
154e9df014cSj_mayer             env->irq_input_state |= 1 << pin;
155e9df014cSj_mayer         else
156e9df014cSj_mayer             env->irq_input_state &= ~(1 << pin);
157e9df014cSj_mayer     }
158e9df014cSj_mayer }
159e9df014cSj_mayer 
160e2684c0bSAndreas Färber void ppc6xx_irq_init(CPUPPCState *env)
161e9df014cSj_mayer {
162a0961245SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
163a0961245SAndreas Färber 
164a0961245SAndreas Färber     env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, cpu,
1657b62a955Sj_mayer                                                   PPC6xx_INPUT_NB);
16647103572Sj_mayer }
16747103572Sj_mayer 
16800af685fSj_mayer #if defined(TARGET_PPC64)
169d0dfae6eSj_mayer /* PowerPC 970 internal IRQ controller */
170d0dfae6eSj_mayer static void ppc970_set_irq(void *opaque, int pin, int level)
171d0dfae6eSj_mayer {
172a0961245SAndreas Färber     PowerPCCPU *cpu = opaque;
173a0961245SAndreas Färber     CPUPPCState *env = &cpu->env;
174d0dfae6eSj_mayer     int cur_level;
175d0dfae6eSj_mayer 
176d12d51d5Saliguori     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
177d0dfae6eSj_mayer                 env, pin, level);
178d0dfae6eSj_mayer     cur_level = (env->irq_input_state >> pin) & 1;
179d0dfae6eSj_mayer     /* Don't generate spurious events */
180d0dfae6eSj_mayer     if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
181259186a7SAndreas Färber         CPUState *cs = CPU(cpu);
182259186a7SAndreas Färber 
183d0dfae6eSj_mayer         switch (pin) {
184d0dfae6eSj_mayer         case PPC970_INPUT_INT:
185d0dfae6eSj_mayer             /* Level sensitive - active high */
186d12d51d5Saliguori             LOG_IRQ("%s: set the external IRQ state to %d\n",
187d0dfae6eSj_mayer                         __func__, level);
1887058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
189d0dfae6eSj_mayer             break;
190d0dfae6eSj_mayer         case PPC970_INPUT_THINT:
191d0dfae6eSj_mayer             /* Level sensitive - active high */
192d12d51d5Saliguori             LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__,
193d0dfae6eSj_mayer                         level);
1947058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_THERM, level);
195d0dfae6eSj_mayer             break;
196d0dfae6eSj_mayer         case PPC970_INPUT_MCP:
197d0dfae6eSj_mayer             /* Negative edge sensitive */
198d0dfae6eSj_mayer             /* XXX: TODO: actual reaction may depends on HID0 status
199d0dfae6eSj_mayer              *            603/604/740/750: check HID0[EMCP]
200d0dfae6eSj_mayer              */
201d0dfae6eSj_mayer             if (cur_level == 1 && level == 0) {
202d12d51d5Saliguori                 LOG_IRQ("%s: raise machine check state\n",
203d0dfae6eSj_mayer                             __func__);
2047058581aSAndreas Färber                 ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1);
205d0dfae6eSj_mayer             }
206d0dfae6eSj_mayer             break;
207d0dfae6eSj_mayer         case PPC970_INPUT_CKSTP:
208d0dfae6eSj_mayer             /* Level sensitive - active low */
209d0dfae6eSj_mayer             /* XXX: TODO: relay the signal to CKSTP_OUT pin */
210d0dfae6eSj_mayer             if (level) {
211d12d51d5Saliguori                 LOG_IRQ("%s: stop the CPU\n", __func__);
212259186a7SAndreas Färber                 cs->halted = 1;
213d0dfae6eSj_mayer             } else {
214d12d51d5Saliguori                 LOG_IRQ("%s: restart the CPU\n", __func__);
215259186a7SAndreas Färber                 cs->halted = 0;
216259186a7SAndreas Färber                 qemu_cpu_kick(cs);
217d0dfae6eSj_mayer             }
218d0dfae6eSj_mayer             break;
219d0dfae6eSj_mayer         case PPC970_INPUT_HRESET:
220d0dfae6eSj_mayer             /* Level sensitive - active low */
221d0dfae6eSj_mayer             if (level) {
222fc0b2c0fSAlexander Graf                 cpu_interrupt(env, CPU_INTERRUPT_RESET);
223d0dfae6eSj_mayer             }
224d0dfae6eSj_mayer             break;
225d0dfae6eSj_mayer         case PPC970_INPUT_SRESET:
226d12d51d5Saliguori             LOG_IRQ("%s: set the RESET IRQ state to %d\n",
227d0dfae6eSj_mayer                         __func__, level);
2287058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level);
229d0dfae6eSj_mayer             break;
230d0dfae6eSj_mayer         case PPC970_INPUT_TBEN:
231d12d51d5Saliguori             LOG_IRQ("%s: set the TBEN state to %d\n", __func__,
232d0dfae6eSj_mayer                         level);
233d0dfae6eSj_mayer             /* XXX: TODO */
234d0dfae6eSj_mayer             break;
235d0dfae6eSj_mayer         default:
236d0dfae6eSj_mayer             /* Unknown pin - do nothing */
237d12d51d5Saliguori             LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
238d0dfae6eSj_mayer             return;
239d0dfae6eSj_mayer         }
240d0dfae6eSj_mayer         if (level)
241d0dfae6eSj_mayer             env->irq_input_state |= 1 << pin;
242d0dfae6eSj_mayer         else
243d0dfae6eSj_mayer             env->irq_input_state &= ~(1 << pin);
244d0dfae6eSj_mayer     }
245d0dfae6eSj_mayer }
246d0dfae6eSj_mayer 
247e2684c0bSAndreas Färber void ppc970_irq_init(CPUPPCState *env)
248d0dfae6eSj_mayer {
249a0961245SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
250a0961245SAndreas Färber 
251a0961245SAndreas Färber     env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, cpu,
2527b62a955Sj_mayer                                                   PPC970_INPUT_NB);
253d0dfae6eSj_mayer }
2549d52e907SDavid Gibson 
2559d52e907SDavid Gibson /* POWER7 internal IRQ controller */
2569d52e907SDavid Gibson static void power7_set_irq(void *opaque, int pin, int level)
2579d52e907SDavid Gibson {
258a0961245SAndreas Färber     PowerPCCPU *cpu = opaque;
259a0961245SAndreas Färber     CPUPPCState *env = &cpu->env;
2609d52e907SDavid Gibson 
2619d52e907SDavid Gibson     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
2629d52e907SDavid Gibson                 env, pin, level);
2639d52e907SDavid Gibson 
2649d52e907SDavid Gibson     switch (pin) {
2659d52e907SDavid Gibson     case POWER7_INPUT_INT:
2669d52e907SDavid Gibson         /* Level sensitive - active high */
2679d52e907SDavid Gibson         LOG_IRQ("%s: set the external IRQ state to %d\n",
2689d52e907SDavid Gibson                 __func__, level);
2697058581aSAndreas Färber         ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
2709d52e907SDavid Gibson         break;
2719d52e907SDavid Gibson     default:
2729d52e907SDavid Gibson         /* Unknown pin - do nothing */
2739d52e907SDavid Gibson         LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
2749d52e907SDavid Gibson         return;
2759d52e907SDavid Gibson     }
2769d52e907SDavid Gibson     if (level) {
2779d52e907SDavid Gibson         env->irq_input_state |= 1 << pin;
2789d52e907SDavid Gibson     } else {
2799d52e907SDavid Gibson         env->irq_input_state &= ~(1 << pin);
2809d52e907SDavid Gibson     }
2819d52e907SDavid Gibson }
2829d52e907SDavid Gibson 
283e2684c0bSAndreas Färber void ppcPOWER7_irq_init(CPUPPCState *env)
2849d52e907SDavid Gibson {
285a0961245SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
286a0961245SAndreas Färber 
287a0961245SAndreas Färber     env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu,
2889d52e907SDavid Gibson                                                   POWER7_INPUT_NB);
2899d52e907SDavid Gibson }
29000af685fSj_mayer #endif /* defined(TARGET_PPC64) */
291d0dfae6eSj_mayer 
2924e290a0bSj_mayer /* PowerPC 40x internal IRQ controller */
2934e290a0bSj_mayer static void ppc40x_set_irq(void *opaque, int pin, int level)
29424be5ae3Sj_mayer {
295a0961245SAndreas Färber     PowerPCCPU *cpu = opaque;
296a0961245SAndreas Färber     CPUPPCState *env = &cpu->env;
29724be5ae3Sj_mayer     int cur_level;
29824be5ae3Sj_mayer 
299d12d51d5Saliguori     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
3008ecc7913Sj_mayer                 env, pin, level);
30124be5ae3Sj_mayer     cur_level = (env->irq_input_state >> pin) & 1;
30224be5ae3Sj_mayer     /* Don't generate spurious events */
30324be5ae3Sj_mayer     if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
304259186a7SAndreas Färber         CPUState *cs = CPU(cpu);
305259186a7SAndreas Färber 
30624be5ae3Sj_mayer         switch (pin) {
3074e290a0bSj_mayer         case PPC40x_INPUT_RESET_SYS:
3088ecc7913Sj_mayer             if (level) {
309d12d51d5Saliguori                 LOG_IRQ("%s: reset the PowerPC system\n",
3108ecc7913Sj_mayer                             __func__);
311f3273ba6SAndreas Färber                 ppc40x_system_reset(cpu);
3128ecc7913Sj_mayer             }
3138ecc7913Sj_mayer             break;
3144e290a0bSj_mayer         case PPC40x_INPUT_RESET_CHIP:
3158ecc7913Sj_mayer             if (level) {
316d12d51d5Saliguori                 LOG_IRQ("%s: reset the PowerPC chip\n", __func__);
317f3273ba6SAndreas Färber                 ppc40x_chip_reset(cpu);
3188ecc7913Sj_mayer             }
3198ecc7913Sj_mayer             break;
3204e290a0bSj_mayer         case PPC40x_INPUT_RESET_CORE:
32124be5ae3Sj_mayer             /* XXX: TODO: update DBSR[MRR] */
32224be5ae3Sj_mayer             if (level) {
323d12d51d5Saliguori                 LOG_IRQ("%s: reset the PowerPC core\n", __func__);
324f3273ba6SAndreas Färber                 ppc40x_core_reset(cpu);
32524be5ae3Sj_mayer             }
32624be5ae3Sj_mayer             break;
3274e290a0bSj_mayer         case PPC40x_INPUT_CINT:
32824be5ae3Sj_mayer             /* Level sensitive - active high */
329d12d51d5Saliguori             LOG_IRQ("%s: set the critical IRQ state to %d\n",
3308ecc7913Sj_mayer                         __func__, level);
3317058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level);
33224be5ae3Sj_mayer             break;
3334e290a0bSj_mayer         case PPC40x_INPUT_INT:
33424be5ae3Sj_mayer             /* Level sensitive - active high */
335d12d51d5Saliguori             LOG_IRQ("%s: set the external IRQ state to %d\n",
336a496775fSj_mayer                         __func__, level);
3377058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
33824be5ae3Sj_mayer             break;
3394e290a0bSj_mayer         case PPC40x_INPUT_HALT:
34024be5ae3Sj_mayer             /* Level sensitive - active low */
34124be5ae3Sj_mayer             if (level) {
342d12d51d5Saliguori                 LOG_IRQ("%s: stop the CPU\n", __func__);
343259186a7SAndreas Färber                 cs->halted = 1;
34424be5ae3Sj_mayer             } else {
345d12d51d5Saliguori                 LOG_IRQ("%s: restart the CPU\n", __func__);
346259186a7SAndreas Färber                 cs->halted = 0;
347259186a7SAndreas Färber                 qemu_cpu_kick(cs);
34824be5ae3Sj_mayer             }
34924be5ae3Sj_mayer             break;
3504e290a0bSj_mayer         case PPC40x_INPUT_DEBUG:
35124be5ae3Sj_mayer             /* Level sensitive - active high */
352d12d51d5Saliguori             LOG_IRQ("%s: set the debug pin state to %d\n",
353a496775fSj_mayer                         __func__, level);
3547058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level);
35524be5ae3Sj_mayer             break;
35624be5ae3Sj_mayer         default:
35724be5ae3Sj_mayer             /* Unknown pin - do nothing */
358d12d51d5Saliguori             LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
35924be5ae3Sj_mayer             return;
36024be5ae3Sj_mayer         }
36124be5ae3Sj_mayer         if (level)
36224be5ae3Sj_mayer             env->irq_input_state |= 1 << pin;
36324be5ae3Sj_mayer         else
36424be5ae3Sj_mayer             env->irq_input_state &= ~(1 << pin);
36524be5ae3Sj_mayer     }
36624be5ae3Sj_mayer }
36724be5ae3Sj_mayer 
368e2684c0bSAndreas Färber void ppc40x_irq_init(CPUPPCState *env)
36924be5ae3Sj_mayer {
370a0961245SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
371a0961245SAndreas Färber 
3724e290a0bSj_mayer     env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq,
373a0961245SAndreas Färber                                                   cpu, PPC40x_INPUT_NB);
37424be5ae3Sj_mayer }
37524be5ae3Sj_mayer 
3769fdc60bfSaurel32 /* PowerPC E500 internal IRQ controller */
3779fdc60bfSaurel32 static void ppce500_set_irq(void *opaque, int pin, int level)
3789fdc60bfSaurel32 {
379a0961245SAndreas Färber     PowerPCCPU *cpu = opaque;
380a0961245SAndreas Färber     CPUPPCState *env = &cpu->env;
3819fdc60bfSaurel32     int cur_level;
3829fdc60bfSaurel32 
3839fdc60bfSaurel32     LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
3849fdc60bfSaurel32                 env, pin, level);
3859fdc60bfSaurel32     cur_level = (env->irq_input_state >> pin) & 1;
3869fdc60bfSaurel32     /* Don't generate spurious events */
3879fdc60bfSaurel32     if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
3889fdc60bfSaurel32         switch (pin) {
3899fdc60bfSaurel32         case PPCE500_INPUT_MCK:
3909fdc60bfSaurel32             if (level) {
3919fdc60bfSaurel32                 LOG_IRQ("%s: reset the PowerPC system\n",
3929fdc60bfSaurel32                             __func__);
3939fdc60bfSaurel32                 qemu_system_reset_request();
3949fdc60bfSaurel32             }
3959fdc60bfSaurel32             break;
3969fdc60bfSaurel32         case PPCE500_INPUT_RESET_CORE:
3979fdc60bfSaurel32             if (level) {
3989fdc60bfSaurel32                 LOG_IRQ("%s: reset the PowerPC core\n", __func__);
3997058581aSAndreas Färber                 ppc_set_irq(cpu, PPC_INTERRUPT_MCK, level);
4009fdc60bfSaurel32             }
4019fdc60bfSaurel32             break;
4029fdc60bfSaurel32         case PPCE500_INPUT_CINT:
4039fdc60bfSaurel32             /* Level sensitive - active high */
4049fdc60bfSaurel32             LOG_IRQ("%s: set the critical IRQ state to %d\n",
4059fdc60bfSaurel32                         __func__, level);
4067058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level);
4079fdc60bfSaurel32             break;
4089fdc60bfSaurel32         case PPCE500_INPUT_INT:
4099fdc60bfSaurel32             /* Level sensitive - active high */
4109fdc60bfSaurel32             LOG_IRQ("%s: set the core IRQ state to %d\n",
4119fdc60bfSaurel32                         __func__, level);
4127058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
4139fdc60bfSaurel32             break;
4149fdc60bfSaurel32         case PPCE500_INPUT_DEBUG:
4159fdc60bfSaurel32             /* Level sensitive - active high */
4169fdc60bfSaurel32             LOG_IRQ("%s: set the debug pin state to %d\n",
4179fdc60bfSaurel32                         __func__, level);
4187058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level);
4199fdc60bfSaurel32             break;
4209fdc60bfSaurel32         default:
4219fdc60bfSaurel32             /* Unknown pin - do nothing */
4229fdc60bfSaurel32             LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
4239fdc60bfSaurel32             return;
4249fdc60bfSaurel32         }
4259fdc60bfSaurel32         if (level)
4269fdc60bfSaurel32             env->irq_input_state |= 1 << pin;
4279fdc60bfSaurel32         else
4289fdc60bfSaurel32             env->irq_input_state &= ~(1 << pin);
4299fdc60bfSaurel32     }
4309fdc60bfSaurel32 }
4319fdc60bfSaurel32 
432e2684c0bSAndreas Färber void ppce500_irq_init(CPUPPCState *env)
4339fdc60bfSaurel32 {
434a0961245SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
435a0961245SAndreas Färber 
4369fdc60bfSaurel32     env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq,
437a0961245SAndreas Färber                                                   cpu, PPCE500_INPUT_NB);
4389fdc60bfSaurel32 }
439e49798b1SAlexander Graf 
440e49798b1SAlexander Graf /* Enable or Disable the E500 EPR capability */
441e49798b1SAlexander Graf void ppce500_set_mpic_proxy(bool enabled)
442e49798b1SAlexander Graf {
443e49798b1SAlexander Graf     CPUPPCState *env;
444e49798b1SAlexander Graf 
445e49798b1SAlexander Graf     for (env = first_cpu; env != NULL; env = env->next_cpu) {
4465b95b8b9SAlexander Graf         PowerPCCPU *cpu = ppc_env_get_cpu(env);
4475b95b8b9SAlexander Graf         CPUState *cs = CPU(cpu);
4485b95b8b9SAlexander Graf 
449e49798b1SAlexander Graf         env->mpic_proxy = enabled;
4505b95b8b9SAlexander Graf         if (kvm_enabled()) {
4515b95b8b9SAlexander Graf             kvmppc_set_mpic_proxy(POWERPC_CPU(cs), enabled);
4525b95b8b9SAlexander Graf         }
453e49798b1SAlexander Graf     }
454e49798b1SAlexander Graf }
455e49798b1SAlexander Graf 
4569fddaa0cSbellard /*****************************************************************************/
457e9df014cSj_mayer /* PowerPC time base and decrementer emulation */
4589fddaa0cSbellard 
459ddd1055bSFabien Chouteau uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset)
4609fddaa0cSbellard {
4619fddaa0cSbellard     /* TB time in tb periods */
4626ee093c9SJuan Quintela     return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
4639fddaa0cSbellard }
4649fddaa0cSbellard 
465e2684c0bSAndreas Färber uint64_t cpu_ppc_load_tbl (CPUPPCState *env)
4669fddaa0cSbellard {
467c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
4689fddaa0cSbellard     uint64_t tb;
4699fddaa0cSbellard 
47090dc8812SScott Wood     if (kvm_enabled()) {
47190dc8812SScott Wood         return env->spr[SPR_TBL];
47290dc8812SScott Wood     }
47390dc8812SScott Wood 
47474475455SPaolo Bonzini     tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset);
475d12d51d5Saliguori     LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
4769fddaa0cSbellard 
477e3ea6529SAlexander Graf     return tb;
4789fddaa0cSbellard }
4799fddaa0cSbellard 
480e2684c0bSAndreas Färber static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env)
4819fddaa0cSbellard {
482c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
4839fddaa0cSbellard     uint64_t tb;
4849fddaa0cSbellard 
48574475455SPaolo Bonzini     tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset);
486d12d51d5Saliguori     LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
48776a66253Sj_mayer 
4889fddaa0cSbellard     return tb >> 32;
4899fddaa0cSbellard }
4909fddaa0cSbellard 
491e2684c0bSAndreas Färber uint32_t cpu_ppc_load_tbu (CPUPPCState *env)
4928a84de23Sj_mayer {
49390dc8812SScott Wood     if (kvm_enabled()) {
49490dc8812SScott Wood         return env->spr[SPR_TBU];
49590dc8812SScott Wood     }
49690dc8812SScott Wood 
4978a84de23Sj_mayer     return _cpu_ppc_load_tbu(env);
4988a84de23Sj_mayer }
4998a84de23Sj_mayer 
500c227f099SAnthony Liguori static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk,
501636aa200SBlue Swirl                                     int64_t *tb_offsetp, uint64_t value)
5029fddaa0cSbellard {
5036ee093c9SJuan Quintela     *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec());
504d12d51d5Saliguori     LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n",
505aae9366aSj_mayer                 __func__, value, *tb_offsetp);
506a496775fSj_mayer }
5079fddaa0cSbellard 
508e2684c0bSAndreas Färber void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value)
5099fddaa0cSbellard {
510c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
511a062e36cSj_mayer     uint64_t tb;
5129fddaa0cSbellard 
51374475455SPaolo Bonzini     tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset);
514a062e36cSj_mayer     tb &= 0xFFFFFFFF00000000ULL;
51574475455SPaolo Bonzini     cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock),
516dbdd2506Sj_mayer                      &tb_env->tb_offset, tb | (uint64_t)value);
517a062e36cSj_mayer }
518a062e36cSj_mayer 
519e2684c0bSAndreas Färber static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value)
520a062e36cSj_mayer {
521c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
522a062e36cSj_mayer     uint64_t tb;
523a062e36cSj_mayer 
52474475455SPaolo Bonzini     tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset);
525a062e36cSj_mayer     tb &= 0x00000000FFFFFFFFULL;
52674475455SPaolo Bonzini     cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock),
527dbdd2506Sj_mayer                      &tb_env->tb_offset, ((uint64_t)value << 32) | tb);
528a062e36cSj_mayer }
529a062e36cSj_mayer 
530e2684c0bSAndreas Färber void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value)
5318a84de23Sj_mayer {
5328a84de23Sj_mayer     _cpu_ppc_store_tbu(env, value);
5338a84de23Sj_mayer }
5348a84de23Sj_mayer 
535e2684c0bSAndreas Färber uint64_t cpu_ppc_load_atbl (CPUPPCState *env)
536a062e36cSj_mayer {
537c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
538a062e36cSj_mayer     uint64_t tb;
539a062e36cSj_mayer 
54074475455SPaolo Bonzini     tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset);
541d12d51d5Saliguori     LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
542a062e36cSj_mayer 
543b711de95SAurelien Jarno     return tb;
544a062e36cSj_mayer }
545a062e36cSj_mayer 
546e2684c0bSAndreas Färber uint32_t cpu_ppc_load_atbu (CPUPPCState *env)
547a062e36cSj_mayer {
548c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
549a062e36cSj_mayer     uint64_t tb;
550a062e36cSj_mayer 
55174475455SPaolo Bonzini     tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset);
552d12d51d5Saliguori     LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
553a062e36cSj_mayer 
554a062e36cSj_mayer     return tb >> 32;
555a062e36cSj_mayer }
556a062e36cSj_mayer 
557e2684c0bSAndreas Färber void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value)
558a062e36cSj_mayer {
559c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
560a062e36cSj_mayer     uint64_t tb;
561a062e36cSj_mayer 
56274475455SPaolo Bonzini     tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset);
563a062e36cSj_mayer     tb &= 0xFFFFFFFF00000000ULL;
56474475455SPaolo Bonzini     cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock),
565dbdd2506Sj_mayer                      &tb_env->atb_offset, tb | (uint64_t)value);
566a062e36cSj_mayer }
567a062e36cSj_mayer 
568e2684c0bSAndreas Färber void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value)
569a062e36cSj_mayer {
570c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
571a062e36cSj_mayer     uint64_t tb;
572a062e36cSj_mayer 
57374475455SPaolo Bonzini     tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset);
574a062e36cSj_mayer     tb &= 0x00000000FFFFFFFFULL;
57574475455SPaolo Bonzini     cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock),
576dbdd2506Sj_mayer                      &tb_env->atb_offset, ((uint64_t)value << 32) | tb);
577dbdd2506Sj_mayer }
578dbdd2506Sj_mayer 
579e2684c0bSAndreas Färber static void cpu_ppc_tb_stop (CPUPPCState *env)
580dbdd2506Sj_mayer {
581c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
582dbdd2506Sj_mayer     uint64_t tb, atb, vmclk;
583dbdd2506Sj_mayer 
584dbdd2506Sj_mayer     /* If the time base is already frozen, do nothing */
585dbdd2506Sj_mayer     if (tb_env->tb_freq != 0) {
58674475455SPaolo Bonzini         vmclk = qemu_get_clock_ns(vm_clock);
587dbdd2506Sj_mayer         /* Get the time base */
588dbdd2506Sj_mayer         tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset);
589dbdd2506Sj_mayer         /* Get the alternate time base */
590dbdd2506Sj_mayer         atb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->atb_offset);
591dbdd2506Sj_mayer         /* Store the time base value (ie compute the current offset) */
592dbdd2506Sj_mayer         cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb);
593dbdd2506Sj_mayer         /* Store the alternate time base value (compute the current offset) */
594dbdd2506Sj_mayer         cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb);
595dbdd2506Sj_mayer         /* Set the time base frequency to zero */
596dbdd2506Sj_mayer         tb_env->tb_freq = 0;
597dbdd2506Sj_mayer         /* Now, the time bases are frozen to tb_offset / atb_offset value */
598dbdd2506Sj_mayer     }
599dbdd2506Sj_mayer }
600dbdd2506Sj_mayer 
601e2684c0bSAndreas Färber static void cpu_ppc_tb_start (CPUPPCState *env)
602dbdd2506Sj_mayer {
603c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
604dbdd2506Sj_mayer     uint64_t tb, atb, vmclk;
605dbdd2506Sj_mayer 
606dbdd2506Sj_mayer     /* If the time base is not frozen, do nothing */
607dbdd2506Sj_mayer     if (tb_env->tb_freq == 0) {
60874475455SPaolo Bonzini         vmclk = qemu_get_clock_ns(vm_clock);
609dbdd2506Sj_mayer         /* Get the time base from tb_offset */
610dbdd2506Sj_mayer         tb = tb_env->tb_offset;
611dbdd2506Sj_mayer         /* Get the alternate time base from atb_offset */
612dbdd2506Sj_mayer         atb = tb_env->atb_offset;
613dbdd2506Sj_mayer         /* Restore the tb frequency from the decrementer frequency */
614dbdd2506Sj_mayer         tb_env->tb_freq = tb_env->decr_freq;
615dbdd2506Sj_mayer         /* Store the time base value */
616dbdd2506Sj_mayer         cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb);
617dbdd2506Sj_mayer         /* Store the alternate time base value */
618dbdd2506Sj_mayer         cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb);
619dbdd2506Sj_mayer     }
6209fddaa0cSbellard }
6219fddaa0cSbellard 
622e2684c0bSAndreas Färber static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next)
6239fddaa0cSbellard {
624c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
6259fddaa0cSbellard     uint32_t decr;
6264e588a4dSbellard     int64_t diff;
6279fddaa0cSbellard 
62874475455SPaolo Bonzini     diff = next - qemu_get_clock_ns(vm_clock);
629ddd1055bSFabien Chouteau     if (diff >= 0) {
6306ee093c9SJuan Quintela         decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec());
631ddd1055bSFabien Chouteau     } else if (tb_env->flags & PPC_TIMER_BOOKE) {
632ddd1055bSFabien Chouteau         decr = 0;
633ddd1055bSFabien Chouteau     }  else {
6346ee093c9SJuan Quintela         decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec());
635ddd1055bSFabien Chouteau     }
636d12d51d5Saliguori     LOG_TB("%s: %08" PRIx32 "\n", __func__, decr);
63776a66253Sj_mayer 
6389fddaa0cSbellard     return decr;
6399fddaa0cSbellard }
6409fddaa0cSbellard 
641e2684c0bSAndreas Färber uint32_t cpu_ppc_load_decr (CPUPPCState *env)
64258a7d328Sj_mayer {
643c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
64458a7d328Sj_mayer 
64590dc8812SScott Wood     if (kvm_enabled()) {
64690dc8812SScott Wood         return env->spr[SPR_DECR];
64790dc8812SScott Wood     }
64890dc8812SScott Wood 
649f55e9d9aSTristan Gingold     return _cpu_ppc_load_decr(env, tb_env->decr_next);
65058a7d328Sj_mayer }
65158a7d328Sj_mayer 
652e2684c0bSAndreas Färber uint32_t cpu_ppc_load_hdecr (CPUPPCState *env)
65358a7d328Sj_mayer {
654c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
65558a7d328Sj_mayer 
656f55e9d9aSTristan Gingold     return _cpu_ppc_load_decr(env, tb_env->hdecr_next);
65758a7d328Sj_mayer }
65858a7d328Sj_mayer 
659e2684c0bSAndreas Färber uint64_t cpu_ppc_load_purr (CPUPPCState *env)
66058a7d328Sj_mayer {
661c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
66258a7d328Sj_mayer     uint64_t diff;
66358a7d328Sj_mayer 
66474475455SPaolo Bonzini     diff = qemu_get_clock_ns(vm_clock) - tb_env->purr_start;
66558a7d328Sj_mayer 
6666ee093c9SJuan Quintela     return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec());
66758a7d328Sj_mayer }
66858a7d328Sj_mayer 
6699fddaa0cSbellard /* When decrementer expires,
6709fddaa0cSbellard  * all we need to do is generate or queue a CPU exception
6719fddaa0cSbellard  */
6727e0a9247SAndreas Färber static inline void cpu_ppc_decr_excp(PowerPCCPU *cpu)
6739fddaa0cSbellard {
6749fddaa0cSbellard     /* Raise it */
675d12d51d5Saliguori     LOG_TB("raise decrementer exception\n");
6767058581aSAndreas Färber     ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 1);
6779fddaa0cSbellard }
6789fddaa0cSbellard 
6797e0a9247SAndreas Färber static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu)
68058a7d328Sj_mayer {
68158a7d328Sj_mayer     /* Raise it */
682d12d51d5Saliguori     LOG_TB("raise decrementer exception\n");
6837058581aSAndreas Färber     ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
68458a7d328Sj_mayer }
68558a7d328Sj_mayer 
6867e0a9247SAndreas Färber static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp,
68758a7d328Sj_mayer                                  struct QEMUTimer *timer,
6887e0a9247SAndreas Färber                                  void (*raise_excp)(PowerPCCPU *),
68958a7d328Sj_mayer                                  uint32_t decr, uint32_t value,
69058a7d328Sj_mayer                                  int is_excp)
6919fddaa0cSbellard {
6927e0a9247SAndreas Färber     CPUPPCState *env = &cpu->env;
693c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
6949fddaa0cSbellard     uint64_t now, next;
6959fddaa0cSbellard 
696d12d51d5Saliguori     LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__,
697aae9366aSj_mayer                 decr, value);
69855f7d4b0SDavid Gibson 
69955f7d4b0SDavid Gibson     if (kvm_enabled()) {
70055f7d4b0SDavid Gibson         /* KVM handles decrementer exceptions, we don't need our own timer */
70155f7d4b0SDavid Gibson         return;
70255f7d4b0SDavid Gibson     }
70355f7d4b0SDavid Gibson 
70474475455SPaolo Bonzini     now = qemu_get_clock_ns(vm_clock);
7056ee093c9SJuan Quintela     next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
706ddd1055bSFabien Chouteau     if (is_excp) {
70758a7d328Sj_mayer         next += *nextp - now;
708ddd1055bSFabien Chouteau     }
709ddd1055bSFabien Chouteau     if (next == now) {
7109fddaa0cSbellard         next++;
711ddd1055bSFabien Chouteau     }
71258a7d328Sj_mayer     *nextp = next;
7139fddaa0cSbellard     /* Adjust timer */
71458a7d328Sj_mayer     qemu_mod_timer(timer, next);
715ddd1055bSFabien Chouteau 
716ddd1055bSFabien Chouteau     /* If we set a negative value and the decrementer was positive, raise an
717ddd1055bSFabien Chouteau      * exception.
7189fddaa0cSbellard      */
719ddd1055bSFabien Chouteau     if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED)
720ddd1055bSFabien Chouteau         && (value & 0x80000000)
721ddd1055bSFabien Chouteau         && !(decr & 0x80000000)) {
7227e0a9247SAndreas Färber         (*raise_excp)(cpu);
72358a7d328Sj_mayer     }
724ddd1055bSFabien Chouteau }
72558a7d328Sj_mayer 
7267e0a9247SAndreas Färber static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, uint32_t decr,
72758a7d328Sj_mayer                                        uint32_t value, int is_excp)
72858a7d328Sj_mayer {
7297e0a9247SAndreas Färber     ppc_tb_t *tb_env = cpu->env.tb_env;
73058a7d328Sj_mayer 
7317e0a9247SAndreas Färber     __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer,
73258a7d328Sj_mayer                          &cpu_ppc_decr_excp, decr, value, is_excp);
7339fddaa0cSbellard }
7349fddaa0cSbellard 
735e2684c0bSAndreas Färber void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value)
7369fddaa0cSbellard {
7377e0a9247SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
7387e0a9247SAndreas Färber 
7397e0a9247SAndreas Färber     _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, 0);
7409fddaa0cSbellard }
7419fddaa0cSbellard 
7429fddaa0cSbellard static void cpu_ppc_decr_cb(void *opaque)
7439fddaa0cSbellard {
74450c680f0SAndreas Färber     PowerPCCPU *cpu = opaque;
7457e0a9247SAndreas Färber 
74650c680f0SAndreas Färber     _cpu_ppc_store_decr(cpu, 0x00000000, 0xFFFFFFFF, 1);
7479fddaa0cSbellard }
7489fddaa0cSbellard 
7497e0a9247SAndreas Färber static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, uint32_t hdecr,
75058a7d328Sj_mayer                                         uint32_t value, int is_excp)
75158a7d328Sj_mayer {
7527e0a9247SAndreas Färber     ppc_tb_t *tb_env = cpu->env.tb_env;
75358a7d328Sj_mayer 
754b172c56aSj_mayer     if (tb_env->hdecr_timer != NULL) {
7557e0a9247SAndreas Färber         __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer,
75658a7d328Sj_mayer                              &cpu_ppc_hdecr_excp, hdecr, value, is_excp);
75758a7d328Sj_mayer     }
758b172c56aSj_mayer }
75958a7d328Sj_mayer 
760e2684c0bSAndreas Färber void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value)
76158a7d328Sj_mayer {
7627e0a9247SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
7637e0a9247SAndreas Färber 
7647e0a9247SAndreas Färber     _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value, 0);
76558a7d328Sj_mayer }
76658a7d328Sj_mayer 
76758a7d328Sj_mayer static void cpu_ppc_hdecr_cb(void *opaque)
76858a7d328Sj_mayer {
76950c680f0SAndreas Färber     PowerPCCPU *cpu = opaque;
7707e0a9247SAndreas Färber 
77150c680f0SAndreas Färber     _cpu_ppc_store_hdecr(cpu, 0x00000000, 0xFFFFFFFF, 1);
77258a7d328Sj_mayer }
77358a7d328Sj_mayer 
7747e0a9247SAndreas Färber static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value)
77558a7d328Sj_mayer {
7767e0a9247SAndreas Färber     ppc_tb_t *tb_env = cpu->env.tb_env;
77758a7d328Sj_mayer 
77858a7d328Sj_mayer     tb_env->purr_load = value;
77974475455SPaolo Bonzini     tb_env->purr_start = qemu_get_clock_ns(vm_clock);
78058a7d328Sj_mayer }
78158a7d328Sj_mayer 
7828ecc7913Sj_mayer static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
7838ecc7913Sj_mayer {
784e2684c0bSAndreas Färber     CPUPPCState *env = opaque;
7857e0a9247SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
786c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
7878ecc7913Sj_mayer 
7888ecc7913Sj_mayer     tb_env->tb_freq = freq;
789dbdd2506Sj_mayer     tb_env->decr_freq = freq;
7908ecc7913Sj_mayer     /* There is a bug in Linux 2.4 kernels:
7918ecc7913Sj_mayer      * if a decrementer exception is pending when it enables msr_ee at startup,
7928ecc7913Sj_mayer      * it's not ready to handle it...
7938ecc7913Sj_mayer      */
7947e0a9247SAndreas Färber     _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 0);
7957e0a9247SAndreas Färber     _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 0);
7967e0a9247SAndreas Färber     cpu_ppc_store_purr(cpu, 0x0000000000000000ULL);
7978ecc7913Sj_mayer }
7988ecc7913Sj_mayer 
7999fddaa0cSbellard /* Set up (once) timebase frequency (in Hz) */
800e2684c0bSAndreas Färber clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
8019fddaa0cSbellard {
80250c680f0SAndreas Färber     PowerPCCPU *cpu = ppc_env_get_cpu(env);
803c227f099SAnthony Liguori     ppc_tb_t *tb_env;
8049fddaa0cSbellard 
8057267c094SAnthony Liguori     tb_env = g_malloc0(sizeof(ppc_tb_t));
8069fddaa0cSbellard     env->tb_env = tb_env;
807ddd1055bSFabien Chouteau     tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
8089fddaa0cSbellard     /* Create new timer */
80950c680f0SAndreas Färber     tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, cpu);
810b172c56aSj_mayer     if (0) {
811b172c56aSj_mayer         /* XXX: find a suitable condition to enable the hypervisor decrementer
812b172c56aSj_mayer          */
81350c680f0SAndreas Färber         tb_env->hdecr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_hdecr_cb,
81450c680f0SAndreas Färber                                                 cpu);
815b172c56aSj_mayer     } else {
816b172c56aSj_mayer         tb_env->hdecr_timer = NULL;
817b172c56aSj_mayer     }
8188ecc7913Sj_mayer     cpu_ppc_set_tb_clk(env, freq);
8199fddaa0cSbellard 
8208ecc7913Sj_mayer     return &cpu_ppc_set_tb_clk;
8219fddaa0cSbellard }
8229fddaa0cSbellard 
82376a66253Sj_mayer /* Specific helpers for POWER & PowerPC 601 RTC */
824b1d8e52eSblueswir1 #if 0
825e2684c0bSAndreas Färber static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env)
82676a66253Sj_mayer {
82776a66253Sj_mayer     return cpu_ppc_tb_init(env, 7812500);
82876a66253Sj_mayer }
829b1d8e52eSblueswir1 #endif
83076a66253Sj_mayer 
831e2684c0bSAndreas Färber void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value)
8328a84de23Sj_mayer {
8338a84de23Sj_mayer     _cpu_ppc_store_tbu(env, value);
8348a84de23Sj_mayer }
83576a66253Sj_mayer 
836e2684c0bSAndreas Färber uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env)
8378a84de23Sj_mayer {
8388a84de23Sj_mayer     return _cpu_ppc_load_tbu(env);
8398a84de23Sj_mayer }
84076a66253Sj_mayer 
841e2684c0bSAndreas Färber void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value)
84276a66253Sj_mayer {
84376a66253Sj_mayer     cpu_ppc_store_tbl(env, value & 0x3FFFFF80);
84476a66253Sj_mayer }
84576a66253Sj_mayer 
846e2684c0bSAndreas Färber uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env)
84776a66253Sj_mayer {
84876a66253Sj_mayer     return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
84976a66253Sj_mayer }
85076a66253Sj_mayer 
851636aaad7Sj_mayer /*****************************************************************************/
852ddd1055bSFabien Chouteau /* PowerPC 40x timers */
853636aaad7Sj_mayer 
854636aaad7Sj_mayer /* PIT, FIT & WDT */
855ddd1055bSFabien Chouteau typedef struct ppc40x_timer_t ppc40x_timer_t;
856ddd1055bSFabien Chouteau struct ppc40x_timer_t {
857636aaad7Sj_mayer     uint64_t pit_reload;  /* PIT auto-reload value        */
858636aaad7Sj_mayer     uint64_t fit_next;    /* Tick for next FIT interrupt  */
859636aaad7Sj_mayer     struct QEMUTimer *fit_timer;
860636aaad7Sj_mayer     uint64_t wdt_next;    /* Tick for next WDT interrupt  */
861636aaad7Sj_mayer     struct QEMUTimer *wdt_timer;
862d63cb48dSEdgar E. Iglesias 
863d63cb48dSEdgar E. Iglesias     /* 405 have the PIT, 440 have a DECR.  */
864d63cb48dSEdgar E. Iglesias     unsigned int decr_excp;
865636aaad7Sj_mayer };
866636aaad7Sj_mayer 
867636aaad7Sj_mayer /* Fixed interval timer */
868636aaad7Sj_mayer static void cpu_4xx_fit_cb (void *opaque)
86976a66253Sj_mayer {
8707058581aSAndreas Färber     PowerPCCPU *cpu;
871e2684c0bSAndreas Färber     CPUPPCState *env;
872c227f099SAnthony Liguori     ppc_tb_t *tb_env;
873ddd1055bSFabien Chouteau     ppc40x_timer_t *ppc40x_timer;
874636aaad7Sj_mayer     uint64_t now, next;
875636aaad7Sj_mayer 
876636aaad7Sj_mayer     env = opaque;
8777058581aSAndreas Färber     cpu = ppc_env_get_cpu(env);
878636aaad7Sj_mayer     tb_env = env->tb_env;
879ddd1055bSFabien Chouteau     ppc40x_timer = tb_env->opaque;
88074475455SPaolo Bonzini     now = qemu_get_clock_ns(vm_clock);
881636aaad7Sj_mayer     switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
882636aaad7Sj_mayer     case 0:
883636aaad7Sj_mayer         next = 1 << 9;
884636aaad7Sj_mayer         break;
885636aaad7Sj_mayer     case 1:
886636aaad7Sj_mayer         next = 1 << 13;
887636aaad7Sj_mayer         break;
888636aaad7Sj_mayer     case 2:
889636aaad7Sj_mayer         next = 1 << 17;
890636aaad7Sj_mayer         break;
891636aaad7Sj_mayer     case 3:
892636aaad7Sj_mayer         next = 1 << 21;
893636aaad7Sj_mayer         break;
894636aaad7Sj_mayer     default:
895636aaad7Sj_mayer         /* Cannot occur, but makes gcc happy */
896636aaad7Sj_mayer         return;
897636aaad7Sj_mayer     }
8986ee093c9SJuan Quintela     next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq);
899636aaad7Sj_mayer     if (next == now)
900636aaad7Sj_mayer         next++;
901ddd1055bSFabien Chouteau     qemu_mod_timer(ppc40x_timer->fit_timer, next);
902636aaad7Sj_mayer     env->spr[SPR_40x_TSR] |= 1 << 26;
9037058581aSAndreas Färber     if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) {
9047058581aSAndreas Färber         ppc_set_irq(cpu, PPC_INTERRUPT_FIT, 1);
9057058581aSAndreas Färber     }
90690e189ecSBlue Swirl     LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
907e96efcfcSj_mayer            (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1),
908636aaad7Sj_mayer            env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
909636aaad7Sj_mayer }
910636aaad7Sj_mayer 
911636aaad7Sj_mayer /* Programmable interval timer */
912e2684c0bSAndreas Färber static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp)
913636aaad7Sj_mayer {
914ddd1055bSFabien Chouteau     ppc40x_timer_t *ppc40x_timer;
915636aaad7Sj_mayer     uint64_t now, next;
916636aaad7Sj_mayer 
917ddd1055bSFabien Chouteau     ppc40x_timer = tb_env->opaque;
918ddd1055bSFabien Chouteau     if (ppc40x_timer->pit_reload <= 1 ||
9194b6d0a4cSj_mayer         !((env->spr[SPR_40x_TCR] >> 26) & 0x1) ||
9204b6d0a4cSj_mayer         (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) {
9214b6d0a4cSj_mayer         /* Stop PIT */
922d12d51d5Saliguori         LOG_TB("%s: stop PIT\n", __func__);
9234b6d0a4cSj_mayer         qemu_del_timer(tb_env->decr_timer);
9244b6d0a4cSj_mayer     } else {
925d12d51d5Saliguori         LOG_TB("%s: start PIT %016" PRIx64 "\n",
926ddd1055bSFabien Chouteau                     __func__, ppc40x_timer->pit_reload);
92774475455SPaolo Bonzini         now = qemu_get_clock_ns(vm_clock);
928ddd1055bSFabien Chouteau         next = now + muldiv64(ppc40x_timer->pit_reload,
9296ee093c9SJuan Quintela                               get_ticks_per_sec(), tb_env->decr_freq);
9304b6d0a4cSj_mayer         if (is_excp)
9314b6d0a4cSj_mayer             next += tb_env->decr_next - now;
932636aaad7Sj_mayer         if (next == now)
933636aaad7Sj_mayer             next++;
934636aaad7Sj_mayer         qemu_mod_timer(tb_env->decr_timer, next);
935636aaad7Sj_mayer         tb_env->decr_next = next;
936636aaad7Sj_mayer     }
9374b6d0a4cSj_mayer }
9384b6d0a4cSj_mayer 
9394b6d0a4cSj_mayer static void cpu_4xx_pit_cb (void *opaque)
9404b6d0a4cSj_mayer {
9417058581aSAndreas Färber     PowerPCCPU *cpu;
942e2684c0bSAndreas Färber     CPUPPCState *env;
943c227f099SAnthony Liguori     ppc_tb_t *tb_env;
944ddd1055bSFabien Chouteau     ppc40x_timer_t *ppc40x_timer;
9454b6d0a4cSj_mayer 
9464b6d0a4cSj_mayer     env = opaque;
9477058581aSAndreas Färber     cpu = ppc_env_get_cpu(env);
9484b6d0a4cSj_mayer     tb_env = env->tb_env;
949ddd1055bSFabien Chouteau     ppc40x_timer = tb_env->opaque;
950636aaad7Sj_mayer     env->spr[SPR_40x_TSR] |= 1 << 27;
9517058581aSAndreas Färber     if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) {
9527058581aSAndreas Färber         ppc_set_irq(cpu, ppc40x_timer->decr_excp, 1);
9537058581aSAndreas Färber     }
9544b6d0a4cSj_mayer     start_stop_pit(env, tb_env, 1);
95590e189ecSBlue Swirl     LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " "
956e96efcfcSj_mayer            "%016" PRIx64 "\n", __func__,
957e96efcfcSj_mayer            (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1),
958e96efcfcSj_mayer            (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1),
959636aaad7Sj_mayer            env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
960ddd1055bSFabien Chouteau            ppc40x_timer->pit_reload);
961636aaad7Sj_mayer }
962636aaad7Sj_mayer 
963636aaad7Sj_mayer /* Watchdog timer */
964636aaad7Sj_mayer static void cpu_4xx_wdt_cb (void *opaque)
965636aaad7Sj_mayer {
9667058581aSAndreas Färber     PowerPCCPU *cpu;
967e2684c0bSAndreas Färber     CPUPPCState *env;
968c227f099SAnthony Liguori     ppc_tb_t *tb_env;
969ddd1055bSFabien Chouteau     ppc40x_timer_t *ppc40x_timer;
970636aaad7Sj_mayer     uint64_t now, next;
971636aaad7Sj_mayer 
972636aaad7Sj_mayer     env = opaque;
9737058581aSAndreas Färber     cpu = ppc_env_get_cpu(env);
974636aaad7Sj_mayer     tb_env = env->tb_env;
975ddd1055bSFabien Chouteau     ppc40x_timer = tb_env->opaque;
97674475455SPaolo Bonzini     now = qemu_get_clock_ns(vm_clock);
977636aaad7Sj_mayer     switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
978636aaad7Sj_mayer     case 0:
979636aaad7Sj_mayer         next = 1 << 17;
980636aaad7Sj_mayer         break;
981636aaad7Sj_mayer     case 1:
982636aaad7Sj_mayer         next = 1 << 21;
983636aaad7Sj_mayer         break;
984636aaad7Sj_mayer     case 2:
985636aaad7Sj_mayer         next = 1 << 25;
986636aaad7Sj_mayer         break;
987636aaad7Sj_mayer     case 3:
988636aaad7Sj_mayer         next = 1 << 29;
989636aaad7Sj_mayer         break;
990636aaad7Sj_mayer     default:
991636aaad7Sj_mayer         /* Cannot occur, but makes gcc happy */
992636aaad7Sj_mayer         return;
993636aaad7Sj_mayer     }
9946ee093c9SJuan Quintela     next = now + muldiv64(next, get_ticks_per_sec(), tb_env->decr_freq);
995636aaad7Sj_mayer     if (next == now)
996636aaad7Sj_mayer         next++;
99790e189ecSBlue Swirl     LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
998636aaad7Sj_mayer            env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
999636aaad7Sj_mayer     switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
1000636aaad7Sj_mayer     case 0x0:
1001636aaad7Sj_mayer     case 0x1:
1002ddd1055bSFabien Chouteau         qemu_mod_timer(ppc40x_timer->wdt_timer, next);
1003ddd1055bSFabien Chouteau         ppc40x_timer->wdt_next = next;
1004636aaad7Sj_mayer         env->spr[SPR_40x_TSR] |= 1 << 31;
1005636aaad7Sj_mayer         break;
1006636aaad7Sj_mayer     case 0x2:
1007ddd1055bSFabien Chouteau         qemu_mod_timer(ppc40x_timer->wdt_timer, next);
1008ddd1055bSFabien Chouteau         ppc40x_timer->wdt_next = next;
1009636aaad7Sj_mayer         env->spr[SPR_40x_TSR] |= 1 << 30;
10107058581aSAndreas Färber         if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) {
10117058581aSAndreas Färber             ppc_set_irq(cpu, PPC_INTERRUPT_WDT, 1);
10127058581aSAndreas Färber         }
1013636aaad7Sj_mayer         break;
1014636aaad7Sj_mayer     case 0x3:
1015636aaad7Sj_mayer         env->spr[SPR_40x_TSR] &= ~0x30000000;
1016636aaad7Sj_mayer         env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000;
1017636aaad7Sj_mayer         switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) {
1018636aaad7Sj_mayer         case 0x0:
1019636aaad7Sj_mayer             /* No reset */
1020636aaad7Sj_mayer             break;
1021636aaad7Sj_mayer         case 0x1: /* Core reset */
1022f3273ba6SAndreas Färber             ppc40x_core_reset(cpu);
10238ecc7913Sj_mayer             break;
1024636aaad7Sj_mayer         case 0x2: /* Chip reset */
1025f3273ba6SAndreas Färber             ppc40x_chip_reset(cpu);
10268ecc7913Sj_mayer             break;
1027636aaad7Sj_mayer         case 0x3: /* System reset */
1028f3273ba6SAndreas Färber             ppc40x_system_reset(cpu);
10298ecc7913Sj_mayer             break;
1030636aaad7Sj_mayer         }
1031636aaad7Sj_mayer     }
103276a66253Sj_mayer }
103376a66253Sj_mayer 
1034e2684c0bSAndreas Färber void store_40x_pit (CPUPPCState *env, target_ulong val)
103576a66253Sj_mayer {
1036c227f099SAnthony Liguori     ppc_tb_t *tb_env;
1037ddd1055bSFabien Chouteau     ppc40x_timer_t *ppc40x_timer;
1038636aaad7Sj_mayer 
1039636aaad7Sj_mayer     tb_env = env->tb_env;
1040ddd1055bSFabien Chouteau     ppc40x_timer = tb_env->opaque;
104190e189ecSBlue Swirl     LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val);
1042ddd1055bSFabien Chouteau     ppc40x_timer->pit_reload = val;
10434b6d0a4cSj_mayer     start_stop_pit(env, tb_env, 0);
104476a66253Sj_mayer }
104576a66253Sj_mayer 
1046e2684c0bSAndreas Färber target_ulong load_40x_pit (CPUPPCState *env)
104776a66253Sj_mayer {
1048636aaad7Sj_mayer     return cpu_ppc_load_decr(env);
104976a66253Sj_mayer }
105076a66253Sj_mayer 
1051ddd1055bSFabien Chouteau static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq)
10524b6d0a4cSj_mayer {
1053e2684c0bSAndreas Färber     CPUPPCState *env = opaque;
1054c227f099SAnthony Liguori     ppc_tb_t *tb_env = env->tb_env;
10554b6d0a4cSj_mayer 
1056d12d51d5Saliguori     LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__,
1057aae9366aSj_mayer                 freq);
10584b6d0a4cSj_mayer     tb_env->tb_freq = freq;
1059dbdd2506Sj_mayer     tb_env->decr_freq = freq;
10604b6d0a4cSj_mayer     /* XXX: we should also update all timers */
10614b6d0a4cSj_mayer }
10624b6d0a4cSj_mayer 
1063e2684c0bSAndreas Färber clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq,
1064d63cb48dSEdgar E. Iglesias                                   unsigned int decr_excp)
1065636aaad7Sj_mayer {
1066c227f099SAnthony Liguori     ppc_tb_t *tb_env;
1067ddd1055bSFabien Chouteau     ppc40x_timer_t *ppc40x_timer;
1068636aaad7Sj_mayer 
10697267c094SAnthony Liguori     tb_env = g_malloc0(sizeof(ppc_tb_t));
10708ecc7913Sj_mayer     env->tb_env = tb_env;
1071ddd1055bSFabien Chouteau     tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
1072ddd1055bSFabien Chouteau     ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t));
10738ecc7913Sj_mayer     tb_env->tb_freq = freq;
1074dbdd2506Sj_mayer     tb_env->decr_freq = freq;
1075ddd1055bSFabien Chouteau     tb_env->opaque = ppc40x_timer;
1076d12d51d5Saliguori     LOG_TB("%s freq %" PRIu32 "\n", __func__, freq);
1077ddd1055bSFabien Chouteau     if (ppc40x_timer != NULL) {
1078636aaad7Sj_mayer         /* We use decr timer for PIT */
107974475455SPaolo Bonzini         tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env);
1080ddd1055bSFabien Chouteau         ppc40x_timer->fit_timer =
108174475455SPaolo Bonzini             qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env);
1082ddd1055bSFabien Chouteau         ppc40x_timer->wdt_timer =
108374475455SPaolo Bonzini             qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env);
1084ddd1055bSFabien Chouteau         ppc40x_timer->decr_excp = decr_excp;
1085636aaad7Sj_mayer     }
10868ecc7913Sj_mayer 
1087ddd1055bSFabien Chouteau     return &ppc_40x_set_tb_clk;
108876a66253Sj_mayer }
108976a66253Sj_mayer 
10902e719ba3Sj_mayer /*****************************************************************************/
10912e719ba3Sj_mayer /* Embedded PowerPC Device Control Registers */
1092c227f099SAnthony Liguori typedef struct ppc_dcrn_t ppc_dcrn_t;
1093c227f099SAnthony Liguori struct ppc_dcrn_t {
10942e719ba3Sj_mayer     dcr_read_cb dcr_read;
10952e719ba3Sj_mayer     dcr_write_cb dcr_write;
10962e719ba3Sj_mayer     void *opaque;
10972e719ba3Sj_mayer };
10982e719ba3Sj_mayer 
1099a750fc0bSj_mayer /* XXX: on 460, DCR addresses are 32 bits wide,
1100a750fc0bSj_mayer  *      using DCRIPR to get the 22 upper bits of the DCR address
1101a750fc0bSj_mayer  */
11022e719ba3Sj_mayer #define DCRN_NB 1024
1103c227f099SAnthony Liguori struct ppc_dcr_t {
1104c227f099SAnthony Liguori     ppc_dcrn_t dcrn[DCRN_NB];
11052e719ba3Sj_mayer     int (*read_error)(int dcrn);
11062e719ba3Sj_mayer     int (*write_error)(int dcrn);
11072e719ba3Sj_mayer };
11082e719ba3Sj_mayer 
110973b01960SAlexander Graf int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
11102e719ba3Sj_mayer {
1111c227f099SAnthony Liguori     ppc_dcrn_t *dcr;
11122e719ba3Sj_mayer 
11132e719ba3Sj_mayer     if (dcrn < 0 || dcrn >= DCRN_NB)
11142e719ba3Sj_mayer         goto error;
11152e719ba3Sj_mayer     dcr = &dcr_env->dcrn[dcrn];
11162e719ba3Sj_mayer     if (dcr->dcr_read == NULL)
11172e719ba3Sj_mayer         goto error;
11182e719ba3Sj_mayer     *valp = (*dcr->dcr_read)(dcr->opaque, dcrn);
11192e719ba3Sj_mayer 
11202e719ba3Sj_mayer     return 0;
11212e719ba3Sj_mayer 
11222e719ba3Sj_mayer  error:
11232e719ba3Sj_mayer     if (dcr_env->read_error != NULL)
11242e719ba3Sj_mayer         return (*dcr_env->read_error)(dcrn);
11252e719ba3Sj_mayer 
11262e719ba3Sj_mayer     return -1;
11272e719ba3Sj_mayer }
11282e719ba3Sj_mayer 
112973b01960SAlexander Graf int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
11302e719ba3Sj_mayer {
1131c227f099SAnthony Liguori     ppc_dcrn_t *dcr;
11322e719ba3Sj_mayer 
11332e719ba3Sj_mayer     if (dcrn < 0 || dcrn >= DCRN_NB)
11342e719ba3Sj_mayer         goto error;
11352e719ba3Sj_mayer     dcr = &dcr_env->dcrn[dcrn];
11362e719ba3Sj_mayer     if (dcr->dcr_write == NULL)
11372e719ba3Sj_mayer         goto error;
11382e719ba3Sj_mayer     (*dcr->dcr_write)(dcr->opaque, dcrn, val);
11392e719ba3Sj_mayer 
11402e719ba3Sj_mayer     return 0;
11412e719ba3Sj_mayer 
11422e719ba3Sj_mayer  error:
11432e719ba3Sj_mayer     if (dcr_env->write_error != NULL)
11442e719ba3Sj_mayer         return (*dcr_env->write_error)(dcrn);
11452e719ba3Sj_mayer 
11462e719ba3Sj_mayer     return -1;
11472e719ba3Sj_mayer }
11482e719ba3Sj_mayer 
1149e2684c0bSAndreas Färber int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque,
11502e719ba3Sj_mayer                       dcr_read_cb dcr_read, dcr_write_cb dcr_write)
11512e719ba3Sj_mayer {
1152c227f099SAnthony Liguori     ppc_dcr_t *dcr_env;
1153c227f099SAnthony Liguori     ppc_dcrn_t *dcr;
11542e719ba3Sj_mayer 
11552e719ba3Sj_mayer     dcr_env = env->dcr_env;
11562e719ba3Sj_mayer     if (dcr_env == NULL)
11572e719ba3Sj_mayer         return -1;
11582e719ba3Sj_mayer     if (dcrn < 0 || dcrn >= DCRN_NB)
11592e719ba3Sj_mayer         return -1;
11602e719ba3Sj_mayer     dcr = &dcr_env->dcrn[dcrn];
11612e719ba3Sj_mayer     if (dcr->opaque != NULL ||
11622e719ba3Sj_mayer         dcr->dcr_read != NULL ||
11632e719ba3Sj_mayer         dcr->dcr_write != NULL)
11642e719ba3Sj_mayer         return -1;
11652e719ba3Sj_mayer     dcr->opaque = opaque;
11662e719ba3Sj_mayer     dcr->dcr_read = dcr_read;
11672e719ba3Sj_mayer     dcr->dcr_write = dcr_write;
11682e719ba3Sj_mayer 
11692e719ba3Sj_mayer     return 0;
11702e719ba3Sj_mayer }
11712e719ba3Sj_mayer 
1172e2684c0bSAndreas Färber int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn),
11732e719ba3Sj_mayer                   int (*write_error)(int dcrn))
11742e719ba3Sj_mayer {
1175c227f099SAnthony Liguori     ppc_dcr_t *dcr_env;
11762e719ba3Sj_mayer 
11777267c094SAnthony Liguori     dcr_env = g_malloc0(sizeof(ppc_dcr_t));
11782e719ba3Sj_mayer     dcr_env->read_error = read_error;
11792e719ba3Sj_mayer     dcr_env->write_error = write_error;
11802e719ba3Sj_mayer     env->dcr_env = dcr_env;
11812e719ba3Sj_mayer 
11822e719ba3Sj_mayer     return 0;
11832e719ba3Sj_mayer }
11842e719ba3Sj_mayer 
118564201201Sbellard /*****************************************************************************/
118664201201Sbellard /* Debug port */
1187fd0bbb12Sbellard void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
118864201201Sbellard {
118964201201Sbellard     addr &= 0xF;
119064201201Sbellard     switch (addr) {
119164201201Sbellard     case 0:
119264201201Sbellard         printf("%c", val);
119364201201Sbellard         break;
119464201201Sbellard     case 1:
119564201201Sbellard         printf("\n");
119664201201Sbellard         fflush(stdout);
119764201201Sbellard         break;
119864201201Sbellard     case 2:
1199aae9366aSj_mayer         printf("Set loglevel to %04" PRIx32 "\n", val);
120024537a01SPeter Maydell         qemu_set_log(val | 0x100);
120164201201Sbellard         break;
120264201201Sbellard     }
120364201201Sbellard }
120464201201Sbellard 
120564201201Sbellard /*****************************************************************************/
120664201201Sbellard /* NVRAM helpers */
1207c227f099SAnthony Liguori static inline uint32_t nvram_read (nvram_t *nvram, uint32_t addr)
120864201201Sbellard {
12093a93113aSDong Xu Wang     return (*nvram->read_fn)(nvram->opaque, addr);
121064201201Sbellard }
121164201201Sbellard 
1212c227f099SAnthony Liguori static inline void nvram_write (nvram_t *nvram, uint32_t addr, uint32_t val)
121364201201Sbellard {
12143cbee15bSj_mayer     (*nvram->write_fn)(nvram->opaque, addr, val);
121564201201Sbellard }
121664201201Sbellard 
121743448292SBlue Swirl static void NVRAM_set_byte(nvram_t *nvram, uint32_t addr, uint8_t value)
121864201201Sbellard {
12193cbee15bSj_mayer     nvram_write(nvram, addr, value);
122064201201Sbellard }
122164201201Sbellard 
122243448292SBlue Swirl static uint8_t NVRAM_get_byte(nvram_t *nvram, uint32_t addr)
12233cbee15bSj_mayer {
12243cbee15bSj_mayer     return nvram_read(nvram, addr);
12253cbee15bSj_mayer }
12263cbee15bSj_mayer 
122743448292SBlue Swirl static void NVRAM_set_word(nvram_t *nvram, uint32_t addr, uint16_t value)
12283cbee15bSj_mayer {
12293cbee15bSj_mayer     nvram_write(nvram, addr, value >> 8);
12303cbee15bSj_mayer     nvram_write(nvram, addr + 1, value & 0xFF);
12313cbee15bSj_mayer }
12323cbee15bSj_mayer 
123343448292SBlue Swirl static uint16_t NVRAM_get_word(nvram_t *nvram, uint32_t addr)
123464201201Sbellard {
123564201201Sbellard     uint16_t tmp;
123664201201Sbellard 
12373cbee15bSj_mayer     tmp = nvram_read(nvram, addr) << 8;
12383cbee15bSj_mayer     tmp |= nvram_read(nvram, addr + 1);
12393cbee15bSj_mayer 
124064201201Sbellard     return tmp;
124164201201Sbellard }
124264201201Sbellard 
124343448292SBlue Swirl static void NVRAM_set_lword(nvram_t *nvram, uint32_t addr, uint32_t value)
124464201201Sbellard {
12453cbee15bSj_mayer     nvram_write(nvram, addr, value >> 24);
12463cbee15bSj_mayer     nvram_write(nvram, addr + 1, (value >> 16) & 0xFF);
12473cbee15bSj_mayer     nvram_write(nvram, addr + 2, (value >> 8) & 0xFF);
12483cbee15bSj_mayer     nvram_write(nvram, addr + 3, value & 0xFF);
124964201201Sbellard }
125064201201Sbellard 
1251c227f099SAnthony Liguori uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr)
125264201201Sbellard {
125364201201Sbellard     uint32_t tmp;
125464201201Sbellard 
12553cbee15bSj_mayer     tmp = nvram_read(nvram, addr) << 24;
12563cbee15bSj_mayer     tmp |= nvram_read(nvram, addr + 1) << 16;
12573cbee15bSj_mayer     tmp |= nvram_read(nvram, addr + 2) << 8;
12583cbee15bSj_mayer     tmp |= nvram_read(nvram, addr + 3);
125976a66253Sj_mayer 
126064201201Sbellard     return tmp;
126164201201Sbellard }
126264201201Sbellard 
126343448292SBlue Swirl static void NVRAM_set_string(nvram_t *nvram, uint32_t addr, const char *str,
126443448292SBlue Swirl                              uint32_t max)
126564201201Sbellard {
126664201201Sbellard     int i;
126764201201Sbellard 
126864201201Sbellard     for (i = 0; i < max && str[i] != '\0'; i++) {
12693cbee15bSj_mayer         nvram_write(nvram, addr + i, str[i]);
127064201201Sbellard     }
12713cbee15bSj_mayer     nvram_write(nvram, addr + i, str[i]);
12723cbee15bSj_mayer     nvram_write(nvram, addr + max - 1, '\0');
127364201201Sbellard }
127464201201Sbellard 
1275c227f099SAnthony Liguori int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max)
127664201201Sbellard {
127764201201Sbellard     int i;
127864201201Sbellard 
127964201201Sbellard     memset(dst, 0, max);
128064201201Sbellard     for (i = 0; i < max; i++) {
128164201201Sbellard         dst[i] = NVRAM_get_byte(nvram, addr + i);
128264201201Sbellard         if (dst[i] == '\0')
128364201201Sbellard             break;
128464201201Sbellard     }
128564201201Sbellard 
128664201201Sbellard     return i;
128764201201Sbellard }
128864201201Sbellard 
128964201201Sbellard static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
129064201201Sbellard {
129164201201Sbellard     uint16_t tmp;
129264201201Sbellard     uint16_t pd, pd1, pd2;
129364201201Sbellard 
129464201201Sbellard     tmp = prev >> 8;
129564201201Sbellard     pd = prev ^ value;
129664201201Sbellard     pd1 = pd & 0x000F;
129764201201Sbellard     pd2 = ((pd >> 4) & 0x000F) ^ pd1;
129864201201Sbellard     tmp ^= (pd1 << 3) | (pd1 << 8);
129964201201Sbellard     tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
130064201201Sbellard 
130164201201Sbellard     return tmp;
130264201201Sbellard }
130364201201Sbellard 
1304c227f099SAnthony Liguori static uint16_t NVRAM_compute_crc (nvram_t *nvram, uint32_t start, uint32_t count)
130564201201Sbellard {
130664201201Sbellard     uint32_t i;
130764201201Sbellard     uint16_t crc = 0xFFFF;
130864201201Sbellard     int odd;
130964201201Sbellard 
131064201201Sbellard     odd = count & 1;
131164201201Sbellard     count &= ~1;
131264201201Sbellard     for (i = 0; i != count; i++) {
131364201201Sbellard         crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
131464201201Sbellard     }
131564201201Sbellard     if (odd) {
131664201201Sbellard         crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
131764201201Sbellard     }
131864201201Sbellard 
131964201201Sbellard     return crc;
132064201201Sbellard }
132164201201Sbellard 
1322fd0bbb12Sbellard #define CMDLINE_ADDR 0x017ff000
1323fd0bbb12Sbellard 
1324c227f099SAnthony Liguori int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size,
1325b55266b5Sblueswir1                           const char *arch,
132664201201Sbellard                           uint32_t RAM_size, int boot_device,
132764201201Sbellard                           uint32_t kernel_image, uint32_t kernel_size,
1328fd0bbb12Sbellard                           const char *cmdline,
132964201201Sbellard                           uint32_t initrd_image, uint32_t initrd_size,
1330fd0bbb12Sbellard                           uint32_t NVRAM_image,
1331fd0bbb12Sbellard                           int width, int height, int depth)
133264201201Sbellard {
133364201201Sbellard     uint16_t crc;
133464201201Sbellard 
133564201201Sbellard     /* Set parameters for Open Hack'Ware BIOS */
133664201201Sbellard     NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
133764201201Sbellard     NVRAM_set_lword(nvram,  0x10, 0x00000002); /* structure v2 */
133864201201Sbellard     NVRAM_set_word(nvram,   0x14, NVRAM_size);
133964201201Sbellard     NVRAM_set_string(nvram, 0x20, arch, 16);
134064201201Sbellard     NVRAM_set_lword(nvram,  0x30, RAM_size);
134164201201Sbellard     NVRAM_set_byte(nvram,   0x34, boot_device);
134264201201Sbellard     NVRAM_set_lword(nvram,  0x38, kernel_image);
134364201201Sbellard     NVRAM_set_lword(nvram,  0x3C, kernel_size);
1344fd0bbb12Sbellard     if (cmdline) {
1345fd0bbb12Sbellard         /* XXX: put the cmdline in NVRAM too ? */
13463c178e72SGerd Hoffmann         pstrcpy_targphys("cmdline", CMDLINE_ADDR, RAM_size - CMDLINE_ADDR, cmdline);
1347fd0bbb12Sbellard         NVRAM_set_lword(nvram,  0x40, CMDLINE_ADDR);
1348fd0bbb12Sbellard         NVRAM_set_lword(nvram,  0x44, strlen(cmdline));
1349fd0bbb12Sbellard     } else {
1350fd0bbb12Sbellard         NVRAM_set_lword(nvram,  0x40, 0);
1351fd0bbb12Sbellard         NVRAM_set_lword(nvram,  0x44, 0);
1352fd0bbb12Sbellard     }
135364201201Sbellard     NVRAM_set_lword(nvram,  0x48, initrd_image);
135464201201Sbellard     NVRAM_set_lword(nvram,  0x4C, initrd_size);
135564201201Sbellard     NVRAM_set_lword(nvram,  0x50, NVRAM_image);
1356fd0bbb12Sbellard 
1357fd0bbb12Sbellard     NVRAM_set_word(nvram,   0x54, width);
1358fd0bbb12Sbellard     NVRAM_set_word(nvram,   0x56, height);
1359fd0bbb12Sbellard     NVRAM_set_word(nvram,   0x58, depth);
1360fd0bbb12Sbellard     crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
1361fd0bbb12Sbellard     NVRAM_set_word(nvram,   0xFC, crc);
136264201201Sbellard 
136364201201Sbellard     return 0;
1364a541f297Sbellard }
1365