blob: e473f9e9622d1d12c3a6b5c7d38b7799aa50025b [file] [log] [blame]
bellarda541f292004-04-12 20:39:29 +00001/*
j_mayere9df0142007-04-09 22:45:36 +00002 * QEMU generic PowerPC hardware System Emulator
ths5fafdf22007-09-16 21:08:06 +00003 *
j_mayer76a66252007-03-07 08:32:30 +00004 * Copyright (c) 2003-2007 Jocelyn Mayer
ths5fafdf22007-09-16 21:08:06 +00005 *
bellarda541f292004-04-12 20:39:29 +00006 * 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 */
pbrook87ecb682007-11-17 17:14:51 +000024#include "hw.h"
25#include "ppc.h"
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010026#include "qemu/timer.h"
Paolo Bonzini9c17d612012-12-17 18:20:04 +010027#include "sysemu/sysemu.h"
pbrook87ecb682007-11-17 17:14:51 +000028#include "nvram.h"
Paolo Bonzini1de7afc2012-12-17 18:20:00 +010029#include "qemu/log.h"
Blue Swirlca20cf32009-09-20 14:58:02 +000030#include "loader.h"
Paolo Bonzini9c17d612012-12-17 18:20:04 +010031#include "sysemu/kvm.h"
Alexander Graffc87e182010-08-30 13:49:15 +020032#include "kvm_ppc.h"
bellarda541f292004-04-12 20:39:29 +000033
j_mayere9df0142007-04-09 22:45:36 +000034//#define PPC_DEBUG_IRQ
j_mayer4b6d0a42007-04-24 06:32:00 +000035//#define PPC_DEBUG_TB
j_mayere9df0142007-04-09 22:45:36 +000036
aliguorid12d51d2009-01-15 21:48:06 +000037#ifdef PPC_DEBUG_IRQ
aliguori93fcfe32009-01-15 22:34:14 +000038# define LOG_IRQ(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
aliguorid12d51d2009-01-15 21:48:06 +000039#else
40# define LOG_IRQ(...) do { } while (0)
41#endif
42
43
44#ifdef PPC_DEBUG_TB
aliguori93fcfe32009-01-15 22:34:14 +000045# define LOG_TB(...) qemu_log(__VA_ARGS__)
aliguorid12d51d2009-01-15 21:48:06 +000046#else
47# define LOG_TB(...) do { } while (0)
48#endif
49
Andreas Färbere2684c02012-03-14 01:38:23 +010050static void cpu_ppc_tb_stop (CPUPPCState *env);
51static void cpu_ppc_tb_start (CPUPPCState *env);
j_mayerdbdd2502007-10-14 09:35:30 +000052
Andreas Färber70585812012-12-01 03:55:58 +010053void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level)
j_mayer47103572007-03-30 09:38:04 +000054{
Andreas Färber70585812012-12-01 03:55:58 +010055 CPUPPCState *env = &cpu->env;
Alexander Graffc87e182010-08-30 13:49:15 +020056 unsigned int old_pending = env->pending_interrupts;
57
j_mayer47103572007-03-30 09:38:04 +000058 if (level) {
59 env->pending_interrupts |= 1 << n_IRQ;
60 cpu_interrupt(env, CPU_INTERRUPT_HARD);
61 } else {
62 env->pending_interrupts &= ~(1 << n_IRQ);
63 if (env->pending_interrupts == 0)
64 cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
65 }
Alexander Graffc87e182010-08-30 13:49:15 +020066
67 if (old_pending != env->pending_interrupts) {
68#ifdef CONFIG_KVM
Andreas Färber70585812012-12-01 03:55:58 +010069 kvmppc_set_interrupt(cpu, n_IRQ, level);
Alexander Graffc87e182010-08-30 13:49:15 +020070#endif
71 }
72
aliguorid12d51d2009-01-15 21:48:06 +000073 LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
j_mayeraae93662007-11-24 02:56:36 +000074 "req %08x\n", __func__, env, n_IRQ, level,
j_mayera4967752007-04-16 07:10:48 +000075 env->pending_interrupts, env->interrupt_request);
j_mayer47103572007-03-30 09:38:04 +000076}
77
j_mayere9df0142007-04-09 22:45:36 +000078/* PowerPC 6xx / 7xx internal IRQ controller */
Andreas Färbera0961242012-05-03 02:48:44 +020079static void ppc6xx_set_irq(void *opaque, int pin, int level)
pbrookd537cf62007-04-07 18:14:41 +000080{
Andreas Färbera0961242012-05-03 02:48:44 +020081 PowerPCCPU *cpu = opaque;
82 CPUPPCState *env = &cpu->env;
j_mayere9df0142007-04-09 22:45:36 +000083 int cur_level;
pbrookd537cf62007-04-07 18:14:41 +000084
aliguorid12d51d2009-01-15 21:48:06 +000085 LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
j_mayera4967752007-04-16 07:10:48 +000086 env, pin, level);
j_mayere9df0142007-04-09 22:45:36 +000087 cur_level = (env->irq_input_state >> pin) & 1;
88 /* Don't generate spurious events */
j_mayer24be5ae2007-04-12 21:24:29 +000089 if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
j_mayere9df0142007-04-09 22:45:36 +000090 switch (pin) {
j_mayerdbdd2502007-10-14 09:35:30 +000091 case PPC6xx_INPUT_TBEN:
92 /* Level sensitive - active high */
aliguorid12d51d2009-01-15 21:48:06 +000093 LOG_IRQ("%s: %s the time base\n",
j_mayerdbdd2502007-10-14 09:35:30 +000094 __func__, level ? "start" : "stop");
j_mayerdbdd2502007-10-14 09:35:30 +000095 if (level) {
96 cpu_ppc_tb_start(env);
97 } else {
98 cpu_ppc_tb_stop(env);
99 }
j_mayer24be5ae2007-04-12 21:24:29 +0000100 case PPC6xx_INPUT_INT:
101 /* Level sensitive - active high */
aliguorid12d51d2009-01-15 21:48:06 +0000102 LOG_IRQ("%s: set the external IRQ state to %d\n",
j_mayera4967752007-04-16 07:10:48 +0000103 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100104 ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
j_mayere9df0142007-04-09 22:45:36 +0000105 break;
j_mayer24be5ae2007-04-12 21:24:29 +0000106 case PPC6xx_INPUT_SMI:
j_mayere9df0142007-04-09 22:45:36 +0000107 /* Level sensitive - active high */
aliguorid12d51d2009-01-15 21:48:06 +0000108 LOG_IRQ("%s: set the SMI IRQ state to %d\n",
j_mayera4967752007-04-16 07:10:48 +0000109 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100110 ppc_set_irq(cpu, PPC_INTERRUPT_SMI, level);
j_mayere9df0142007-04-09 22:45:36 +0000111 break;
j_mayer24be5ae2007-04-12 21:24:29 +0000112 case PPC6xx_INPUT_MCP:
j_mayere9df0142007-04-09 22:45:36 +0000113 /* Negative edge sensitive */
114 /* XXX: TODO: actual reaction may depends on HID0 status
115 * 603/604/740/750: check HID0[EMCP]
116 */
117 if (cur_level == 1 && level == 0) {
aliguorid12d51d2009-01-15 21:48:06 +0000118 LOG_IRQ("%s: raise machine check state\n",
j_mayera4967752007-04-16 07:10:48 +0000119 __func__);
Andreas Färber70585812012-12-01 03:55:58 +0100120 ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1);
j_mayere9df0142007-04-09 22:45:36 +0000121 }
122 break;
j_mayer24be5ae2007-04-12 21:24:29 +0000123 case PPC6xx_INPUT_CKSTP_IN:
j_mayere9df0142007-04-09 22:45:36 +0000124 /* Level sensitive - active low */
125 /* XXX: TODO: relay the signal to CKSTP_OUT pin */
j_mayere63ecc62007-10-14 08:48:23 +0000126 /* XXX: Note that the only way to restart the CPU is to reset it */
j_mayere9df0142007-04-09 22:45:36 +0000127 if (level) {
aliguorid12d51d2009-01-15 21:48:06 +0000128 LOG_IRQ("%s: stop the CPU\n", __func__);
j_mayere9df0142007-04-09 22:45:36 +0000129 env->halted = 1;
j_mayere9df0142007-04-09 22:45:36 +0000130 }
131 break;
j_mayer24be5ae2007-04-12 21:24:29 +0000132 case PPC6xx_INPUT_HRESET:
j_mayere9df0142007-04-09 22:45:36 +0000133 /* Level sensitive - active low */
134 if (level) {
aliguorid12d51d2009-01-15 21:48:06 +0000135 LOG_IRQ("%s: reset the CPU\n", __func__);
Alexander Graffc0b2c02012-02-21 19:41:59 +0100136 cpu_interrupt(env, CPU_INTERRUPT_RESET);
j_mayere9df0142007-04-09 22:45:36 +0000137 }
138 break;
j_mayer24be5ae2007-04-12 21:24:29 +0000139 case PPC6xx_INPUT_SRESET:
aliguorid12d51d2009-01-15 21:48:06 +0000140 LOG_IRQ("%s: set the RESET IRQ state to %d\n",
j_mayera4967752007-04-16 07:10:48 +0000141 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100142 ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level);
j_mayere9df0142007-04-09 22:45:36 +0000143 break;
144 default:
145 /* Unknown pin - do nothing */
aliguorid12d51d2009-01-15 21:48:06 +0000146 LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
j_mayere9df0142007-04-09 22:45:36 +0000147 return;
148 }
149 if (level)
150 env->irq_input_state |= 1 << pin;
151 else
152 env->irq_input_state &= ~(1 << pin);
pbrookd537cf62007-04-07 18:14:41 +0000153 }
154}
155
Andreas Färbera0961242012-05-03 02:48:44 +0200156void ppc6xx_irq_init(CPUPPCState *env)
j_mayer47103572007-03-30 09:38:04 +0000157{
Andreas Färbera0961242012-05-03 02:48:44 +0200158 PowerPCCPU *cpu = ppc_env_get_cpu(env);
159
160 env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, cpu,
j_mayer7b62a952007-11-17 02:04:00 +0000161 PPC6xx_INPUT_NB);
j_mayer47103572007-03-30 09:38:04 +0000162}
163
j_mayer00af6852007-10-03 01:05:39 +0000164#if defined(TARGET_PPC64)
j_mayerd0dfae62007-04-16 07:34:39 +0000165/* PowerPC 970 internal IRQ controller */
Andreas Färbera0961242012-05-03 02:48:44 +0200166static void ppc970_set_irq(void *opaque, int pin, int level)
j_mayerd0dfae62007-04-16 07:34:39 +0000167{
Andreas Färbera0961242012-05-03 02:48:44 +0200168 PowerPCCPU *cpu = opaque;
169 CPUPPCState *env = &cpu->env;
j_mayerd0dfae62007-04-16 07:34:39 +0000170 int cur_level;
171
aliguorid12d51d2009-01-15 21:48:06 +0000172 LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
j_mayerd0dfae62007-04-16 07:34:39 +0000173 env, pin, level);
j_mayerd0dfae62007-04-16 07:34:39 +0000174 cur_level = (env->irq_input_state >> pin) & 1;
175 /* Don't generate spurious events */
176 if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
177 switch (pin) {
178 case PPC970_INPUT_INT:
179 /* Level sensitive - active high */
aliguorid12d51d2009-01-15 21:48:06 +0000180 LOG_IRQ("%s: set the external IRQ state to %d\n",
j_mayerd0dfae62007-04-16 07:34:39 +0000181 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100182 ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
j_mayerd0dfae62007-04-16 07:34:39 +0000183 break;
184 case PPC970_INPUT_THINT:
185 /* Level sensitive - active high */
aliguorid12d51d2009-01-15 21:48:06 +0000186 LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__,
j_mayerd0dfae62007-04-16 07:34:39 +0000187 level);
Andreas Färber70585812012-12-01 03:55:58 +0100188 ppc_set_irq(cpu, PPC_INTERRUPT_THERM, level);
j_mayerd0dfae62007-04-16 07:34:39 +0000189 break;
190 case PPC970_INPUT_MCP:
191 /* Negative edge sensitive */
192 /* XXX: TODO: actual reaction may depends on HID0 status
193 * 603/604/740/750: check HID0[EMCP]
194 */
195 if (cur_level == 1 && level == 0) {
aliguorid12d51d2009-01-15 21:48:06 +0000196 LOG_IRQ("%s: raise machine check state\n",
j_mayerd0dfae62007-04-16 07:34:39 +0000197 __func__);
Andreas Färber70585812012-12-01 03:55:58 +0100198 ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1);
j_mayerd0dfae62007-04-16 07:34:39 +0000199 }
200 break;
201 case PPC970_INPUT_CKSTP:
202 /* Level sensitive - active low */
203 /* XXX: TODO: relay the signal to CKSTP_OUT pin */
204 if (level) {
aliguorid12d51d2009-01-15 21:48:06 +0000205 LOG_IRQ("%s: stop the CPU\n", __func__);
j_mayerd0dfae62007-04-16 07:34:39 +0000206 env->halted = 1;
207 } else {
aliguorid12d51d2009-01-15 21:48:06 +0000208 LOG_IRQ("%s: restart the CPU\n", __func__);
j_mayerd0dfae62007-04-16 07:34:39 +0000209 env->halted = 0;
Andreas Färberc08d7422012-05-03 04:34:15 +0200210 qemu_cpu_kick(CPU(cpu));
j_mayerd0dfae62007-04-16 07:34:39 +0000211 }
212 break;
213 case PPC970_INPUT_HRESET:
214 /* Level sensitive - active low */
215 if (level) {
Alexander Graffc0b2c02012-02-21 19:41:59 +0100216 cpu_interrupt(env, CPU_INTERRUPT_RESET);
j_mayerd0dfae62007-04-16 07:34:39 +0000217 }
218 break;
219 case PPC970_INPUT_SRESET:
aliguorid12d51d2009-01-15 21:48:06 +0000220 LOG_IRQ("%s: set the RESET IRQ state to %d\n",
j_mayerd0dfae62007-04-16 07:34:39 +0000221 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100222 ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level);
j_mayerd0dfae62007-04-16 07:34:39 +0000223 break;
224 case PPC970_INPUT_TBEN:
aliguorid12d51d2009-01-15 21:48:06 +0000225 LOG_IRQ("%s: set the TBEN state to %d\n", __func__,
j_mayerd0dfae62007-04-16 07:34:39 +0000226 level);
j_mayerd0dfae62007-04-16 07:34:39 +0000227 /* XXX: TODO */
228 break;
229 default:
230 /* Unknown pin - do nothing */
aliguorid12d51d2009-01-15 21:48:06 +0000231 LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
j_mayerd0dfae62007-04-16 07:34:39 +0000232 return;
233 }
234 if (level)
235 env->irq_input_state |= 1 << pin;
236 else
237 env->irq_input_state &= ~(1 << pin);
238 }
239}
240
Andreas Färbera0961242012-05-03 02:48:44 +0200241void ppc970_irq_init(CPUPPCState *env)
j_mayerd0dfae62007-04-16 07:34:39 +0000242{
Andreas Färbera0961242012-05-03 02:48:44 +0200243 PowerPCCPU *cpu = ppc_env_get_cpu(env);
244
245 env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, cpu,
j_mayer7b62a952007-11-17 02:04:00 +0000246 PPC970_INPUT_NB);
j_mayerd0dfae62007-04-16 07:34:39 +0000247}
David Gibson9d52e902011-04-01 15:15:19 +1100248
249/* POWER7 internal IRQ controller */
Andreas Färbera0961242012-05-03 02:48:44 +0200250static void power7_set_irq(void *opaque, int pin, int level)
David Gibson9d52e902011-04-01 15:15:19 +1100251{
Andreas Färbera0961242012-05-03 02:48:44 +0200252 PowerPCCPU *cpu = opaque;
253 CPUPPCState *env = &cpu->env;
David Gibson9d52e902011-04-01 15:15:19 +1100254
255 LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
256 env, pin, level);
David Gibson9d52e902011-04-01 15:15:19 +1100257
258 switch (pin) {
259 case POWER7_INPUT_INT:
260 /* Level sensitive - active high */
261 LOG_IRQ("%s: set the external IRQ state to %d\n",
262 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100263 ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
David Gibson9d52e902011-04-01 15:15:19 +1100264 break;
265 default:
266 /* Unknown pin - do nothing */
267 LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
268 return;
269 }
270 if (level) {
271 env->irq_input_state |= 1 << pin;
272 } else {
273 env->irq_input_state &= ~(1 << pin);
274 }
275}
276
Andreas Färbera0961242012-05-03 02:48:44 +0200277void ppcPOWER7_irq_init(CPUPPCState *env)
David Gibson9d52e902011-04-01 15:15:19 +1100278{
Andreas Färbera0961242012-05-03 02:48:44 +0200279 PowerPCCPU *cpu = ppc_env_get_cpu(env);
280
281 env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu,
David Gibson9d52e902011-04-01 15:15:19 +1100282 POWER7_INPUT_NB);
283}
j_mayer00af6852007-10-03 01:05:39 +0000284#endif /* defined(TARGET_PPC64) */
j_mayerd0dfae62007-04-16 07:34:39 +0000285
j_mayer4e290a02007-10-01 01:27:10 +0000286/* PowerPC 40x internal IRQ controller */
Andreas Färbera0961242012-05-03 02:48:44 +0200287static void ppc40x_set_irq(void *opaque, int pin, int level)
j_mayer24be5ae2007-04-12 21:24:29 +0000288{
Andreas Färbera0961242012-05-03 02:48:44 +0200289 PowerPCCPU *cpu = opaque;
290 CPUPPCState *env = &cpu->env;
j_mayer24be5ae2007-04-12 21:24:29 +0000291 int cur_level;
292
aliguorid12d51d2009-01-15 21:48:06 +0000293 LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
j_mayer8ecc7912007-04-16 20:09:45 +0000294 env, pin, level);
j_mayer24be5ae2007-04-12 21:24:29 +0000295 cur_level = (env->irq_input_state >> pin) & 1;
296 /* Don't generate spurious events */
297 if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
298 switch (pin) {
j_mayer4e290a02007-10-01 01:27:10 +0000299 case PPC40x_INPUT_RESET_SYS:
j_mayer8ecc7912007-04-16 20:09:45 +0000300 if (level) {
aliguorid12d51d2009-01-15 21:48:06 +0000301 LOG_IRQ("%s: reset the PowerPC system\n",
j_mayer8ecc7912007-04-16 20:09:45 +0000302 __func__);
j_mayer8ecc7912007-04-16 20:09:45 +0000303 ppc40x_system_reset(env);
304 }
305 break;
j_mayer4e290a02007-10-01 01:27:10 +0000306 case PPC40x_INPUT_RESET_CHIP:
j_mayer8ecc7912007-04-16 20:09:45 +0000307 if (level) {
aliguorid12d51d2009-01-15 21:48:06 +0000308 LOG_IRQ("%s: reset the PowerPC chip\n", __func__);
j_mayer8ecc7912007-04-16 20:09:45 +0000309 ppc40x_chip_reset(env);
310 }
311 break;
j_mayer4e290a02007-10-01 01:27:10 +0000312 case PPC40x_INPUT_RESET_CORE:
j_mayer24be5ae2007-04-12 21:24:29 +0000313 /* XXX: TODO: update DBSR[MRR] */
314 if (level) {
aliguorid12d51d2009-01-15 21:48:06 +0000315 LOG_IRQ("%s: reset the PowerPC core\n", __func__);
j_mayer8ecc7912007-04-16 20:09:45 +0000316 ppc40x_core_reset(env);
j_mayer24be5ae2007-04-12 21:24:29 +0000317 }
318 break;
j_mayer4e290a02007-10-01 01:27:10 +0000319 case PPC40x_INPUT_CINT:
j_mayer24be5ae2007-04-12 21:24:29 +0000320 /* Level sensitive - active high */
aliguorid12d51d2009-01-15 21:48:06 +0000321 LOG_IRQ("%s: set the critical IRQ state to %d\n",
j_mayer8ecc7912007-04-16 20:09:45 +0000322 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100323 ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level);
j_mayer24be5ae2007-04-12 21:24:29 +0000324 break;
j_mayer4e290a02007-10-01 01:27:10 +0000325 case PPC40x_INPUT_INT:
j_mayer24be5ae2007-04-12 21:24:29 +0000326 /* Level sensitive - active high */
aliguorid12d51d2009-01-15 21:48:06 +0000327 LOG_IRQ("%s: set the external IRQ state to %d\n",
j_mayera4967752007-04-16 07:10:48 +0000328 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100329 ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
j_mayer24be5ae2007-04-12 21:24:29 +0000330 break;
j_mayer4e290a02007-10-01 01:27:10 +0000331 case PPC40x_INPUT_HALT:
j_mayer24be5ae2007-04-12 21:24:29 +0000332 /* Level sensitive - active low */
333 if (level) {
aliguorid12d51d2009-01-15 21:48:06 +0000334 LOG_IRQ("%s: stop the CPU\n", __func__);
j_mayer24be5ae2007-04-12 21:24:29 +0000335 env->halted = 1;
336 } else {
aliguorid12d51d2009-01-15 21:48:06 +0000337 LOG_IRQ("%s: restart the CPU\n", __func__);
j_mayer24be5ae2007-04-12 21:24:29 +0000338 env->halted = 0;
Andreas Färberc08d7422012-05-03 04:34:15 +0200339 qemu_cpu_kick(CPU(cpu));
j_mayer24be5ae2007-04-12 21:24:29 +0000340 }
341 break;
j_mayer4e290a02007-10-01 01:27:10 +0000342 case PPC40x_INPUT_DEBUG:
j_mayer24be5ae2007-04-12 21:24:29 +0000343 /* Level sensitive - active high */
aliguorid12d51d2009-01-15 21:48:06 +0000344 LOG_IRQ("%s: set the debug pin state to %d\n",
j_mayera4967752007-04-16 07:10:48 +0000345 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100346 ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level);
j_mayer24be5ae2007-04-12 21:24:29 +0000347 break;
348 default:
349 /* Unknown pin - do nothing */
aliguorid12d51d2009-01-15 21:48:06 +0000350 LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
j_mayer24be5ae2007-04-12 21:24:29 +0000351 return;
352 }
353 if (level)
354 env->irq_input_state |= 1 << pin;
355 else
356 env->irq_input_state &= ~(1 << pin);
357 }
358}
359
Andreas Färbera0961242012-05-03 02:48:44 +0200360void ppc40x_irq_init(CPUPPCState *env)
j_mayer24be5ae2007-04-12 21:24:29 +0000361{
Andreas Färbera0961242012-05-03 02:48:44 +0200362 PowerPCCPU *cpu = ppc_env_get_cpu(env);
363
j_mayer4e290a02007-10-01 01:27:10 +0000364 env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq,
Andreas Färbera0961242012-05-03 02:48:44 +0200365 cpu, PPC40x_INPUT_NB);
j_mayer24be5ae2007-04-12 21:24:29 +0000366}
367
aurel329fdc60b2009-03-02 16:42:32 +0000368/* PowerPC E500 internal IRQ controller */
Andreas Färbera0961242012-05-03 02:48:44 +0200369static void ppce500_set_irq(void *opaque, int pin, int level)
aurel329fdc60b2009-03-02 16:42:32 +0000370{
Andreas Färbera0961242012-05-03 02:48:44 +0200371 PowerPCCPU *cpu = opaque;
372 CPUPPCState *env = &cpu->env;
aurel329fdc60b2009-03-02 16:42:32 +0000373 int cur_level;
374
375 LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
376 env, pin, level);
377 cur_level = (env->irq_input_state >> pin) & 1;
378 /* Don't generate spurious events */
379 if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
380 switch (pin) {
381 case PPCE500_INPUT_MCK:
382 if (level) {
383 LOG_IRQ("%s: reset the PowerPC system\n",
384 __func__);
385 qemu_system_reset_request();
386 }
387 break;
388 case PPCE500_INPUT_RESET_CORE:
389 if (level) {
390 LOG_IRQ("%s: reset the PowerPC core\n", __func__);
Andreas Färber70585812012-12-01 03:55:58 +0100391 ppc_set_irq(cpu, PPC_INTERRUPT_MCK, level);
aurel329fdc60b2009-03-02 16:42:32 +0000392 }
393 break;
394 case PPCE500_INPUT_CINT:
395 /* Level sensitive - active high */
396 LOG_IRQ("%s: set the critical IRQ state to %d\n",
397 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100398 ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level);
aurel329fdc60b2009-03-02 16:42:32 +0000399 break;
400 case PPCE500_INPUT_INT:
401 /* Level sensitive - active high */
402 LOG_IRQ("%s: set the core IRQ state to %d\n",
403 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100404 ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level);
aurel329fdc60b2009-03-02 16:42:32 +0000405 break;
406 case PPCE500_INPUT_DEBUG:
407 /* Level sensitive - active high */
408 LOG_IRQ("%s: set the debug pin state to %d\n",
409 __func__, level);
Andreas Färber70585812012-12-01 03:55:58 +0100410 ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level);
aurel329fdc60b2009-03-02 16:42:32 +0000411 break;
412 default:
413 /* Unknown pin - do nothing */
414 LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
415 return;
416 }
417 if (level)
418 env->irq_input_state |= 1 << pin;
419 else
420 env->irq_input_state &= ~(1 << pin);
421 }
422}
423
Andreas Färbera0961242012-05-03 02:48:44 +0200424void ppce500_irq_init(CPUPPCState *env)
aurel329fdc60b2009-03-02 16:42:32 +0000425{
Andreas Färbera0961242012-05-03 02:48:44 +0200426 PowerPCCPU *cpu = ppc_env_get_cpu(env);
427
aurel329fdc60b2009-03-02 16:42:32 +0000428 env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq,
Andreas Färbera0961242012-05-03 02:48:44 +0200429 cpu, PPCE500_INPUT_NB);
aurel329fdc60b2009-03-02 16:42:32 +0000430}
bellard9fddaa02004-05-21 12:59:32 +0000431/*****************************************************************************/
j_mayere9df0142007-04-09 22:45:36 +0000432/* PowerPC time base and decrementer emulation */
bellard9fddaa02004-05-21 12:59:32 +0000433
Fabien Chouteauddd10552011-09-13 04:00:32 +0000434uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset)
bellard9fddaa02004-05-21 12:59:32 +0000435{
436 /* TB time in tb periods */
Juan Quintela6ee093c2009-09-10 03:04:26 +0200437 return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
bellard9fddaa02004-05-21 12:59:32 +0000438}
439
Andreas Färbere2684c02012-03-14 01:38:23 +0100440uint64_t cpu_ppc_load_tbl (CPUPPCState *env)
bellard9fddaa02004-05-21 12:59:32 +0000441{
Anthony Liguoric227f092009-10-01 16:12:16 -0500442 ppc_tb_t *tb_env = env->tb_env;
bellard9fddaa02004-05-21 12:59:32 +0000443 uint64_t tb;
444
Scott Wood90dc8812011-04-29 17:10:23 -0500445 if (kvm_enabled()) {
446 return env->spr[SPR_TBL];
447 }
448
Paolo Bonzini74475452011-03-11 16:47:48 +0100449 tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset);
aliguorid12d51d2009-01-15 21:48:06 +0000450 LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
bellard9fddaa02004-05-21 12:59:32 +0000451
Alexander Grafe3ea6522009-12-21 12:24:17 +0100452 return tb;
bellard9fddaa02004-05-21 12:59:32 +0000453}
454
Andreas Färbere2684c02012-03-14 01:38:23 +0100455static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env)
bellard9fddaa02004-05-21 12:59:32 +0000456{
Anthony Liguoric227f092009-10-01 16:12:16 -0500457 ppc_tb_t *tb_env = env->tb_env;
bellard9fddaa02004-05-21 12:59:32 +0000458 uint64_t tb;
459
Paolo Bonzini74475452011-03-11 16:47:48 +0100460 tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset);
aliguorid12d51d2009-01-15 21:48:06 +0000461 LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
j_mayer76a66252007-03-07 08:32:30 +0000462
bellard9fddaa02004-05-21 12:59:32 +0000463 return tb >> 32;
464}
465
Andreas Färbere2684c02012-03-14 01:38:23 +0100466uint32_t cpu_ppc_load_tbu (CPUPPCState *env)
j_mayer8a84de22007-09-30 14:44:52 +0000467{
Scott Wood90dc8812011-04-29 17:10:23 -0500468 if (kvm_enabled()) {
469 return env->spr[SPR_TBU];
470 }
471
j_mayer8a84de22007-09-30 14:44:52 +0000472 return _cpu_ppc_load_tbu(env);
473}
474
Anthony Liguoric227f092009-10-01 16:12:16 -0500475static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk,
Blue Swirl636aa202009-08-16 09:06:54 +0000476 int64_t *tb_offsetp, uint64_t value)
bellard9fddaa02004-05-21 12:59:32 +0000477{
Juan Quintela6ee093c2009-09-10 03:04:26 +0200478 *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec());
aliguorid12d51d2009-01-15 21:48:06 +0000479 LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n",
j_mayeraae93662007-11-24 02:56:36 +0000480 __func__, value, *tb_offsetp);
bellard9fddaa02004-05-21 12:59:32 +0000481}
482
Andreas Färbere2684c02012-03-14 01:38:23 +0100483void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value)
bellard9fddaa02004-05-21 12:59:32 +0000484{
Anthony Liguoric227f092009-10-01 16:12:16 -0500485 ppc_tb_t *tb_env = env->tb_env;
j_mayera062e362007-09-30 00:38:38 +0000486 uint64_t tb;
bellard9fddaa02004-05-21 12:59:32 +0000487
Paolo Bonzini74475452011-03-11 16:47:48 +0100488 tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset);
j_mayera062e362007-09-30 00:38:38 +0000489 tb &= 0xFFFFFFFF00000000ULL;
Paolo Bonzini74475452011-03-11 16:47:48 +0100490 cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock),
j_mayerdbdd2502007-10-14 09:35:30 +0000491 &tb_env->tb_offset, tb | (uint64_t)value);
j_mayera062e362007-09-30 00:38:38 +0000492}
493
Andreas Färbere2684c02012-03-14 01:38:23 +0100494static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value)
j_mayera062e362007-09-30 00:38:38 +0000495{
Anthony Liguoric227f092009-10-01 16:12:16 -0500496 ppc_tb_t *tb_env = env->tb_env;
j_mayera062e362007-09-30 00:38:38 +0000497 uint64_t tb;
498
Paolo Bonzini74475452011-03-11 16:47:48 +0100499 tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->tb_offset);
j_mayera062e362007-09-30 00:38:38 +0000500 tb &= 0x00000000FFFFFFFFULL;
Paolo Bonzini74475452011-03-11 16:47:48 +0100501 cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock),
j_mayerdbdd2502007-10-14 09:35:30 +0000502 &tb_env->tb_offset, ((uint64_t)value << 32) | tb);
j_mayera062e362007-09-30 00:38:38 +0000503}
504
Andreas Färbere2684c02012-03-14 01:38:23 +0100505void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value)
j_mayer8a84de22007-09-30 14:44:52 +0000506{
507 _cpu_ppc_store_tbu(env, value);
508}
509
Andreas Färbere2684c02012-03-14 01:38:23 +0100510uint64_t cpu_ppc_load_atbl (CPUPPCState *env)
j_mayera062e362007-09-30 00:38:38 +0000511{
Anthony Liguoric227f092009-10-01 16:12:16 -0500512 ppc_tb_t *tb_env = env->tb_env;
j_mayera062e362007-09-30 00:38:38 +0000513 uint64_t tb;
514
Paolo Bonzini74475452011-03-11 16:47:48 +0100515 tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset);
aliguorid12d51d2009-01-15 21:48:06 +0000516 LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
j_mayera062e362007-09-30 00:38:38 +0000517
Aurelien Jarnob711de92009-12-21 13:52:08 +0100518 return tb;
j_mayera062e362007-09-30 00:38:38 +0000519}
520
Andreas Färbere2684c02012-03-14 01:38:23 +0100521uint32_t cpu_ppc_load_atbu (CPUPPCState *env)
j_mayera062e362007-09-30 00:38:38 +0000522{
Anthony Liguoric227f092009-10-01 16:12:16 -0500523 ppc_tb_t *tb_env = env->tb_env;
j_mayera062e362007-09-30 00:38:38 +0000524 uint64_t tb;
525
Paolo Bonzini74475452011-03-11 16:47:48 +0100526 tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset);
aliguorid12d51d2009-01-15 21:48:06 +0000527 LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
j_mayera062e362007-09-30 00:38:38 +0000528
529 return tb >> 32;
530}
531
Andreas Färbere2684c02012-03-14 01:38:23 +0100532void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value)
j_mayera062e362007-09-30 00:38:38 +0000533{
Anthony Liguoric227f092009-10-01 16:12:16 -0500534 ppc_tb_t *tb_env = env->tb_env;
j_mayera062e362007-09-30 00:38:38 +0000535 uint64_t tb;
536
Paolo Bonzini74475452011-03-11 16:47:48 +0100537 tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset);
j_mayera062e362007-09-30 00:38:38 +0000538 tb &= 0xFFFFFFFF00000000ULL;
Paolo Bonzini74475452011-03-11 16:47:48 +0100539 cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock),
j_mayerdbdd2502007-10-14 09:35:30 +0000540 &tb_env->atb_offset, tb | (uint64_t)value);
j_mayera062e362007-09-30 00:38:38 +0000541}
542
Andreas Färbere2684c02012-03-14 01:38:23 +0100543void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value)
j_mayera062e362007-09-30 00:38:38 +0000544{
Anthony Liguoric227f092009-10-01 16:12:16 -0500545 ppc_tb_t *tb_env = env->tb_env;
j_mayera062e362007-09-30 00:38:38 +0000546 uint64_t tb;
547
Paolo Bonzini74475452011-03-11 16:47:48 +0100548 tb = cpu_ppc_get_tb(tb_env, qemu_get_clock_ns(vm_clock), tb_env->atb_offset);
j_mayera062e362007-09-30 00:38:38 +0000549 tb &= 0x00000000FFFFFFFFULL;
Paolo Bonzini74475452011-03-11 16:47:48 +0100550 cpu_ppc_store_tb(tb_env, qemu_get_clock_ns(vm_clock),
j_mayerdbdd2502007-10-14 09:35:30 +0000551 &tb_env->atb_offset, ((uint64_t)value << 32) | tb);
552}
553
Andreas Färbere2684c02012-03-14 01:38:23 +0100554static void cpu_ppc_tb_stop (CPUPPCState *env)
j_mayerdbdd2502007-10-14 09:35:30 +0000555{
Anthony Liguoric227f092009-10-01 16:12:16 -0500556 ppc_tb_t *tb_env = env->tb_env;
j_mayerdbdd2502007-10-14 09:35:30 +0000557 uint64_t tb, atb, vmclk;
558
559 /* If the time base is already frozen, do nothing */
560 if (tb_env->tb_freq != 0) {
Paolo Bonzini74475452011-03-11 16:47:48 +0100561 vmclk = qemu_get_clock_ns(vm_clock);
j_mayerdbdd2502007-10-14 09:35:30 +0000562 /* Get the time base */
563 tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset);
564 /* Get the alternate time base */
565 atb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->atb_offset);
566 /* Store the time base value (ie compute the current offset) */
567 cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb);
568 /* Store the alternate time base value (compute the current offset) */
569 cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb);
570 /* Set the time base frequency to zero */
571 tb_env->tb_freq = 0;
572 /* Now, the time bases are frozen to tb_offset / atb_offset value */
573 }
574}
575
Andreas Färbere2684c02012-03-14 01:38:23 +0100576static void cpu_ppc_tb_start (CPUPPCState *env)
j_mayerdbdd2502007-10-14 09:35:30 +0000577{
Anthony Liguoric227f092009-10-01 16:12:16 -0500578 ppc_tb_t *tb_env = env->tb_env;
j_mayerdbdd2502007-10-14 09:35:30 +0000579 uint64_t tb, atb, vmclk;
j_mayeraae93662007-11-24 02:56:36 +0000580
j_mayerdbdd2502007-10-14 09:35:30 +0000581 /* If the time base is not frozen, do nothing */
582 if (tb_env->tb_freq == 0) {
Paolo Bonzini74475452011-03-11 16:47:48 +0100583 vmclk = qemu_get_clock_ns(vm_clock);
j_mayerdbdd2502007-10-14 09:35:30 +0000584 /* Get the time base from tb_offset */
585 tb = tb_env->tb_offset;
586 /* Get the alternate time base from atb_offset */
587 atb = tb_env->atb_offset;
588 /* Restore the tb frequency from the decrementer frequency */
589 tb_env->tb_freq = tb_env->decr_freq;
590 /* Store the time base value */
591 cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb);
592 /* Store the alternate time base value */
593 cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb);
594 }
bellard9fddaa02004-05-21 12:59:32 +0000595}
596
Andreas Färbere2684c02012-03-14 01:38:23 +0100597static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next)
bellard9fddaa02004-05-21 12:59:32 +0000598{
Anthony Liguoric227f092009-10-01 16:12:16 -0500599 ppc_tb_t *tb_env = env->tb_env;
bellard9fddaa02004-05-21 12:59:32 +0000600 uint32_t decr;
bellard4e588a42005-07-07 21:46:29 +0000601 int64_t diff;
bellard9fddaa02004-05-21 12:59:32 +0000602
Paolo Bonzini74475452011-03-11 16:47:48 +0100603 diff = next - qemu_get_clock_ns(vm_clock);
Fabien Chouteauddd10552011-09-13 04:00:32 +0000604 if (diff >= 0) {
Juan Quintela6ee093c2009-09-10 03:04:26 +0200605 decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec());
Fabien Chouteauddd10552011-09-13 04:00:32 +0000606 } else if (tb_env->flags & PPC_TIMER_BOOKE) {
607 decr = 0;
608 } else {
Juan Quintela6ee093c2009-09-10 03:04:26 +0200609 decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec());
Fabien Chouteauddd10552011-09-13 04:00:32 +0000610 }
aliguorid12d51d2009-01-15 21:48:06 +0000611 LOG_TB("%s: %08" PRIx32 "\n", __func__, decr);
j_mayer76a66252007-03-07 08:32:30 +0000612
bellard9fddaa02004-05-21 12:59:32 +0000613 return decr;
614}
615
Andreas Färbere2684c02012-03-14 01:38:23 +0100616uint32_t cpu_ppc_load_decr (CPUPPCState *env)
j_mayer58a7d322007-09-29 13:21:37 +0000617{
Anthony Liguoric227f092009-10-01 16:12:16 -0500618 ppc_tb_t *tb_env = env->tb_env;
j_mayer58a7d322007-09-29 13:21:37 +0000619
Scott Wood90dc8812011-04-29 17:10:23 -0500620 if (kvm_enabled()) {
621 return env->spr[SPR_DECR];
622 }
623
Tristan Gingoldf55e9d92009-04-27 10:55:47 +0200624 return _cpu_ppc_load_decr(env, tb_env->decr_next);
j_mayer58a7d322007-09-29 13:21:37 +0000625}
626
Andreas Färbere2684c02012-03-14 01:38:23 +0100627uint32_t cpu_ppc_load_hdecr (CPUPPCState *env)
j_mayer58a7d322007-09-29 13:21:37 +0000628{
Anthony Liguoric227f092009-10-01 16:12:16 -0500629 ppc_tb_t *tb_env = env->tb_env;
j_mayer58a7d322007-09-29 13:21:37 +0000630
Tristan Gingoldf55e9d92009-04-27 10:55:47 +0200631 return _cpu_ppc_load_decr(env, tb_env->hdecr_next);
j_mayer58a7d322007-09-29 13:21:37 +0000632}
633
Andreas Färbere2684c02012-03-14 01:38:23 +0100634uint64_t cpu_ppc_load_purr (CPUPPCState *env)
j_mayer58a7d322007-09-29 13:21:37 +0000635{
Anthony Liguoric227f092009-10-01 16:12:16 -0500636 ppc_tb_t *tb_env = env->tb_env;
j_mayer58a7d322007-09-29 13:21:37 +0000637 uint64_t diff;
638
Paolo Bonzini74475452011-03-11 16:47:48 +0100639 diff = qemu_get_clock_ns(vm_clock) - tb_env->purr_start;
j_mayerb33c17e2007-10-07 17:30:34 +0000640
Juan Quintela6ee093c2009-09-10 03:04:26 +0200641 return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec());
j_mayer58a7d322007-09-29 13:21:37 +0000642}
j_mayer58a7d322007-09-29 13:21:37 +0000643
bellard9fddaa02004-05-21 12:59:32 +0000644/* When decrementer expires,
645 * all we need to do is generate or queue a CPU exception
646 */
Andreas Färber7e0a9242012-12-01 04:18:02 +0100647static inline void cpu_ppc_decr_excp(PowerPCCPU *cpu)
bellard9fddaa02004-05-21 12:59:32 +0000648{
649 /* Raise it */
aliguorid12d51d2009-01-15 21:48:06 +0000650 LOG_TB("raise decrementer exception\n");
Andreas Färber70585812012-12-01 03:55:58 +0100651 ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 1);
bellard9fddaa02004-05-21 12:59:32 +0000652}
653
Andreas Färber7e0a9242012-12-01 04:18:02 +0100654static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu)
j_mayer58a7d322007-09-29 13:21:37 +0000655{
656 /* Raise it */
aliguorid12d51d2009-01-15 21:48:06 +0000657 LOG_TB("raise decrementer exception\n");
Andreas Färber70585812012-12-01 03:55:58 +0100658 ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1);
j_mayer58a7d322007-09-29 13:21:37 +0000659}
660
Andreas Färber7e0a9242012-12-01 04:18:02 +0100661static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp,
662 struct QEMUTimer *timer,
663 void (*raise_excp)(PowerPCCPU *),
664 uint32_t decr, uint32_t value,
665 int is_excp)
bellard9fddaa02004-05-21 12:59:32 +0000666{
Andreas Färber7e0a9242012-12-01 04:18:02 +0100667 CPUPPCState *env = &cpu->env;
Anthony Liguoric227f092009-10-01 16:12:16 -0500668 ppc_tb_t *tb_env = env->tb_env;
bellard9fddaa02004-05-21 12:59:32 +0000669 uint64_t now, next;
670
aliguorid12d51d2009-01-15 21:48:06 +0000671 LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__,
j_mayeraae93662007-11-24 02:56:36 +0000672 decr, value);
David Gibson55f7d4b2011-10-16 19:26:17 +0000673
674 if (kvm_enabled()) {
675 /* KVM handles decrementer exceptions, we don't need our own timer */
676 return;
677 }
678
Paolo Bonzini74475452011-03-11 16:47:48 +0100679 now = qemu_get_clock_ns(vm_clock);
Juan Quintela6ee093c2009-09-10 03:04:26 +0200680 next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
Fabien Chouteauddd10552011-09-13 04:00:32 +0000681 if (is_excp) {
j_mayer58a7d322007-09-29 13:21:37 +0000682 next += *nextp - now;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000683 }
684 if (next == now) {
j_mayer76a66252007-03-07 08:32:30 +0000685 next++;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000686 }
j_mayer58a7d322007-09-29 13:21:37 +0000687 *nextp = next;
bellard9fddaa02004-05-21 12:59:32 +0000688 /* Adjust timer */
j_mayer58a7d322007-09-29 13:21:37 +0000689 qemu_mod_timer(timer, next);
Fabien Chouteauddd10552011-09-13 04:00:32 +0000690
691 /* If we set a negative value and the decrementer was positive, raise an
692 * exception.
bellard9fddaa02004-05-21 12:59:32 +0000693 */
Fabien Chouteauddd10552011-09-13 04:00:32 +0000694 if ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED)
695 && (value & 0x80000000)
696 && !(decr & 0x80000000)) {
Andreas Färber7e0a9242012-12-01 04:18:02 +0100697 (*raise_excp)(cpu);
Fabien Chouteauddd10552011-09-13 04:00:32 +0000698 }
j_mayer58a7d322007-09-29 13:21:37 +0000699}
700
Andreas Färber7e0a9242012-12-01 04:18:02 +0100701static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, uint32_t decr,
Blue Swirl636aa202009-08-16 09:06:54 +0000702 uint32_t value, int is_excp)
j_mayer58a7d322007-09-29 13:21:37 +0000703{
Andreas Färber7e0a9242012-12-01 04:18:02 +0100704 ppc_tb_t *tb_env = cpu->env.tb_env;
j_mayer58a7d322007-09-29 13:21:37 +0000705
Andreas Färber7e0a9242012-12-01 04:18:02 +0100706 __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer,
j_mayer58a7d322007-09-29 13:21:37 +0000707 &cpu_ppc_decr_excp, decr, value, is_excp);
bellard9fddaa02004-05-21 12:59:32 +0000708}
709
Andreas Färbere2684c02012-03-14 01:38:23 +0100710void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value)
bellard9fddaa02004-05-21 12:59:32 +0000711{
Andreas Färber7e0a9242012-12-01 04:18:02 +0100712 PowerPCCPU *cpu = ppc_env_get_cpu(env);
713
714 _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, 0);
bellard9fddaa02004-05-21 12:59:32 +0000715}
716
Andreas Färber50c680f2012-12-01 04:26:55 +0100717static void cpu_ppc_decr_cb(void *opaque)
bellard9fddaa02004-05-21 12:59:32 +0000718{
Andreas Färber50c680f2012-12-01 04:26:55 +0100719 PowerPCCPU *cpu = opaque;
Andreas Färber7e0a9242012-12-01 04:18:02 +0100720
Andreas Färber50c680f2012-12-01 04:26:55 +0100721 _cpu_ppc_store_decr(cpu, 0x00000000, 0xFFFFFFFF, 1);
bellard9fddaa02004-05-21 12:59:32 +0000722}
723
Andreas Färber7e0a9242012-12-01 04:18:02 +0100724static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, uint32_t hdecr,
Blue Swirl636aa202009-08-16 09:06:54 +0000725 uint32_t value, int is_excp)
j_mayer58a7d322007-09-29 13:21:37 +0000726{
Andreas Färber7e0a9242012-12-01 04:18:02 +0100727 ppc_tb_t *tb_env = cpu->env.tb_env;
j_mayer58a7d322007-09-29 13:21:37 +0000728
j_mayerb172c562007-11-17 01:37:44 +0000729 if (tb_env->hdecr_timer != NULL) {
Andreas Färber7e0a9242012-12-01 04:18:02 +0100730 __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer,
j_mayerb172c562007-11-17 01:37:44 +0000731 &cpu_ppc_hdecr_excp, hdecr, value, is_excp);
732 }
j_mayer58a7d322007-09-29 13:21:37 +0000733}
734
Andreas Färbere2684c02012-03-14 01:38:23 +0100735void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value)
j_mayer58a7d322007-09-29 13:21:37 +0000736{
Andreas Färber7e0a9242012-12-01 04:18:02 +0100737 PowerPCCPU *cpu = ppc_env_get_cpu(env);
738
739 _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value, 0);
j_mayer58a7d322007-09-29 13:21:37 +0000740}
741
Andreas Färber50c680f2012-12-01 04:26:55 +0100742static void cpu_ppc_hdecr_cb(void *opaque)
j_mayer58a7d322007-09-29 13:21:37 +0000743{
Andreas Färber50c680f2012-12-01 04:26:55 +0100744 PowerPCCPU *cpu = opaque;
Andreas Färber7e0a9242012-12-01 04:18:02 +0100745
Andreas Färber50c680f2012-12-01 04:26:55 +0100746 _cpu_ppc_store_hdecr(cpu, 0x00000000, 0xFFFFFFFF, 1);
j_mayer58a7d322007-09-29 13:21:37 +0000747}
748
Andreas Färber7e0a9242012-12-01 04:18:02 +0100749static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value)
j_mayer58a7d322007-09-29 13:21:37 +0000750{
Andreas Färber7e0a9242012-12-01 04:18:02 +0100751 ppc_tb_t *tb_env = cpu->env.tb_env;
j_mayer58a7d322007-09-29 13:21:37 +0000752
753 tb_env->purr_load = value;
Paolo Bonzini74475452011-03-11 16:47:48 +0100754 tb_env->purr_start = qemu_get_clock_ns(vm_clock);
j_mayer58a7d322007-09-29 13:21:37 +0000755}
j_mayer58a7d322007-09-29 13:21:37 +0000756
j_mayer8ecc7912007-04-16 20:09:45 +0000757static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
758{
Andreas Färbere2684c02012-03-14 01:38:23 +0100759 CPUPPCState *env = opaque;
Andreas Färber7e0a9242012-12-01 04:18:02 +0100760 PowerPCCPU *cpu = ppc_env_get_cpu(env);
Anthony Liguoric227f092009-10-01 16:12:16 -0500761 ppc_tb_t *tb_env = env->tb_env;
j_mayer8ecc7912007-04-16 20:09:45 +0000762
763 tb_env->tb_freq = freq;
j_mayerdbdd2502007-10-14 09:35:30 +0000764 tb_env->decr_freq = freq;
j_mayer8ecc7912007-04-16 20:09:45 +0000765 /* There is a bug in Linux 2.4 kernels:
766 * if a decrementer exception is pending when it enables msr_ee at startup,
767 * it's not ready to handle it...
768 */
Andreas Färber7e0a9242012-12-01 04:18:02 +0100769 _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 0);
770 _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 0);
771 cpu_ppc_store_purr(cpu, 0x0000000000000000ULL);
j_mayer8ecc7912007-04-16 20:09:45 +0000772}
773
bellard9fddaa02004-05-21 12:59:32 +0000774/* Set up (once) timebase frequency (in Hz) */
Andreas Färbere2684c02012-03-14 01:38:23 +0100775clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq)
bellard9fddaa02004-05-21 12:59:32 +0000776{
Andreas Färber50c680f2012-12-01 04:26:55 +0100777 PowerPCCPU *cpu = ppc_env_get_cpu(env);
Anthony Liguoric227f092009-10-01 16:12:16 -0500778 ppc_tb_t *tb_env;
bellard9fddaa02004-05-21 12:59:32 +0000779
Anthony Liguori7267c092011-08-20 22:09:37 -0500780 tb_env = g_malloc0(sizeof(ppc_tb_t));
bellard9fddaa02004-05-21 12:59:32 +0000781 env->tb_env = tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000782 tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
j_mayer8ecc7912007-04-16 20:09:45 +0000783 /* Create new timer */
Andreas Färber50c680f2012-12-01 04:26:55 +0100784 tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_decr_cb, cpu);
j_mayerb172c562007-11-17 01:37:44 +0000785 if (0) {
786 /* XXX: find a suitable condition to enable the hypervisor decrementer
787 */
Andreas Färber50c680f2012-12-01 04:26:55 +0100788 tb_env->hdecr_timer = qemu_new_timer_ns(vm_clock, &cpu_ppc_hdecr_cb,
789 cpu);
j_mayerb172c562007-11-17 01:37:44 +0000790 } else {
791 tb_env->hdecr_timer = NULL;
792 }
j_mayer8ecc7912007-04-16 20:09:45 +0000793 cpu_ppc_set_tb_clk(env, freq);
bellard9fddaa02004-05-21 12:59:32 +0000794
j_mayer8ecc7912007-04-16 20:09:45 +0000795 return &cpu_ppc_set_tb_clk;
bellard9fddaa02004-05-21 12:59:32 +0000796}
797
j_mayer76a66252007-03-07 08:32:30 +0000798/* Specific helpers for POWER & PowerPC 601 RTC */
blueswir1b1d8e522008-10-26 13:43:07 +0000799#if 0
Andreas Färbere2684c02012-03-14 01:38:23 +0100800static clk_setup_cb cpu_ppc601_rtc_init (CPUPPCState *env)
j_mayer76a66252007-03-07 08:32:30 +0000801{
802 return cpu_ppc_tb_init(env, 7812500);
803}
blueswir1b1d8e522008-10-26 13:43:07 +0000804#endif
j_mayer76a66252007-03-07 08:32:30 +0000805
Andreas Färbere2684c02012-03-14 01:38:23 +0100806void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value)
j_mayer8a84de22007-09-30 14:44:52 +0000807{
808 _cpu_ppc_store_tbu(env, value);
809}
j_mayer76a66252007-03-07 08:32:30 +0000810
Andreas Färbere2684c02012-03-14 01:38:23 +0100811uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env)
j_mayer8a84de22007-09-30 14:44:52 +0000812{
813 return _cpu_ppc_load_tbu(env);
814}
j_mayer76a66252007-03-07 08:32:30 +0000815
Andreas Färbere2684c02012-03-14 01:38:23 +0100816void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value)
j_mayer76a66252007-03-07 08:32:30 +0000817{
818 cpu_ppc_store_tbl(env, value & 0x3FFFFF80);
819}
820
Andreas Färbere2684c02012-03-14 01:38:23 +0100821uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env)
j_mayer76a66252007-03-07 08:32:30 +0000822{
823 return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
824}
825
j_mayer636aaad2007-03-31 11:38:38 +0000826/*****************************************************************************/
Fabien Chouteauddd10552011-09-13 04:00:32 +0000827/* PowerPC 40x timers */
j_mayer636aaad2007-03-31 11:38:38 +0000828
829/* PIT, FIT & WDT */
Fabien Chouteauddd10552011-09-13 04:00:32 +0000830typedef struct ppc40x_timer_t ppc40x_timer_t;
831struct ppc40x_timer_t {
j_mayer636aaad2007-03-31 11:38:38 +0000832 uint64_t pit_reload; /* PIT auto-reload value */
833 uint64_t fit_next; /* Tick for next FIT interrupt */
834 struct QEMUTimer *fit_timer;
835 uint64_t wdt_next; /* Tick for next WDT interrupt */
836 struct QEMUTimer *wdt_timer;
Edgar E. Iglesiasd63cb482010-09-20 19:08:42 +0200837
838 /* 405 have the PIT, 440 have a DECR. */
839 unsigned int decr_excp;
j_mayer636aaad2007-03-31 11:38:38 +0000840};
ths3b46e622007-09-17 08:09:54 +0000841
j_mayer636aaad2007-03-31 11:38:38 +0000842/* Fixed interval timer */
843static void cpu_4xx_fit_cb (void *opaque)
j_mayer76a66252007-03-07 08:32:30 +0000844{
Andreas Färber70585812012-12-01 03:55:58 +0100845 PowerPCCPU *cpu;
Andreas Färbere2684c02012-03-14 01:38:23 +0100846 CPUPPCState *env;
Anthony Liguoric227f092009-10-01 16:12:16 -0500847 ppc_tb_t *tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000848 ppc40x_timer_t *ppc40x_timer;
j_mayer636aaad2007-03-31 11:38:38 +0000849 uint64_t now, next;
850
851 env = opaque;
Andreas Färber70585812012-12-01 03:55:58 +0100852 cpu = ppc_env_get_cpu(env);
j_mayer636aaad2007-03-31 11:38:38 +0000853 tb_env = env->tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000854 ppc40x_timer = tb_env->opaque;
Paolo Bonzini74475452011-03-11 16:47:48 +0100855 now = qemu_get_clock_ns(vm_clock);
j_mayer636aaad2007-03-31 11:38:38 +0000856 switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
857 case 0:
858 next = 1 << 9;
859 break;
860 case 1:
861 next = 1 << 13;
862 break;
863 case 2:
864 next = 1 << 17;
865 break;
866 case 3:
867 next = 1 << 21;
868 break;
869 default:
870 /* Cannot occur, but makes gcc happy */
871 return;
872 }
Juan Quintela6ee093c2009-09-10 03:04:26 +0200873 next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq);
j_mayer636aaad2007-03-31 11:38:38 +0000874 if (next == now)
875 next++;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000876 qemu_mod_timer(ppc40x_timer->fit_timer, next);
j_mayer636aaad2007-03-31 11:38:38 +0000877 env->spr[SPR_40x_TSR] |= 1 << 26;
Andreas Färber70585812012-12-01 03:55:58 +0100878 if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) {
879 ppc_set_irq(cpu, PPC_INTERRUPT_FIT, 1);
880 }
Blue Swirl90e189e2009-08-16 11:13:18 +0000881 LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
882 (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1),
883 env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
j_mayer636aaad2007-03-31 11:38:38 +0000884}
885
886/* Programmable interval timer */
Andreas Färbere2684c02012-03-14 01:38:23 +0100887static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp)
j_mayer636aaad2007-03-31 11:38:38 +0000888{
Fabien Chouteauddd10552011-09-13 04:00:32 +0000889 ppc40x_timer_t *ppc40x_timer;
j_mayer636aaad2007-03-31 11:38:38 +0000890 uint64_t now, next;
891
Fabien Chouteauddd10552011-09-13 04:00:32 +0000892 ppc40x_timer = tb_env->opaque;
893 if (ppc40x_timer->pit_reload <= 1 ||
j_mayer4b6d0a42007-04-24 06:32:00 +0000894 !((env->spr[SPR_40x_TCR] >> 26) & 0x1) ||
895 (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) {
896 /* Stop PIT */
aliguorid12d51d2009-01-15 21:48:06 +0000897 LOG_TB("%s: stop PIT\n", __func__);
j_mayer4b6d0a42007-04-24 06:32:00 +0000898 qemu_del_timer(tb_env->decr_timer);
899 } else {
aliguorid12d51d2009-01-15 21:48:06 +0000900 LOG_TB("%s: start PIT %016" PRIx64 "\n",
Fabien Chouteauddd10552011-09-13 04:00:32 +0000901 __func__, ppc40x_timer->pit_reload);
Paolo Bonzini74475452011-03-11 16:47:48 +0100902 now = qemu_get_clock_ns(vm_clock);
Fabien Chouteauddd10552011-09-13 04:00:32 +0000903 next = now + muldiv64(ppc40x_timer->pit_reload,
Juan Quintela6ee093c2009-09-10 03:04:26 +0200904 get_ticks_per_sec(), tb_env->decr_freq);
j_mayer4b6d0a42007-04-24 06:32:00 +0000905 if (is_excp)
906 next += tb_env->decr_next - now;
j_mayer636aaad2007-03-31 11:38:38 +0000907 if (next == now)
908 next++;
909 qemu_mod_timer(tb_env->decr_timer, next);
910 tb_env->decr_next = next;
911 }
j_mayer4b6d0a42007-04-24 06:32:00 +0000912}
913
914static void cpu_4xx_pit_cb (void *opaque)
915{
Andreas Färber70585812012-12-01 03:55:58 +0100916 PowerPCCPU *cpu;
Andreas Färbere2684c02012-03-14 01:38:23 +0100917 CPUPPCState *env;
Anthony Liguoric227f092009-10-01 16:12:16 -0500918 ppc_tb_t *tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000919 ppc40x_timer_t *ppc40x_timer;
j_mayer4b6d0a42007-04-24 06:32:00 +0000920
921 env = opaque;
Andreas Färber70585812012-12-01 03:55:58 +0100922 cpu = ppc_env_get_cpu(env);
j_mayer4b6d0a42007-04-24 06:32:00 +0000923 tb_env = env->tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000924 ppc40x_timer = tb_env->opaque;
j_mayer636aaad2007-03-31 11:38:38 +0000925 env->spr[SPR_40x_TSR] |= 1 << 27;
Andreas Färber70585812012-12-01 03:55:58 +0100926 if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) {
927 ppc_set_irq(cpu, ppc40x_timer->decr_excp, 1);
928 }
j_mayer4b6d0a42007-04-24 06:32:00 +0000929 start_stop_pit(env, tb_env, 1);
Blue Swirl90e189e2009-08-16 11:13:18 +0000930 LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " "
931 "%016" PRIx64 "\n", __func__,
932 (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1),
933 (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1),
934 env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
Fabien Chouteauddd10552011-09-13 04:00:32 +0000935 ppc40x_timer->pit_reload);
j_mayer636aaad2007-03-31 11:38:38 +0000936}
937
938/* Watchdog timer */
939static void cpu_4xx_wdt_cb (void *opaque)
940{
Andreas Färber70585812012-12-01 03:55:58 +0100941 PowerPCCPU *cpu;
Andreas Färbere2684c02012-03-14 01:38:23 +0100942 CPUPPCState *env;
Anthony Liguoric227f092009-10-01 16:12:16 -0500943 ppc_tb_t *tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000944 ppc40x_timer_t *ppc40x_timer;
j_mayer636aaad2007-03-31 11:38:38 +0000945 uint64_t now, next;
946
947 env = opaque;
Andreas Färber70585812012-12-01 03:55:58 +0100948 cpu = ppc_env_get_cpu(env);
j_mayer636aaad2007-03-31 11:38:38 +0000949 tb_env = env->tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +0000950 ppc40x_timer = tb_env->opaque;
Paolo Bonzini74475452011-03-11 16:47:48 +0100951 now = qemu_get_clock_ns(vm_clock);
j_mayer636aaad2007-03-31 11:38:38 +0000952 switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
953 case 0:
954 next = 1 << 17;
955 break;
956 case 1:
957 next = 1 << 21;
958 break;
959 case 2:
960 next = 1 << 25;
961 break;
962 case 3:
963 next = 1 << 29;
964 break;
965 default:
966 /* Cannot occur, but makes gcc happy */
967 return;
968 }
Juan Quintela6ee093c2009-09-10 03:04:26 +0200969 next = now + muldiv64(next, get_ticks_per_sec(), tb_env->decr_freq);
j_mayer636aaad2007-03-31 11:38:38 +0000970 if (next == now)
971 next++;
Blue Swirl90e189e2009-08-16 11:13:18 +0000972 LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
973 env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
j_mayer636aaad2007-03-31 11:38:38 +0000974 switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
975 case 0x0:
976 case 0x1:
Fabien Chouteauddd10552011-09-13 04:00:32 +0000977 qemu_mod_timer(ppc40x_timer->wdt_timer, next);
978 ppc40x_timer->wdt_next = next;
j_mayer636aaad2007-03-31 11:38:38 +0000979 env->spr[SPR_40x_TSR] |= 1 << 31;
980 break;
981 case 0x2:
Fabien Chouteauddd10552011-09-13 04:00:32 +0000982 qemu_mod_timer(ppc40x_timer->wdt_timer, next);
983 ppc40x_timer->wdt_next = next;
j_mayer636aaad2007-03-31 11:38:38 +0000984 env->spr[SPR_40x_TSR] |= 1 << 30;
Andreas Färber70585812012-12-01 03:55:58 +0100985 if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) {
986 ppc_set_irq(cpu, PPC_INTERRUPT_WDT, 1);
987 }
j_mayer636aaad2007-03-31 11:38:38 +0000988 break;
989 case 0x3:
990 env->spr[SPR_40x_TSR] &= ~0x30000000;
991 env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000;
992 switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) {
993 case 0x0:
994 /* No reset */
995 break;
996 case 0x1: /* Core reset */
j_mayer8ecc7912007-04-16 20:09:45 +0000997 ppc40x_core_reset(env);
998 break;
j_mayer636aaad2007-03-31 11:38:38 +0000999 case 0x2: /* Chip reset */
j_mayer8ecc7912007-04-16 20:09:45 +00001000 ppc40x_chip_reset(env);
1001 break;
j_mayer636aaad2007-03-31 11:38:38 +00001002 case 0x3: /* System reset */
j_mayer8ecc7912007-04-16 20:09:45 +00001003 ppc40x_system_reset(env);
1004 break;
j_mayer636aaad2007-03-31 11:38:38 +00001005 }
1006 }
j_mayer76a66252007-03-07 08:32:30 +00001007}
1008
Andreas Färbere2684c02012-03-14 01:38:23 +01001009void store_40x_pit (CPUPPCState *env, target_ulong val)
j_mayer76a66252007-03-07 08:32:30 +00001010{
Anthony Liguoric227f092009-10-01 16:12:16 -05001011 ppc_tb_t *tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +00001012 ppc40x_timer_t *ppc40x_timer;
j_mayer636aaad2007-03-31 11:38:38 +00001013
1014 tb_env = env->tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +00001015 ppc40x_timer = tb_env->opaque;
Blue Swirl90e189e2009-08-16 11:13:18 +00001016 LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val);
Fabien Chouteauddd10552011-09-13 04:00:32 +00001017 ppc40x_timer->pit_reload = val;
j_mayer4b6d0a42007-04-24 06:32:00 +00001018 start_stop_pit(env, tb_env, 0);
j_mayer76a66252007-03-07 08:32:30 +00001019}
1020
Andreas Färbere2684c02012-03-14 01:38:23 +01001021target_ulong load_40x_pit (CPUPPCState *env)
j_mayer76a66252007-03-07 08:32:30 +00001022{
j_mayer636aaad2007-03-31 11:38:38 +00001023 return cpu_ppc_load_decr(env);
j_mayer76a66252007-03-07 08:32:30 +00001024}
1025
Fabien Chouteauddd10552011-09-13 04:00:32 +00001026static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq)
j_mayer4b6d0a42007-04-24 06:32:00 +00001027{
Andreas Färbere2684c02012-03-14 01:38:23 +01001028 CPUPPCState *env = opaque;
Anthony Liguoric227f092009-10-01 16:12:16 -05001029 ppc_tb_t *tb_env = env->tb_env;
j_mayer4b6d0a42007-04-24 06:32:00 +00001030
aliguorid12d51d2009-01-15 21:48:06 +00001031 LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__,
j_mayeraae93662007-11-24 02:56:36 +00001032 freq);
j_mayer4b6d0a42007-04-24 06:32:00 +00001033 tb_env->tb_freq = freq;
j_mayerdbdd2502007-10-14 09:35:30 +00001034 tb_env->decr_freq = freq;
j_mayer4b6d0a42007-04-24 06:32:00 +00001035 /* XXX: we should also update all timers */
1036}
1037
Andreas Färbere2684c02012-03-14 01:38:23 +01001038clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq,
Edgar E. Iglesiasd63cb482010-09-20 19:08:42 +02001039 unsigned int decr_excp)
j_mayer636aaad2007-03-31 11:38:38 +00001040{
Anthony Liguoric227f092009-10-01 16:12:16 -05001041 ppc_tb_t *tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +00001042 ppc40x_timer_t *ppc40x_timer;
j_mayer636aaad2007-03-31 11:38:38 +00001043
Anthony Liguori7267c092011-08-20 22:09:37 -05001044 tb_env = g_malloc0(sizeof(ppc_tb_t));
j_mayer8ecc7912007-04-16 20:09:45 +00001045 env->tb_env = tb_env;
Fabien Chouteauddd10552011-09-13 04:00:32 +00001046 tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED;
1047 ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t));
j_mayer8ecc7912007-04-16 20:09:45 +00001048 tb_env->tb_freq = freq;
j_mayerdbdd2502007-10-14 09:35:30 +00001049 tb_env->decr_freq = freq;
Fabien Chouteauddd10552011-09-13 04:00:32 +00001050 tb_env->opaque = ppc40x_timer;
aliguorid12d51d2009-01-15 21:48:06 +00001051 LOG_TB("%s freq %" PRIu32 "\n", __func__, freq);
Fabien Chouteauddd10552011-09-13 04:00:32 +00001052 if (ppc40x_timer != NULL) {
j_mayer636aaad2007-03-31 11:38:38 +00001053 /* We use decr timer for PIT */
Paolo Bonzini74475452011-03-11 16:47:48 +01001054 tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &cpu_4xx_pit_cb, env);
Fabien Chouteauddd10552011-09-13 04:00:32 +00001055 ppc40x_timer->fit_timer =
Paolo Bonzini74475452011-03-11 16:47:48 +01001056 qemu_new_timer_ns(vm_clock, &cpu_4xx_fit_cb, env);
Fabien Chouteauddd10552011-09-13 04:00:32 +00001057 ppc40x_timer->wdt_timer =
Paolo Bonzini74475452011-03-11 16:47:48 +01001058 qemu_new_timer_ns(vm_clock, &cpu_4xx_wdt_cb, env);
Fabien Chouteauddd10552011-09-13 04:00:32 +00001059 ppc40x_timer->decr_excp = decr_excp;
j_mayer636aaad2007-03-31 11:38:38 +00001060 }
j_mayer8ecc7912007-04-16 20:09:45 +00001061
Fabien Chouteauddd10552011-09-13 04:00:32 +00001062 return &ppc_40x_set_tb_clk;
j_mayer76a66252007-03-07 08:32:30 +00001063}
1064
j_mayer2e719ba2007-04-12 21:11:03 +00001065/*****************************************************************************/
1066/* Embedded PowerPC Device Control Registers */
Anthony Liguoric227f092009-10-01 16:12:16 -05001067typedef struct ppc_dcrn_t ppc_dcrn_t;
1068struct ppc_dcrn_t {
j_mayer2e719ba2007-04-12 21:11:03 +00001069 dcr_read_cb dcr_read;
1070 dcr_write_cb dcr_write;
1071 void *opaque;
1072};
1073
j_mayera750fc02007-09-26 23:54:22 +00001074/* XXX: on 460, DCR addresses are 32 bits wide,
1075 * using DCRIPR to get the 22 upper bits of the DCR address
1076 */
j_mayer2e719ba2007-04-12 21:11:03 +00001077#define DCRN_NB 1024
Anthony Liguoric227f092009-10-01 16:12:16 -05001078struct ppc_dcr_t {
1079 ppc_dcrn_t dcrn[DCRN_NB];
j_mayer2e719ba2007-04-12 21:11:03 +00001080 int (*read_error)(int dcrn);
1081 int (*write_error)(int dcrn);
1082};
1083
Alexander Graf73b01962009-12-21 14:02:39 +01001084int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
j_mayer2e719ba2007-04-12 21:11:03 +00001085{
Anthony Liguoric227f092009-10-01 16:12:16 -05001086 ppc_dcrn_t *dcr;
j_mayer2e719ba2007-04-12 21:11:03 +00001087
1088 if (dcrn < 0 || dcrn >= DCRN_NB)
1089 goto error;
1090 dcr = &dcr_env->dcrn[dcrn];
1091 if (dcr->dcr_read == NULL)
1092 goto error;
1093 *valp = (*dcr->dcr_read)(dcr->opaque, dcrn);
1094
1095 return 0;
1096
1097 error:
1098 if (dcr_env->read_error != NULL)
1099 return (*dcr_env->read_error)(dcrn);
1100
1101 return -1;
1102}
1103
Alexander Graf73b01962009-12-21 14:02:39 +01001104int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
j_mayer2e719ba2007-04-12 21:11:03 +00001105{
Anthony Liguoric227f092009-10-01 16:12:16 -05001106 ppc_dcrn_t *dcr;
j_mayer2e719ba2007-04-12 21:11:03 +00001107
1108 if (dcrn < 0 || dcrn >= DCRN_NB)
1109 goto error;
1110 dcr = &dcr_env->dcrn[dcrn];
1111 if (dcr->dcr_write == NULL)
1112 goto error;
1113 (*dcr->dcr_write)(dcr->opaque, dcrn, val);
1114
1115 return 0;
1116
1117 error:
1118 if (dcr_env->write_error != NULL)
1119 return (*dcr_env->write_error)(dcrn);
1120
1121 return -1;
1122}
1123
Andreas Färbere2684c02012-03-14 01:38:23 +01001124int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque,
j_mayer2e719ba2007-04-12 21:11:03 +00001125 dcr_read_cb dcr_read, dcr_write_cb dcr_write)
1126{
Anthony Liguoric227f092009-10-01 16:12:16 -05001127 ppc_dcr_t *dcr_env;
1128 ppc_dcrn_t *dcr;
j_mayer2e719ba2007-04-12 21:11:03 +00001129
1130 dcr_env = env->dcr_env;
1131 if (dcr_env == NULL)
1132 return -1;
1133 if (dcrn < 0 || dcrn >= DCRN_NB)
1134 return -1;
1135 dcr = &dcr_env->dcrn[dcrn];
1136 if (dcr->opaque != NULL ||
1137 dcr->dcr_read != NULL ||
1138 dcr->dcr_write != NULL)
1139 return -1;
1140 dcr->opaque = opaque;
1141 dcr->dcr_read = dcr_read;
1142 dcr->dcr_write = dcr_write;
1143
1144 return 0;
1145}
1146
Andreas Färbere2684c02012-03-14 01:38:23 +01001147int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn),
j_mayer2e719ba2007-04-12 21:11:03 +00001148 int (*write_error)(int dcrn))
1149{
Anthony Liguoric227f092009-10-01 16:12:16 -05001150 ppc_dcr_t *dcr_env;
j_mayer2e719ba2007-04-12 21:11:03 +00001151
Anthony Liguori7267c092011-08-20 22:09:37 -05001152 dcr_env = g_malloc0(sizeof(ppc_dcr_t));
j_mayer2e719ba2007-04-12 21:11:03 +00001153 dcr_env->read_error = read_error;
1154 dcr_env->write_error = write_error;
1155 env->dcr_env = dcr_env;
1156
1157 return 0;
1158}
1159
bellard64201202004-05-26 22:55:16 +00001160/*****************************************************************************/
1161/* Debug port */
bellardfd0bbb12004-06-21 16:53:42 +00001162void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
bellard64201202004-05-26 22:55:16 +00001163{
1164 addr &= 0xF;
1165 switch (addr) {
1166 case 0:
1167 printf("%c", val);
1168 break;
1169 case 1:
1170 printf("\n");
1171 fflush(stdout);
1172 break;
1173 case 2:
j_mayeraae93662007-11-24 02:56:36 +00001174 printf("Set loglevel to %04" PRIx32 "\n", val);
bellardfd0bbb12004-06-21 16:53:42 +00001175 cpu_set_log(val | 0x100);
bellard64201202004-05-26 22:55:16 +00001176 break;
1177 }
1178}
1179
1180/*****************************************************************************/
1181/* NVRAM helpers */
Anthony Liguoric227f092009-10-01 16:12:16 -05001182static inline uint32_t nvram_read (nvram_t *nvram, uint32_t addr)
bellard64201202004-05-26 22:55:16 +00001183{
Dong Xu Wang3a931132011-11-29 16:52:38 +08001184 return (*nvram->read_fn)(nvram->opaque, addr);
bellard64201202004-05-26 22:55:16 +00001185}
1186
Anthony Liguoric227f092009-10-01 16:12:16 -05001187static inline void nvram_write (nvram_t *nvram, uint32_t addr, uint32_t val)
bellard64201202004-05-26 22:55:16 +00001188{
j_mayer3cbee152007-10-28 23:42:18 +00001189 (*nvram->write_fn)(nvram->opaque, addr, val);
bellard64201202004-05-26 22:55:16 +00001190}
1191
Blue Swirl43448292012-10-28 11:04:49 +00001192static void NVRAM_set_byte(nvram_t *nvram, uint32_t addr, uint8_t value)
bellard64201202004-05-26 22:55:16 +00001193{
j_mayer3cbee152007-10-28 23:42:18 +00001194 nvram_write(nvram, addr, value);
bellard64201202004-05-26 22:55:16 +00001195}
1196
Blue Swirl43448292012-10-28 11:04:49 +00001197static uint8_t NVRAM_get_byte(nvram_t *nvram, uint32_t addr)
j_mayer3cbee152007-10-28 23:42:18 +00001198{
1199 return nvram_read(nvram, addr);
1200}
1201
Blue Swirl43448292012-10-28 11:04:49 +00001202static void NVRAM_set_word(nvram_t *nvram, uint32_t addr, uint16_t value)
j_mayer3cbee152007-10-28 23:42:18 +00001203{
1204 nvram_write(nvram, addr, value >> 8);
1205 nvram_write(nvram, addr + 1, value & 0xFF);
1206}
1207
Blue Swirl43448292012-10-28 11:04:49 +00001208static uint16_t NVRAM_get_word(nvram_t *nvram, uint32_t addr)
bellard64201202004-05-26 22:55:16 +00001209{
1210 uint16_t tmp;
1211
j_mayer3cbee152007-10-28 23:42:18 +00001212 tmp = nvram_read(nvram, addr) << 8;
1213 tmp |= nvram_read(nvram, addr + 1);
1214
bellard64201202004-05-26 22:55:16 +00001215 return tmp;
1216}
1217
Blue Swirl43448292012-10-28 11:04:49 +00001218static void NVRAM_set_lword(nvram_t *nvram, uint32_t addr, uint32_t value)
bellard64201202004-05-26 22:55:16 +00001219{
j_mayer3cbee152007-10-28 23:42:18 +00001220 nvram_write(nvram, addr, value >> 24);
1221 nvram_write(nvram, addr + 1, (value >> 16) & 0xFF);
1222 nvram_write(nvram, addr + 2, (value >> 8) & 0xFF);
1223 nvram_write(nvram, addr + 3, value & 0xFF);
bellard64201202004-05-26 22:55:16 +00001224}
1225
Anthony Liguoric227f092009-10-01 16:12:16 -05001226uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr)
bellard64201202004-05-26 22:55:16 +00001227{
1228 uint32_t tmp;
1229
j_mayer3cbee152007-10-28 23:42:18 +00001230 tmp = nvram_read(nvram, addr) << 24;
1231 tmp |= nvram_read(nvram, addr + 1) << 16;
1232 tmp |= nvram_read(nvram, addr + 2) << 8;
1233 tmp |= nvram_read(nvram, addr + 3);
j_mayer76a66252007-03-07 08:32:30 +00001234
bellard64201202004-05-26 22:55:16 +00001235 return tmp;
1236}
1237
Blue Swirl43448292012-10-28 11:04:49 +00001238static void NVRAM_set_string(nvram_t *nvram, uint32_t addr, const char *str,
1239 uint32_t max)
bellard64201202004-05-26 22:55:16 +00001240{
1241 int i;
1242
1243 for (i = 0; i < max && str[i] != '\0'; i++) {
j_mayer3cbee152007-10-28 23:42:18 +00001244 nvram_write(nvram, addr + i, str[i]);
bellard64201202004-05-26 22:55:16 +00001245 }
j_mayer3cbee152007-10-28 23:42:18 +00001246 nvram_write(nvram, addr + i, str[i]);
1247 nvram_write(nvram, addr + max - 1, '\0');
bellard64201202004-05-26 22:55:16 +00001248}
1249
Anthony Liguoric227f092009-10-01 16:12:16 -05001250int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max)
bellard64201202004-05-26 22:55:16 +00001251{
1252 int i;
1253
1254 memset(dst, 0, max);
1255 for (i = 0; i < max; i++) {
1256 dst[i] = NVRAM_get_byte(nvram, addr + i);
1257 if (dst[i] == '\0')
1258 break;
1259 }
1260
1261 return i;
1262}
1263
1264static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
1265{
1266 uint16_t tmp;
1267 uint16_t pd, pd1, pd2;
1268
1269 tmp = prev >> 8;
1270 pd = prev ^ value;
1271 pd1 = pd & 0x000F;
1272 pd2 = ((pd >> 4) & 0x000F) ^ pd1;
1273 tmp ^= (pd1 << 3) | (pd1 << 8);
1274 tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
1275
1276 return tmp;
1277}
1278
Anthony Liguoric227f092009-10-01 16:12:16 -05001279static uint16_t NVRAM_compute_crc (nvram_t *nvram, uint32_t start, uint32_t count)
bellard64201202004-05-26 22:55:16 +00001280{
1281 uint32_t i;
1282 uint16_t crc = 0xFFFF;
1283 int odd;
1284
1285 odd = count & 1;
1286 count &= ~1;
1287 for (i = 0; i != count; i++) {
j_mayer76a66252007-03-07 08:32:30 +00001288 crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
bellard64201202004-05-26 22:55:16 +00001289 }
1290 if (odd) {
j_mayer76a66252007-03-07 08:32:30 +00001291 crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
bellard64201202004-05-26 22:55:16 +00001292 }
1293
1294 return crc;
1295}
1296
bellardfd0bbb12004-06-21 16:53:42 +00001297#define CMDLINE_ADDR 0x017ff000
1298
Anthony Liguoric227f092009-10-01 16:12:16 -05001299int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size,
blueswir1b55266b2008-09-20 08:07:15 +00001300 const char *arch,
bellard64201202004-05-26 22:55:16 +00001301 uint32_t RAM_size, int boot_device,
1302 uint32_t kernel_image, uint32_t kernel_size,
bellardfd0bbb12004-06-21 16:53:42 +00001303 const char *cmdline,
bellard64201202004-05-26 22:55:16 +00001304 uint32_t initrd_image, uint32_t initrd_size,
bellardfd0bbb12004-06-21 16:53:42 +00001305 uint32_t NVRAM_image,
1306 int width, int height, int depth)
bellard64201202004-05-26 22:55:16 +00001307{
1308 uint16_t crc;
1309
1310 /* Set parameters for Open Hack'Ware BIOS */
1311 NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
1312 NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */
1313 NVRAM_set_word(nvram, 0x14, NVRAM_size);
1314 NVRAM_set_string(nvram, 0x20, arch, 16);
1315 NVRAM_set_lword(nvram, 0x30, RAM_size);
1316 NVRAM_set_byte(nvram, 0x34, boot_device);
1317 NVRAM_set_lword(nvram, 0x38, kernel_image);
1318 NVRAM_set_lword(nvram, 0x3C, kernel_size);
bellardfd0bbb12004-06-21 16:53:42 +00001319 if (cmdline) {
1320 /* XXX: put the cmdline in NVRAM too ? */
Gerd Hoffmann3c178e72009-10-07 13:37:06 +02001321 pstrcpy_targphys("cmdline", CMDLINE_ADDR, RAM_size - CMDLINE_ADDR, cmdline);
bellardfd0bbb12004-06-21 16:53:42 +00001322 NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR);
1323 NVRAM_set_lword(nvram, 0x44, strlen(cmdline));
1324 } else {
1325 NVRAM_set_lword(nvram, 0x40, 0);
1326 NVRAM_set_lword(nvram, 0x44, 0);
1327 }
bellard64201202004-05-26 22:55:16 +00001328 NVRAM_set_lword(nvram, 0x48, initrd_image);
1329 NVRAM_set_lword(nvram, 0x4C, initrd_size);
1330 NVRAM_set_lword(nvram, 0x50, NVRAM_image);
bellardfd0bbb12004-06-21 16:53:42 +00001331
1332 NVRAM_set_word(nvram, 0x54, width);
1333 NVRAM_set_word(nvram, 0x56, height);
1334 NVRAM_set_word(nvram, 0x58, depth);
1335 crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
j_mayer3cbee152007-10-28 23:42:18 +00001336 NVRAM_set_word(nvram, 0xFC, crc);
bellard64201202004-05-26 22:55:16 +00001337
1338 return 0;
bellarda541f292004-04-12 20:39:29 +00001339}