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 */ 240d75590dSPeter Maydell #include "qemu/osdep.h" 254771d756SPaolo Bonzini #include "cpu.h" 2683c9f4caSPaolo Bonzini #include "hw/hw.h" 270d09e41aSPaolo Bonzini #include "hw/ppc/ppc.h" 282b927571SAndreas Färber #include "hw/ppc/ppc_e500.h" 291de7afc9SPaolo Bonzini #include "qemu/timer.h" 309c17d615SPaolo Bonzini #include "sysemu/sysemu.h" 310ce470cdSAlexey Kardashevskiy #include "sysemu/cpus.h" 321de7afc9SPaolo Bonzini #include "qemu/log.h" 3398a8b524SAlexey Kardashevskiy #include "qemu/error-report.h" 349c17d615SPaolo Bonzini #include "sysemu/kvm.h" 35fc87e185SAlexander Graf #include "kvm_ppc.h" 3698a8b524SAlexey Kardashevskiy #include "trace.h" 37a541f297Sbellard 38e9df014cSj_mayer //#define PPC_DEBUG_IRQ 394b6d0a4cSj_mayer //#define PPC_DEBUG_TB 40e9df014cSj_mayer 41d12d51d5Saliguori #ifdef PPC_DEBUG_IRQ 4293fcfe39Saliguori # define LOG_IRQ(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) 43d12d51d5Saliguori #else 44d12d51d5Saliguori # define LOG_IRQ(...) do { } while (0) 45d12d51d5Saliguori #endif 46d12d51d5Saliguori 47d12d51d5Saliguori 48d12d51d5Saliguori #ifdef PPC_DEBUG_TB 4993fcfe39Saliguori # define LOG_TB(...) qemu_log(__VA_ARGS__) 50d12d51d5Saliguori #else 51d12d51d5Saliguori # define LOG_TB(...) do { } while (0) 52d12d51d5Saliguori #endif 53d12d51d5Saliguori 54e2684c0bSAndreas Färber static void cpu_ppc_tb_stop (CPUPPCState *env); 55e2684c0bSAndreas Färber static void cpu_ppc_tb_start (CPUPPCState *env); 56dbdd2506Sj_mayer 577058581aSAndreas Färber void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level) 5847103572Sj_mayer { 59d8ed887bSAndreas Färber CPUState *cs = CPU(cpu); 607058581aSAndreas Färber CPUPPCState *env = &cpu->env; 618d04fb55SJan Kiszka unsigned int old_pending; 628d04fb55SJan Kiszka bool locked = false; 638d04fb55SJan Kiszka 648d04fb55SJan Kiszka /* We may already have the BQL if coming from the reset path */ 658d04fb55SJan Kiszka if (!qemu_mutex_iothread_locked()) { 668d04fb55SJan Kiszka locked = true; 678d04fb55SJan Kiszka qemu_mutex_lock_iothread(); 688d04fb55SJan Kiszka } 698d04fb55SJan Kiszka 708d04fb55SJan Kiszka old_pending = env->pending_interrupts; 71fc87e185SAlexander Graf 7247103572Sj_mayer if (level) { 7347103572Sj_mayer env->pending_interrupts |= 1 << n_IRQ; 74c3affe56SAndreas Färber cpu_interrupt(cs, CPU_INTERRUPT_HARD); 7547103572Sj_mayer } else { 7647103572Sj_mayer env->pending_interrupts &= ~(1 << n_IRQ); 77d8ed887bSAndreas Färber if (env->pending_interrupts == 0) { 78d8ed887bSAndreas Färber cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); 79d8ed887bSAndreas Färber } 8047103572Sj_mayer } 81fc87e185SAlexander Graf 82fc87e185SAlexander Graf if (old_pending != env->pending_interrupts) { 837058581aSAndreas Färber kvmppc_set_interrupt(cpu, n_IRQ, level); 84fc87e185SAlexander Graf } 85fc87e185SAlexander Graf 868d04fb55SJan Kiszka 87d12d51d5Saliguori LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32 88aae9366aSj_mayer "req %08x\n", __func__, env, n_IRQ, level, 89259186a7SAndreas Färber env->pending_interrupts, CPU(cpu)->interrupt_request); 908d04fb55SJan Kiszka 918d04fb55SJan Kiszka if (locked) { 928d04fb55SJan Kiszka qemu_mutex_unlock_iothread(); 938d04fb55SJan Kiszka } 94a496775fSj_mayer } 9547103572Sj_mayer 96e9df014cSj_mayer /* PowerPC 6xx / 7xx internal IRQ controller */ 97e9df014cSj_mayer static void ppc6xx_set_irq(void *opaque, int pin, int level) 98d537cf6cSpbrook { 99a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 100a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 101e9df014cSj_mayer int cur_level; 102d537cf6cSpbrook 103d12d51d5Saliguori LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 104a496775fSj_mayer env, pin, level); 105e9df014cSj_mayer cur_level = (env->irq_input_state >> pin) & 1; 106e9df014cSj_mayer /* Don't generate spurious events */ 10724be5ae3Sj_mayer if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { 108259186a7SAndreas Färber CPUState *cs = CPU(cpu); 109259186a7SAndreas Färber 110e9df014cSj_mayer switch (pin) { 111dbdd2506Sj_mayer case PPC6xx_INPUT_TBEN: 112dbdd2506Sj_mayer /* Level sensitive - active high */ 113d12d51d5Saliguori LOG_IRQ("%s: %s the time base\n", 114dbdd2506Sj_mayer __func__, level ? "start" : "stop"); 115dbdd2506Sj_mayer if (level) { 116dbdd2506Sj_mayer cpu_ppc_tb_start(env); 117dbdd2506Sj_mayer } else { 118dbdd2506Sj_mayer cpu_ppc_tb_stop(env); 119dbdd2506Sj_mayer } 12024be5ae3Sj_mayer case PPC6xx_INPUT_INT: 12124be5ae3Sj_mayer /* Level sensitive - active high */ 122d12d51d5Saliguori LOG_IRQ("%s: set the external IRQ state to %d\n", 123a496775fSj_mayer __func__, level); 1247058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 125e9df014cSj_mayer break; 12624be5ae3Sj_mayer case PPC6xx_INPUT_SMI: 127e9df014cSj_mayer /* Level sensitive - active high */ 128d12d51d5Saliguori LOG_IRQ("%s: set the SMI IRQ state to %d\n", 129a496775fSj_mayer __func__, level); 1307058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_SMI, level); 131e9df014cSj_mayer break; 13224be5ae3Sj_mayer case PPC6xx_INPUT_MCP: 133e9df014cSj_mayer /* Negative edge sensitive */ 134e9df014cSj_mayer /* XXX: TODO: actual reaction may depends on HID0 status 135e9df014cSj_mayer * 603/604/740/750: check HID0[EMCP] 136e9df014cSj_mayer */ 137e9df014cSj_mayer if (cur_level == 1 && level == 0) { 138d12d51d5Saliguori LOG_IRQ("%s: raise machine check state\n", 139a496775fSj_mayer __func__); 1407058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); 141d537cf6cSpbrook } 142e9df014cSj_mayer break; 14324be5ae3Sj_mayer case PPC6xx_INPUT_CKSTP_IN: 144e9df014cSj_mayer /* Level sensitive - active low */ 145e9df014cSj_mayer /* XXX: TODO: relay the signal to CKSTP_OUT pin */ 146e63ecc6fSj_mayer /* XXX: Note that the only way to restart the CPU is to reset it */ 147e9df014cSj_mayer if (level) { 148d12d51d5Saliguori LOG_IRQ("%s: stop the CPU\n", __func__); 149259186a7SAndreas Färber cs->halted = 1; 150d537cf6cSpbrook } 15147103572Sj_mayer break; 15224be5ae3Sj_mayer case PPC6xx_INPUT_HRESET: 153e9df014cSj_mayer /* Level sensitive - active low */ 154e9df014cSj_mayer if (level) { 155d12d51d5Saliguori LOG_IRQ("%s: reset the CPU\n", __func__); 156c3affe56SAndreas Färber cpu_interrupt(cs, CPU_INTERRUPT_RESET); 157e9df014cSj_mayer } 15847103572Sj_mayer break; 15924be5ae3Sj_mayer case PPC6xx_INPUT_SRESET: 160d12d51d5Saliguori LOG_IRQ("%s: set the RESET IRQ state to %d\n", 161a496775fSj_mayer __func__, level); 1627058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); 16347103572Sj_mayer break; 164e9df014cSj_mayer default: 165e9df014cSj_mayer /* Unknown pin - do nothing */ 166d12d51d5Saliguori LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 16747103572Sj_mayer return; 16847103572Sj_mayer } 169e9df014cSj_mayer if (level) 170e9df014cSj_mayer env->irq_input_state |= 1 << pin; 171e9df014cSj_mayer else 172e9df014cSj_mayer env->irq_input_state &= ~(1 << pin); 173e9df014cSj_mayer } 174e9df014cSj_mayer } 175e9df014cSj_mayer 176aa5a9e24SPaolo Bonzini void ppc6xx_irq_init(PowerPCCPU *cpu) 177e9df014cSj_mayer { 178aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 179a0961245SAndreas Färber 180a0961245SAndreas Färber env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, cpu, 1817b62a955Sj_mayer PPC6xx_INPUT_NB); 18247103572Sj_mayer } 18347103572Sj_mayer 18400af685fSj_mayer #if defined(TARGET_PPC64) 185d0dfae6eSj_mayer /* PowerPC 970 internal IRQ controller */ 186d0dfae6eSj_mayer static void ppc970_set_irq(void *opaque, int pin, int level) 187d0dfae6eSj_mayer { 188a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 189a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 190d0dfae6eSj_mayer int cur_level; 191d0dfae6eSj_mayer 192d12d51d5Saliguori LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 193d0dfae6eSj_mayer env, pin, level); 194d0dfae6eSj_mayer cur_level = (env->irq_input_state >> pin) & 1; 195d0dfae6eSj_mayer /* Don't generate spurious events */ 196d0dfae6eSj_mayer if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { 197259186a7SAndreas Färber CPUState *cs = CPU(cpu); 198259186a7SAndreas Färber 199d0dfae6eSj_mayer switch (pin) { 200d0dfae6eSj_mayer case PPC970_INPUT_INT: 201d0dfae6eSj_mayer /* Level sensitive - active high */ 202d12d51d5Saliguori LOG_IRQ("%s: set the external IRQ state to %d\n", 203d0dfae6eSj_mayer __func__, level); 2047058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 205d0dfae6eSj_mayer break; 206d0dfae6eSj_mayer case PPC970_INPUT_THINT: 207d0dfae6eSj_mayer /* Level sensitive - active high */ 208d12d51d5Saliguori LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__, 209d0dfae6eSj_mayer level); 2107058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_THERM, level); 211d0dfae6eSj_mayer break; 212d0dfae6eSj_mayer case PPC970_INPUT_MCP: 213d0dfae6eSj_mayer /* Negative edge sensitive */ 214d0dfae6eSj_mayer /* XXX: TODO: actual reaction may depends on HID0 status 215d0dfae6eSj_mayer * 603/604/740/750: check HID0[EMCP] 216d0dfae6eSj_mayer */ 217d0dfae6eSj_mayer if (cur_level == 1 && level == 0) { 218d12d51d5Saliguori LOG_IRQ("%s: raise machine check state\n", 219d0dfae6eSj_mayer __func__); 2207058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); 221d0dfae6eSj_mayer } 222d0dfae6eSj_mayer break; 223d0dfae6eSj_mayer case PPC970_INPUT_CKSTP: 224d0dfae6eSj_mayer /* Level sensitive - active low */ 225d0dfae6eSj_mayer /* XXX: TODO: relay the signal to CKSTP_OUT pin */ 226d0dfae6eSj_mayer if (level) { 227d12d51d5Saliguori LOG_IRQ("%s: stop the CPU\n", __func__); 228259186a7SAndreas Färber cs->halted = 1; 229d0dfae6eSj_mayer } else { 230d12d51d5Saliguori LOG_IRQ("%s: restart the CPU\n", __func__); 231259186a7SAndreas Färber cs->halted = 0; 232259186a7SAndreas Färber qemu_cpu_kick(cs); 233d0dfae6eSj_mayer } 234d0dfae6eSj_mayer break; 235d0dfae6eSj_mayer case PPC970_INPUT_HRESET: 236d0dfae6eSj_mayer /* Level sensitive - active low */ 237d0dfae6eSj_mayer if (level) { 238c3affe56SAndreas Färber cpu_interrupt(cs, CPU_INTERRUPT_RESET); 239d0dfae6eSj_mayer } 240d0dfae6eSj_mayer break; 241d0dfae6eSj_mayer case PPC970_INPUT_SRESET: 242d12d51d5Saliguori LOG_IRQ("%s: set the RESET IRQ state to %d\n", 243d0dfae6eSj_mayer __func__, level); 2447058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); 245d0dfae6eSj_mayer break; 246d0dfae6eSj_mayer case PPC970_INPUT_TBEN: 247d12d51d5Saliguori LOG_IRQ("%s: set the TBEN state to %d\n", __func__, 248d0dfae6eSj_mayer level); 249d0dfae6eSj_mayer /* XXX: TODO */ 250d0dfae6eSj_mayer break; 251d0dfae6eSj_mayer default: 252d0dfae6eSj_mayer /* Unknown pin - do nothing */ 253d12d51d5Saliguori LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 254d0dfae6eSj_mayer return; 255d0dfae6eSj_mayer } 256d0dfae6eSj_mayer if (level) 257d0dfae6eSj_mayer env->irq_input_state |= 1 << pin; 258d0dfae6eSj_mayer else 259d0dfae6eSj_mayer env->irq_input_state &= ~(1 << pin); 260d0dfae6eSj_mayer } 261d0dfae6eSj_mayer } 262d0dfae6eSj_mayer 263aa5a9e24SPaolo Bonzini void ppc970_irq_init(PowerPCCPU *cpu) 264d0dfae6eSj_mayer { 265aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 266a0961245SAndreas Färber 267a0961245SAndreas Färber env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, cpu, 2687b62a955Sj_mayer PPC970_INPUT_NB); 269d0dfae6eSj_mayer } 2709d52e907SDavid Gibson 2719d52e907SDavid Gibson /* POWER7 internal IRQ controller */ 2729d52e907SDavid Gibson static void power7_set_irq(void *opaque, int pin, int level) 2739d52e907SDavid Gibson { 274a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 275a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 2769d52e907SDavid Gibson 2779d52e907SDavid Gibson LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 2789d52e907SDavid Gibson env, pin, level); 2799d52e907SDavid Gibson 2809d52e907SDavid Gibson switch (pin) { 2819d52e907SDavid Gibson case POWER7_INPUT_INT: 2829d52e907SDavid Gibson /* Level sensitive - active high */ 2839d52e907SDavid Gibson LOG_IRQ("%s: set the external IRQ state to %d\n", 2849d52e907SDavid Gibson __func__, level); 2857058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 2869d52e907SDavid Gibson break; 2879d52e907SDavid Gibson default: 2889d52e907SDavid Gibson /* Unknown pin - do nothing */ 2899d52e907SDavid Gibson LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 2909d52e907SDavid Gibson return; 2919d52e907SDavid Gibson } 2929d52e907SDavid Gibson if (level) { 2939d52e907SDavid Gibson env->irq_input_state |= 1 << pin; 2949d52e907SDavid Gibson } else { 2959d52e907SDavid Gibson env->irq_input_state &= ~(1 << pin); 2969d52e907SDavid Gibson } 2979d52e907SDavid Gibson } 2989d52e907SDavid Gibson 299aa5a9e24SPaolo Bonzini void ppcPOWER7_irq_init(PowerPCCPU *cpu) 3009d52e907SDavid Gibson { 301aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 302a0961245SAndreas Färber 303a0961245SAndreas Färber env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu, 3049d52e907SDavid Gibson POWER7_INPUT_NB); 3059d52e907SDavid Gibson } 30667afe775SBenjamin Herrenschmidt 30767afe775SBenjamin Herrenschmidt /* POWER9 internal IRQ controller */ 30867afe775SBenjamin Herrenschmidt static void power9_set_irq(void *opaque, int pin, int level) 30967afe775SBenjamin Herrenschmidt { 31067afe775SBenjamin Herrenschmidt PowerPCCPU *cpu = opaque; 31167afe775SBenjamin Herrenschmidt CPUPPCState *env = &cpu->env; 31267afe775SBenjamin Herrenschmidt 31367afe775SBenjamin Herrenschmidt LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 31467afe775SBenjamin Herrenschmidt env, pin, level); 31567afe775SBenjamin Herrenschmidt 31667afe775SBenjamin Herrenschmidt switch (pin) { 31767afe775SBenjamin Herrenschmidt case POWER9_INPUT_INT: 31867afe775SBenjamin Herrenschmidt /* Level sensitive - active high */ 31967afe775SBenjamin Herrenschmidt LOG_IRQ("%s: set the external IRQ state to %d\n", 32067afe775SBenjamin Herrenschmidt __func__, level); 32167afe775SBenjamin Herrenschmidt ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 32267afe775SBenjamin Herrenschmidt break; 32367afe775SBenjamin Herrenschmidt case POWER9_INPUT_HINT: 32467afe775SBenjamin Herrenschmidt /* Level sensitive - active high */ 32567afe775SBenjamin Herrenschmidt LOG_IRQ("%s: set the external IRQ state to %d\n", 32667afe775SBenjamin Herrenschmidt __func__, level); 32767afe775SBenjamin Herrenschmidt ppc_set_irq(cpu, PPC_INTERRUPT_HVIRT, level); 32867afe775SBenjamin Herrenschmidt break; 32967afe775SBenjamin Herrenschmidt default: 33067afe775SBenjamin Herrenschmidt /* Unknown pin - do nothing */ 33167afe775SBenjamin Herrenschmidt LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 33267afe775SBenjamin Herrenschmidt return; 33367afe775SBenjamin Herrenschmidt } 33467afe775SBenjamin Herrenschmidt if (level) { 33567afe775SBenjamin Herrenschmidt env->irq_input_state |= 1 << pin; 33667afe775SBenjamin Herrenschmidt } else { 33767afe775SBenjamin Herrenschmidt env->irq_input_state &= ~(1 << pin); 33867afe775SBenjamin Herrenschmidt } 33967afe775SBenjamin Herrenschmidt } 34067afe775SBenjamin Herrenschmidt 34167afe775SBenjamin Herrenschmidt void ppcPOWER9_irq_init(PowerPCCPU *cpu) 34267afe775SBenjamin Herrenschmidt { 34367afe775SBenjamin Herrenschmidt CPUPPCState *env = &cpu->env; 34467afe775SBenjamin Herrenschmidt 34567afe775SBenjamin Herrenschmidt env->irq_inputs = (void **)qemu_allocate_irqs(&power9_set_irq, cpu, 34667afe775SBenjamin Herrenschmidt POWER9_INPUT_NB); 34767afe775SBenjamin Herrenschmidt } 34800af685fSj_mayer #endif /* defined(TARGET_PPC64) */ 349d0dfae6eSj_mayer 35052144b69SThomas Huth void ppc40x_core_reset(PowerPCCPU *cpu) 35152144b69SThomas Huth { 35252144b69SThomas Huth CPUPPCState *env = &cpu->env; 35352144b69SThomas Huth target_ulong dbsr; 35452144b69SThomas Huth 35552144b69SThomas Huth qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC core\n"); 35652144b69SThomas Huth cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); 35752144b69SThomas Huth dbsr = env->spr[SPR_40x_DBSR]; 35852144b69SThomas Huth dbsr &= ~0x00000300; 35952144b69SThomas Huth dbsr |= 0x00000100; 36052144b69SThomas Huth env->spr[SPR_40x_DBSR] = dbsr; 36152144b69SThomas Huth } 36252144b69SThomas Huth 36352144b69SThomas Huth void ppc40x_chip_reset(PowerPCCPU *cpu) 36452144b69SThomas Huth { 36552144b69SThomas Huth CPUPPCState *env = &cpu->env; 36652144b69SThomas Huth target_ulong dbsr; 36752144b69SThomas Huth 36852144b69SThomas Huth qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC chip\n"); 36952144b69SThomas Huth cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); 37052144b69SThomas Huth /* XXX: TODO reset all internal peripherals */ 37152144b69SThomas Huth dbsr = env->spr[SPR_40x_DBSR]; 37252144b69SThomas Huth dbsr &= ~0x00000300; 37352144b69SThomas Huth dbsr |= 0x00000200; 37452144b69SThomas Huth env->spr[SPR_40x_DBSR] = dbsr; 37552144b69SThomas Huth } 37652144b69SThomas Huth 37752144b69SThomas Huth void ppc40x_system_reset(PowerPCCPU *cpu) 37852144b69SThomas Huth { 37952144b69SThomas Huth qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC system\n"); 38052144b69SThomas Huth qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 38152144b69SThomas Huth } 38252144b69SThomas Huth 38352144b69SThomas Huth void store_40x_dbcr0(CPUPPCState *env, uint32_t val) 38452144b69SThomas Huth { 385db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 38652144b69SThomas Huth 38752144b69SThomas Huth switch ((val >> 28) & 0x3) { 38852144b69SThomas Huth case 0x0: 38952144b69SThomas Huth /* No action */ 39052144b69SThomas Huth break; 39152144b69SThomas Huth case 0x1: 39252144b69SThomas Huth /* Core reset */ 39352144b69SThomas Huth ppc40x_core_reset(cpu); 39452144b69SThomas Huth break; 39552144b69SThomas Huth case 0x2: 39652144b69SThomas Huth /* Chip reset */ 39752144b69SThomas Huth ppc40x_chip_reset(cpu); 39852144b69SThomas Huth break; 39952144b69SThomas Huth case 0x3: 40052144b69SThomas Huth /* System reset */ 40152144b69SThomas Huth ppc40x_system_reset(cpu); 40252144b69SThomas Huth break; 40352144b69SThomas Huth } 40452144b69SThomas Huth } 40552144b69SThomas Huth 4064e290a0bSj_mayer /* PowerPC 40x internal IRQ controller */ 4074e290a0bSj_mayer static void ppc40x_set_irq(void *opaque, int pin, int level) 40824be5ae3Sj_mayer { 409a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 410a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 41124be5ae3Sj_mayer int cur_level; 41224be5ae3Sj_mayer 413d12d51d5Saliguori LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 4148ecc7913Sj_mayer env, pin, level); 41524be5ae3Sj_mayer cur_level = (env->irq_input_state >> pin) & 1; 41624be5ae3Sj_mayer /* Don't generate spurious events */ 41724be5ae3Sj_mayer if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { 418259186a7SAndreas Färber CPUState *cs = CPU(cpu); 419259186a7SAndreas Färber 42024be5ae3Sj_mayer switch (pin) { 4214e290a0bSj_mayer case PPC40x_INPUT_RESET_SYS: 4228ecc7913Sj_mayer if (level) { 423d12d51d5Saliguori LOG_IRQ("%s: reset the PowerPC system\n", 4248ecc7913Sj_mayer __func__); 425f3273ba6SAndreas Färber ppc40x_system_reset(cpu); 4268ecc7913Sj_mayer } 4278ecc7913Sj_mayer break; 4284e290a0bSj_mayer case PPC40x_INPUT_RESET_CHIP: 4298ecc7913Sj_mayer if (level) { 430d12d51d5Saliguori LOG_IRQ("%s: reset the PowerPC chip\n", __func__); 431f3273ba6SAndreas Färber ppc40x_chip_reset(cpu); 4328ecc7913Sj_mayer } 4338ecc7913Sj_mayer break; 4344e290a0bSj_mayer case PPC40x_INPUT_RESET_CORE: 43524be5ae3Sj_mayer /* XXX: TODO: update DBSR[MRR] */ 43624be5ae3Sj_mayer if (level) { 437d12d51d5Saliguori LOG_IRQ("%s: reset the PowerPC core\n", __func__); 438f3273ba6SAndreas Färber ppc40x_core_reset(cpu); 43924be5ae3Sj_mayer } 44024be5ae3Sj_mayer break; 4414e290a0bSj_mayer case PPC40x_INPUT_CINT: 44224be5ae3Sj_mayer /* Level sensitive - active high */ 443d12d51d5Saliguori LOG_IRQ("%s: set the critical IRQ state to %d\n", 4448ecc7913Sj_mayer __func__, level); 4457058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); 44624be5ae3Sj_mayer break; 4474e290a0bSj_mayer case PPC40x_INPUT_INT: 44824be5ae3Sj_mayer /* Level sensitive - active high */ 449d12d51d5Saliguori LOG_IRQ("%s: set the external IRQ state to %d\n", 450a496775fSj_mayer __func__, level); 4517058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 45224be5ae3Sj_mayer break; 4534e290a0bSj_mayer case PPC40x_INPUT_HALT: 45424be5ae3Sj_mayer /* Level sensitive - active low */ 45524be5ae3Sj_mayer if (level) { 456d12d51d5Saliguori LOG_IRQ("%s: stop the CPU\n", __func__); 457259186a7SAndreas Färber cs->halted = 1; 45824be5ae3Sj_mayer } else { 459d12d51d5Saliguori LOG_IRQ("%s: restart the CPU\n", __func__); 460259186a7SAndreas Färber cs->halted = 0; 461259186a7SAndreas Färber qemu_cpu_kick(cs); 46224be5ae3Sj_mayer } 46324be5ae3Sj_mayer break; 4644e290a0bSj_mayer case PPC40x_INPUT_DEBUG: 46524be5ae3Sj_mayer /* Level sensitive - active high */ 466d12d51d5Saliguori LOG_IRQ("%s: set the debug pin state to %d\n", 467a496775fSj_mayer __func__, level); 4687058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); 46924be5ae3Sj_mayer break; 47024be5ae3Sj_mayer default: 47124be5ae3Sj_mayer /* Unknown pin - do nothing */ 472d12d51d5Saliguori LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 47324be5ae3Sj_mayer return; 47424be5ae3Sj_mayer } 47524be5ae3Sj_mayer if (level) 47624be5ae3Sj_mayer env->irq_input_state |= 1 << pin; 47724be5ae3Sj_mayer else 47824be5ae3Sj_mayer env->irq_input_state &= ~(1 << pin); 47924be5ae3Sj_mayer } 48024be5ae3Sj_mayer } 48124be5ae3Sj_mayer 482aa5a9e24SPaolo Bonzini void ppc40x_irq_init(PowerPCCPU *cpu) 48324be5ae3Sj_mayer { 484aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 485a0961245SAndreas Färber 4864e290a0bSj_mayer env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq, 487a0961245SAndreas Färber cpu, PPC40x_INPUT_NB); 48824be5ae3Sj_mayer } 48924be5ae3Sj_mayer 4909fdc60bfSaurel32 /* PowerPC E500 internal IRQ controller */ 4919fdc60bfSaurel32 static void ppce500_set_irq(void *opaque, int pin, int level) 4929fdc60bfSaurel32 { 493a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 494a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 4959fdc60bfSaurel32 int cur_level; 4969fdc60bfSaurel32 4979fdc60bfSaurel32 LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 4989fdc60bfSaurel32 env, pin, level); 4999fdc60bfSaurel32 cur_level = (env->irq_input_state >> pin) & 1; 5009fdc60bfSaurel32 /* Don't generate spurious events */ 5019fdc60bfSaurel32 if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { 5029fdc60bfSaurel32 switch (pin) { 5039fdc60bfSaurel32 case PPCE500_INPUT_MCK: 5049fdc60bfSaurel32 if (level) { 5059fdc60bfSaurel32 LOG_IRQ("%s: reset the PowerPC system\n", 5069fdc60bfSaurel32 __func__); 507cf83f140SEric Blake qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 5089fdc60bfSaurel32 } 5099fdc60bfSaurel32 break; 5109fdc60bfSaurel32 case PPCE500_INPUT_RESET_CORE: 5119fdc60bfSaurel32 if (level) { 5129fdc60bfSaurel32 LOG_IRQ("%s: reset the PowerPC core\n", __func__); 5137058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_MCK, level); 5149fdc60bfSaurel32 } 5159fdc60bfSaurel32 break; 5169fdc60bfSaurel32 case PPCE500_INPUT_CINT: 5179fdc60bfSaurel32 /* Level sensitive - active high */ 5189fdc60bfSaurel32 LOG_IRQ("%s: set the critical IRQ state to %d\n", 5199fdc60bfSaurel32 __func__, level); 5207058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); 5219fdc60bfSaurel32 break; 5229fdc60bfSaurel32 case PPCE500_INPUT_INT: 5239fdc60bfSaurel32 /* Level sensitive - active high */ 5249fdc60bfSaurel32 LOG_IRQ("%s: set the core IRQ state to %d\n", 5259fdc60bfSaurel32 __func__, level); 5267058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 5279fdc60bfSaurel32 break; 5289fdc60bfSaurel32 case PPCE500_INPUT_DEBUG: 5299fdc60bfSaurel32 /* Level sensitive - active high */ 5309fdc60bfSaurel32 LOG_IRQ("%s: set the debug pin state to %d\n", 5319fdc60bfSaurel32 __func__, level); 5327058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); 5339fdc60bfSaurel32 break; 5349fdc60bfSaurel32 default: 5359fdc60bfSaurel32 /* Unknown pin - do nothing */ 5369fdc60bfSaurel32 LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 5379fdc60bfSaurel32 return; 5389fdc60bfSaurel32 } 5399fdc60bfSaurel32 if (level) 5409fdc60bfSaurel32 env->irq_input_state |= 1 << pin; 5419fdc60bfSaurel32 else 5429fdc60bfSaurel32 env->irq_input_state &= ~(1 << pin); 5439fdc60bfSaurel32 } 5449fdc60bfSaurel32 } 5459fdc60bfSaurel32 546aa5a9e24SPaolo Bonzini void ppce500_irq_init(PowerPCCPU *cpu) 5479fdc60bfSaurel32 { 548aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 549a0961245SAndreas Färber 5509fdc60bfSaurel32 env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq, 551a0961245SAndreas Färber cpu, PPCE500_INPUT_NB); 5529fdc60bfSaurel32 } 553e49798b1SAlexander Graf 554e49798b1SAlexander Graf /* Enable or Disable the E500 EPR capability */ 555e49798b1SAlexander Graf void ppce500_set_mpic_proxy(bool enabled) 556e49798b1SAlexander Graf { 557182735efSAndreas Färber CPUState *cs; 558e49798b1SAlexander Graf 559bdc44640SAndreas Färber CPU_FOREACH(cs) { 560182735efSAndreas Färber PowerPCCPU *cpu = POWERPC_CPU(cs); 5615b95b8b9SAlexander Graf 562182735efSAndreas Färber cpu->env.mpic_proxy = enabled; 5635b95b8b9SAlexander Graf if (kvm_enabled()) { 564182735efSAndreas Färber kvmppc_set_mpic_proxy(cpu, enabled); 5655b95b8b9SAlexander Graf } 566e49798b1SAlexander Graf } 567e49798b1SAlexander Graf } 568e49798b1SAlexander Graf 5699fddaa0cSbellard /*****************************************************************************/ 570e9df014cSj_mayer /* PowerPC time base and decrementer emulation */ 5719fddaa0cSbellard 572ddd1055bSFabien Chouteau uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) 5739fddaa0cSbellard { 5749fddaa0cSbellard /* TB time in tb periods */ 57573bcb24dSRutuja Shah return muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND) + tb_offset; 5769fddaa0cSbellard } 5779fddaa0cSbellard 578e2684c0bSAndreas Färber uint64_t cpu_ppc_load_tbl (CPUPPCState *env) 5799fddaa0cSbellard { 580c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 5819fddaa0cSbellard uint64_t tb; 5829fddaa0cSbellard 58390dc8812SScott Wood if (kvm_enabled()) { 58490dc8812SScott Wood return env->spr[SPR_TBL]; 58590dc8812SScott Wood } 58690dc8812SScott Wood 587bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); 588d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); 5899fddaa0cSbellard 590e3ea6529SAlexander Graf return tb; 5919fddaa0cSbellard } 5929fddaa0cSbellard 593e2684c0bSAndreas Färber static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env) 5949fddaa0cSbellard { 595c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 5969fddaa0cSbellard uint64_t tb; 5979fddaa0cSbellard 598bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); 599d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); 60076a66253Sj_mayer 6019fddaa0cSbellard return tb >> 32; 6029fddaa0cSbellard } 6039fddaa0cSbellard 604e2684c0bSAndreas Färber uint32_t cpu_ppc_load_tbu (CPUPPCState *env) 6058a84de23Sj_mayer { 60690dc8812SScott Wood if (kvm_enabled()) { 60790dc8812SScott Wood return env->spr[SPR_TBU]; 60890dc8812SScott Wood } 60990dc8812SScott Wood 6108a84de23Sj_mayer return _cpu_ppc_load_tbu(env); 6118a84de23Sj_mayer } 6128a84de23Sj_mayer 613c227f099SAnthony Liguori static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk, 614636aa200SBlue Swirl int64_t *tb_offsetp, uint64_t value) 6159fddaa0cSbellard { 61673bcb24dSRutuja Shah *tb_offsetp = value - 61773bcb24dSRutuja Shah muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND); 61873bcb24dSRutuja Shah 619d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n", 620aae9366aSj_mayer __func__, value, *tb_offsetp); 621a496775fSj_mayer } 6229fddaa0cSbellard 623e2684c0bSAndreas Färber void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value) 6249fddaa0cSbellard { 625c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 626a062e36cSj_mayer uint64_t tb; 6279fddaa0cSbellard 628bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); 629a062e36cSj_mayer tb &= 0xFFFFFFFF00000000ULL; 630bc72ad67SAlex Bligh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 631dbdd2506Sj_mayer &tb_env->tb_offset, tb | (uint64_t)value); 632a062e36cSj_mayer } 633a062e36cSj_mayer 634e2684c0bSAndreas Färber static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value) 635a062e36cSj_mayer { 636c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 637a062e36cSj_mayer uint64_t tb; 638a062e36cSj_mayer 639bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); 640a062e36cSj_mayer tb &= 0x00000000FFFFFFFFULL; 641bc72ad67SAlex Bligh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 642dbdd2506Sj_mayer &tb_env->tb_offset, ((uint64_t)value << 32) | tb); 643a062e36cSj_mayer } 644a062e36cSj_mayer 645e2684c0bSAndreas Färber void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value) 6468a84de23Sj_mayer { 6478a84de23Sj_mayer _cpu_ppc_store_tbu(env, value); 6488a84de23Sj_mayer } 6498a84de23Sj_mayer 650e2684c0bSAndreas Färber uint64_t cpu_ppc_load_atbl (CPUPPCState *env) 651a062e36cSj_mayer { 652c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 653a062e36cSj_mayer uint64_t tb; 654a062e36cSj_mayer 655bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); 656d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); 657a062e36cSj_mayer 658b711de95SAurelien Jarno return tb; 659a062e36cSj_mayer } 660a062e36cSj_mayer 661e2684c0bSAndreas Färber uint32_t cpu_ppc_load_atbu (CPUPPCState *env) 662a062e36cSj_mayer { 663c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 664a062e36cSj_mayer uint64_t tb; 665a062e36cSj_mayer 666bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); 667d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); 668a062e36cSj_mayer 669a062e36cSj_mayer return tb >> 32; 670a062e36cSj_mayer } 671a062e36cSj_mayer 672e2684c0bSAndreas Färber void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value) 673a062e36cSj_mayer { 674c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 675a062e36cSj_mayer uint64_t tb; 676a062e36cSj_mayer 677bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); 678a062e36cSj_mayer tb &= 0xFFFFFFFF00000000ULL; 679bc72ad67SAlex Bligh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 680dbdd2506Sj_mayer &tb_env->atb_offset, tb | (uint64_t)value); 681a062e36cSj_mayer } 682a062e36cSj_mayer 683e2684c0bSAndreas Färber void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) 684a062e36cSj_mayer { 685c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 686a062e36cSj_mayer uint64_t tb; 687a062e36cSj_mayer 688bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); 689a062e36cSj_mayer tb &= 0x00000000FFFFFFFFULL; 690bc72ad67SAlex Bligh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 691dbdd2506Sj_mayer &tb_env->atb_offset, ((uint64_t)value << 32) | tb); 692dbdd2506Sj_mayer } 693dbdd2506Sj_mayer 694e2684c0bSAndreas Färber static void cpu_ppc_tb_stop (CPUPPCState *env) 695dbdd2506Sj_mayer { 696c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 697dbdd2506Sj_mayer uint64_t tb, atb, vmclk; 698dbdd2506Sj_mayer 699dbdd2506Sj_mayer /* If the time base is already frozen, do nothing */ 700dbdd2506Sj_mayer if (tb_env->tb_freq != 0) { 701bc72ad67SAlex Bligh vmclk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 702dbdd2506Sj_mayer /* Get the time base */ 703dbdd2506Sj_mayer tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset); 704dbdd2506Sj_mayer /* Get the alternate time base */ 705dbdd2506Sj_mayer atb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->atb_offset); 706dbdd2506Sj_mayer /* Store the time base value (ie compute the current offset) */ 707dbdd2506Sj_mayer cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb); 708dbdd2506Sj_mayer /* Store the alternate time base value (compute the current offset) */ 709dbdd2506Sj_mayer cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb); 710dbdd2506Sj_mayer /* Set the time base frequency to zero */ 711dbdd2506Sj_mayer tb_env->tb_freq = 0; 712dbdd2506Sj_mayer /* Now, the time bases are frozen to tb_offset / atb_offset value */ 713dbdd2506Sj_mayer } 714dbdd2506Sj_mayer } 715dbdd2506Sj_mayer 716e2684c0bSAndreas Färber static void cpu_ppc_tb_start (CPUPPCState *env) 717dbdd2506Sj_mayer { 718c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 719dbdd2506Sj_mayer uint64_t tb, atb, vmclk; 720dbdd2506Sj_mayer 721dbdd2506Sj_mayer /* If the time base is not frozen, do nothing */ 722dbdd2506Sj_mayer if (tb_env->tb_freq == 0) { 723bc72ad67SAlex Bligh vmclk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 724dbdd2506Sj_mayer /* Get the time base from tb_offset */ 725dbdd2506Sj_mayer tb = tb_env->tb_offset; 726dbdd2506Sj_mayer /* Get the alternate time base from atb_offset */ 727dbdd2506Sj_mayer atb = tb_env->atb_offset; 728dbdd2506Sj_mayer /* Restore the tb frequency from the decrementer frequency */ 729dbdd2506Sj_mayer tb_env->tb_freq = tb_env->decr_freq; 730dbdd2506Sj_mayer /* Store the time base value */ 731dbdd2506Sj_mayer cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb); 732dbdd2506Sj_mayer /* Store the alternate time base value */ 733dbdd2506Sj_mayer cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb); 734dbdd2506Sj_mayer } 7359fddaa0cSbellard } 7369fddaa0cSbellard 737e81a982aSAlexander Graf bool ppc_decr_clear_on_delivery(CPUPPCState *env) 738e81a982aSAlexander Graf { 739e81a982aSAlexander Graf ppc_tb_t *tb_env = env->tb_env; 740e81a982aSAlexander Graf int flags = PPC_DECR_UNDERFLOW_TRIGGERED | PPC_DECR_UNDERFLOW_LEVEL; 741e81a982aSAlexander Graf return ((tb_env->flags & flags) == PPC_DECR_UNDERFLOW_TRIGGERED); 742e81a982aSAlexander Graf } 743e81a982aSAlexander Graf 744a8dafa52SSuraj Jitindar Singh static inline int64_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) 7459fddaa0cSbellard { 746c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 747a8dafa52SSuraj Jitindar Singh int64_t decr, diff; 7489fddaa0cSbellard 749bc72ad67SAlex Bligh diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 750ddd1055bSFabien Chouteau if (diff >= 0) { 75173bcb24dSRutuja Shah decr = muldiv64(diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); 752ddd1055bSFabien Chouteau } else if (tb_env->flags & PPC_TIMER_BOOKE) { 753ddd1055bSFabien Chouteau decr = 0; 754ddd1055bSFabien Chouteau } else { 75573bcb24dSRutuja Shah decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); 756ddd1055bSFabien Chouteau } 757a8dafa52SSuraj Jitindar Singh LOG_TB("%s: %016" PRIx64 "\n", __func__, decr); 75876a66253Sj_mayer 7599fddaa0cSbellard return decr; 7609fddaa0cSbellard } 7619fddaa0cSbellard 762a8dafa52SSuraj Jitindar Singh target_ulong cpu_ppc_load_decr(CPUPPCState *env) 76358a7d328Sj_mayer { 764c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 765a8dafa52SSuraj Jitindar Singh uint64_t decr; 76658a7d328Sj_mayer 76790dc8812SScott Wood if (kvm_enabled()) { 76890dc8812SScott Wood return env->spr[SPR_DECR]; 76990dc8812SScott Wood } 77090dc8812SScott Wood 771a8dafa52SSuraj Jitindar Singh decr = _cpu_ppc_load_decr(env, tb_env->decr_next); 772a8dafa52SSuraj Jitindar Singh 773a8dafa52SSuraj Jitindar Singh /* 774a8dafa52SSuraj Jitindar Singh * If large decrementer is enabled then the decrementer is signed extened 775a8dafa52SSuraj Jitindar Singh * to 64 bits, otherwise it is a 32 bit value. 776a8dafa52SSuraj Jitindar Singh */ 777a8dafa52SSuraj Jitindar Singh if (env->spr[SPR_LPCR] & LPCR_LD) { 778a8dafa52SSuraj Jitindar Singh return decr; 779a8dafa52SSuraj Jitindar Singh } 780a8dafa52SSuraj Jitindar Singh return (uint32_t) decr; 78158a7d328Sj_mayer } 78258a7d328Sj_mayer 783a8dafa52SSuraj Jitindar Singh target_ulong cpu_ppc_load_hdecr(CPUPPCState *env) 78458a7d328Sj_mayer { 785db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 786a8dafa52SSuraj Jitindar Singh PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); 787c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 788a8dafa52SSuraj Jitindar Singh uint64_t hdecr; 78958a7d328Sj_mayer 790a8dafa52SSuraj Jitindar Singh hdecr = _cpu_ppc_load_decr(env, tb_env->hdecr_next); 791a8dafa52SSuraj Jitindar Singh 792a8dafa52SSuraj Jitindar Singh /* 793a8dafa52SSuraj Jitindar Singh * If we have a large decrementer (POWER9 or later) then hdecr is sign 794a8dafa52SSuraj Jitindar Singh * extended to 64 bits, otherwise it is 32 bits. 795a8dafa52SSuraj Jitindar Singh */ 796a8dafa52SSuraj Jitindar Singh if (pcc->lrg_decr_bits > 32) { 797a8dafa52SSuraj Jitindar Singh return hdecr; 798a8dafa52SSuraj Jitindar Singh } 799a8dafa52SSuraj Jitindar Singh return (uint32_t) hdecr; 80058a7d328Sj_mayer } 80158a7d328Sj_mayer 802e2684c0bSAndreas Färber uint64_t cpu_ppc_load_purr (CPUPPCState *env) 80358a7d328Sj_mayer { 804c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 80558a7d328Sj_mayer uint64_t diff; 80658a7d328Sj_mayer 807bc72ad67SAlex Bligh diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start; 80858a7d328Sj_mayer 80973bcb24dSRutuja Shah return tb_env->purr_load + 81073bcb24dSRutuja Shah muldiv64(diff, tb_env->tb_freq, NANOSECONDS_PER_SECOND); 81158a7d328Sj_mayer } 81258a7d328Sj_mayer 8139fddaa0cSbellard /* When decrementer expires, 8149fddaa0cSbellard * all we need to do is generate or queue a CPU exception 8159fddaa0cSbellard */ 8167e0a9247SAndreas Färber static inline void cpu_ppc_decr_excp(PowerPCCPU *cpu) 8179fddaa0cSbellard { 8189fddaa0cSbellard /* Raise it */ 819d12d51d5Saliguori LOG_TB("raise decrementer exception\n"); 8207058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 1); 8219fddaa0cSbellard } 8229fddaa0cSbellard 823e81a982aSAlexander Graf static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu) 824e81a982aSAlexander Graf { 825e81a982aSAlexander Graf ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 0); 826e81a982aSAlexander Graf } 827e81a982aSAlexander Graf 8287e0a9247SAndreas Färber static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) 82958a7d328Sj_mayer { 8304b236b62SBenjamin Herrenschmidt CPUPPCState *env = &cpu->env; 8314b236b62SBenjamin Herrenschmidt 83258a7d328Sj_mayer /* Raise it */ 8334b236b62SBenjamin Herrenschmidt LOG_TB("raise hv decrementer exception\n"); 8344b236b62SBenjamin Herrenschmidt 8354b236b62SBenjamin Herrenschmidt /* The architecture specifies that we don't deliver HDEC 8364b236b62SBenjamin Herrenschmidt * interrupts in a PM state. Not only they don't cause a 8374b236b62SBenjamin Herrenschmidt * wakeup but they also get effectively discarded. 8384b236b62SBenjamin Herrenschmidt */ 8391e7fd61dSBenjamin Herrenschmidt if (!env->resume_as_sreset) { 8407058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); 84158a7d328Sj_mayer } 8424b236b62SBenjamin Herrenschmidt } 84358a7d328Sj_mayer 844e81a982aSAlexander Graf static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu) 845e81a982aSAlexander Graf { 846e81a982aSAlexander Graf ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); 847e81a982aSAlexander Graf } 848e81a982aSAlexander Graf 8497e0a9247SAndreas Färber static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, 8501246b259SStefan Weil QEMUTimer *timer, 851e81a982aSAlexander Graf void (*raise_excp)(void *), 852e81a982aSAlexander Graf void (*lower_excp)(PowerPCCPU *), 853a8dafa52SSuraj Jitindar Singh target_ulong decr, target_ulong value, 854a8dafa52SSuraj Jitindar Singh int nr_bits) 8559fddaa0cSbellard { 8567e0a9247SAndreas Färber CPUPPCState *env = &cpu->env; 857c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 8589fddaa0cSbellard uint64_t now, next; 859a8dafa52SSuraj Jitindar Singh bool negative; 8609fddaa0cSbellard 861a8dafa52SSuraj Jitindar Singh /* Truncate value to decr_width and sign extend for simplicity */ 862a8dafa52SSuraj Jitindar Singh value &= ((1ULL << nr_bits) - 1); 863a8dafa52SSuraj Jitindar Singh negative = !!(value & (1ULL << (nr_bits - 1))); 864a8dafa52SSuraj Jitindar Singh if (negative) { 865a8dafa52SSuraj Jitindar Singh value |= (0xFFFFFFFFULL << nr_bits); 866a8dafa52SSuraj Jitindar Singh } 867a8dafa52SSuraj Jitindar Singh 868a8dafa52SSuraj Jitindar Singh LOG_TB("%s: " TARGET_FMT_lx " => " TARGET_FMT_lx "\n", __func__, 869aae9366aSj_mayer decr, value); 87055f7d4b0SDavid Gibson 87155f7d4b0SDavid Gibson if (kvm_enabled()) { 87255f7d4b0SDavid Gibson /* KVM handles decrementer exceptions, we don't need our own timer */ 87355f7d4b0SDavid Gibson return; 87455f7d4b0SDavid Gibson } 87555f7d4b0SDavid Gibson 876e81a982aSAlexander Graf /* 877e81a982aSAlexander Graf * Going from 2 -> 1, 1 -> 0 or 0 -> -1 is the event to generate a DEC 878e81a982aSAlexander Graf * interrupt. 879e81a982aSAlexander Graf * 880e81a982aSAlexander Graf * If we get a really small DEC value, we can assume that by the time we 881e81a982aSAlexander Graf * handled it we should inject an interrupt already. 882e81a982aSAlexander Graf * 883e81a982aSAlexander Graf * On MSB level based DEC implementations the MSB always means the interrupt 884e81a982aSAlexander Graf * is pending, so raise it on those. 885e81a982aSAlexander Graf * 886e81a982aSAlexander Graf * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers 887e81a982aSAlexander Graf * an edge interrupt, so raise it here too. 888e81a982aSAlexander Graf */ 889e81a982aSAlexander Graf if ((value < 3) || 890a8dafa52SSuraj Jitindar Singh ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && negative) || 891a8dafa52SSuraj Jitindar Singh ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && negative 892a8dafa52SSuraj Jitindar Singh && !(decr & (1ULL << (nr_bits - 1))))) { 893e81a982aSAlexander Graf (*raise_excp)(cpu); 894e81a982aSAlexander Graf return; 895e81a982aSAlexander Graf } 896e81a982aSAlexander Graf 897e81a982aSAlexander Graf /* On MSB level based systems a 0 for the MSB stops interrupt delivery */ 898a8dafa52SSuraj Jitindar Singh if (!negative && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { 899e81a982aSAlexander Graf (*lower_excp)(cpu); 900e81a982aSAlexander Graf } 901e81a982aSAlexander Graf 902e81a982aSAlexander Graf /* Calculate the next timer event */ 903bc72ad67SAlex Bligh now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 90473bcb24dSRutuja Shah next = now + muldiv64(value, NANOSECONDS_PER_SECOND, tb_env->decr_freq); 90558a7d328Sj_mayer *nextp = next; 906e81a982aSAlexander Graf 9079fddaa0cSbellard /* Adjust timer */ 908bc72ad67SAlex Bligh timer_mod(timer, next); 909ddd1055bSFabien Chouteau } 91058a7d328Sj_mayer 911a8dafa52SSuraj Jitindar Singh static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, target_ulong decr, 912a8dafa52SSuraj Jitindar Singh target_ulong value, int nr_bits) 91358a7d328Sj_mayer { 9147e0a9247SAndreas Färber ppc_tb_t *tb_env = cpu->env.tb_env; 91558a7d328Sj_mayer 9167e0a9247SAndreas Färber __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer, 917e81a982aSAlexander Graf tb_env->decr_timer->cb, &cpu_ppc_decr_lower, decr, 918a8dafa52SSuraj Jitindar Singh value, nr_bits); 9199fddaa0cSbellard } 9209fddaa0cSbellard 921a8dafa52SSuraj Jitindar Singh void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value) 9229fddaa0cSbellard { 923db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 924a8dafa52SSuraj Jitindar Singh PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); 925a8dafa52SSuraj Jitindar Singh int nr_bits = 32; 9267e0a9247SAndreas Färber 927a8dafa52SSuraj Jitindar Singh if (env->spr[SPR_LPCR] & LPCR_LD) { 928a8dafa52SSuraj Jitindar Singh nr_bits = pcc->lrg_decr_bits; 929a8dafa52SSuraj Jitindar Singh } 930a8dafa52SSuraj Jitindar Singh 931a8dafa52SSuraj Jitindar Singh _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, nr_bits); 9329fddaa0cSbellard } 9339fddaa0cSbellard 9349fddaa0cSbellard static void cpu_ppc_decr_cb(void *opaque) 9359fddaa0cSbellard { 93650c680f0SAndreas Färber PowerPCCPU *cpu = opaque; 9377e0a9247SAndreas Färber 938e81a982aSAlexander Graf cpu_ppc_decr_excp(cpu); 9399fddaa0cSbellard } 9409fddaa0cSbellard 941a8dafa52SSuraj Jitindar Singh static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, target_ulong hdecr, 942a8dafa52SSuraj Jitindar Singh target_ulong value, int nr_bits) 94358a7d328Sj_mayer { 9447e0a9247SAndreas Färber ppc_tb_t *tb_env = cpu->env.tb_env; 94558a7d328Sj_mayer 946b172c56aSj_mayer if (tb_env->hdecr_timer != NULL) { 9477e0a9247SAndreas Färber __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer, 948e81a982aSAlexander Graf tb_env->hdecr_timer->cb, &cpu_ppc_hdecr_lower, 949a8dafa52SSuraj Jitindar Singh hdecr, value, nr_bits); 95058a7d328Sj_mayer } 951b172c56aSj_mayer } 95258a7d328Sj_mayer 953a8dafa52SSuraj Jitindar Singh void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value) 95458a7d328Sj_mayer { 955db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 956a8dafa52SSuraj Jitindar Singh PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); 9577e0a9247SAndreas Färber 958a8dafa52SSuraj Jitindar Singh _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value, 959a8dafa52SSuraj Jitindar Singh pcc->lrg_decr_bits); 96058a7d328Sj_mayer } 96158a7d328Sj_mayer 96258a7d328Sj_mayer static void cpu_ppc_hdecr_cb(void *opaque) 96358a7d328Sj_mayer { 96450c680f0SAndreas Färber PowerPCCPU *cpu = opaque; 9657e0a9247SAndreas Färber 966e81a982aSAlexander Graf cpu_ppc_hdecr_excp(cpu); 96758a7d328Sj_mayer } 96858a7d328Sj_mayer 9697e0a9247SAndreas Färber static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value) 97058a7d328Sj_mayer { 9717e0a9247SAndreas Färber ppc_tb_t *tb_env = cpu->env.tb_env; 97258a7d328Sj_mayer 97358a7d328Sj_mayer tb_env->purr_load = value; 974bc72ad67SAlex Bligh tb_env->purr_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 97558a7d328Sj_mayer } 97658a7d328Sj_mayer 9778ecc7913Sj_mayer static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) 9788ecc7913Sj_mayer { 979e2684c0bSAndreas Färber CPUPPCState *env = opaque; 980db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 981c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 9828ecc7913Sj_mayer 9838ecc7913Sj_mayer tb_env->tb_freq = freq; 984dbdd2506Sj_mayer tb_env->decr_freq = freq; 9858ecc7913Sj_mayer /* There is a bug in Linux 2.4 kernels: 9868ecc7913Sj_mayer * if a decrementer exception is pending when it enables msr_ee at startup, 9878ecc7913Sj_mayer * it's not ready to handle it... 9888ecc7913Sj_mayer */ 989a8dafa52SSuraj Jitindar Singh _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); 990a8dafa52SSuraj Jitindar Singh _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); 9917e0a9247SAndreas Färber cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); 9928ecc7913Sj_mayer } 9938ecc7913Sj_mayer 99442043e4fSLaurent Vivier static void timebase_save(PPCTimebase *tb) 99598a8b524SAlexey Kardashevskiy { 9964a7428c5SChristopher Covington uint64_t ticks = cpu_get_host_ticks(); 99798a8b524SAlexey Kardashevskiy PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); 99898a8b524SAlexey Kardashevskiy 99998a8b524SAlexey Kardashevskiy if (!first_ppc_cpu->env.tb_env) { 100098a8b524SAlexey Kardashevskiy error_report("No timebase object"); 100198a8b524SAlexey Kardashevskiy return; 100298a8b524SAlexey Kardashevskiy } 100398a8b524SAlexey Kardashevskiy 100442043e4fSLaurent Vivier /* not used anymore, we keep it for compatibility */ 100577bad151SPaolo Bonzini tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); 100698a8b524SAlexey Kardashevskiy /* 100742043e4fSLaurent Vivier * tb_offset is only expected to be changed by QEMU so 100898a8b524SAlexey Kardashevskiy * there is no need to update it from KVM here 100998a8b524SAlexey Kardashevskiy */ 101098a8b524SAlexey Kardashevskiy tb->guest_timebase = ticks + first_ppc_cpu->env.tb_env->tb_offset; 101198a8b524SAlexey Kardashevskiy } 101298a8b524SAlexey Kardashevskiy 101342043e4fSLaurent Vivier static void timebase_load(PPCTimebase *tb) 101498a8b524SAlexey Kardashevskiy { 101598a8b524SAlexey Kardashevskiy CPUState *cpu; 101698a8b524SAlexey Kardashevskiy PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); 101742043e4fSLaurent Vivier int64_t tb_off_adj, tb_off; 101898a8b524SAlexey Kardashevskiy unsigned long freq; 101998a8b524SAlexey Kardashevskiy 102098a8b524SAlexey Kardashevskiy if (!first_ppc_cpu->env.tb_env) { 102198a8b524SAlexey Kardashevskiy error_report("No timebase object"); 102242043e4fSLaurent Vivier return; 102398a8b524SAlexey Kardashevskiy } 102498a8b524SAlexey Kardashevskiy 102598a8b524SAlexey Kardashevskiy freq = first_ppc_cpu->env.tb_env->tb_freq; 102698a8b524SAlexey Kardashevskiy 102742043e4fSLaurent Vivier tb_off_adj = tb->guest_timebase - cpu_get_host_ticks(); 102898a8b524SAlexey Kardashevskiy 102998a8b524SAlexey Kardashevskiy tb_off = first_ppc_cpu->env.tb_env->tb_offset; 103098a8b524SAlexey Kardashevskiy trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off, 103198a8b524SAlexey Kardashevskiy (tb_off_adj - tb_off) / freq); 103298a8b524SAlexey Kardashevskiy 103398a8b524SAlexey Kardashevskiy /* Set new offset to all CPUs */ 103498a8b524SAlexey Kardashevskiy CPU_FOREACH(cpu) { 103598a8b524SAlexey Kardashevskiy PowerPCCPU *pcpu = POWERPC_CPU(cpu); 103698a8b524SAlexey Kardashevskiy pcpu->env.tb_env->tb_offset = tb_off_adj; 1037*9723295aSGreg Kurz kvmppc_set_reg_tb_offset(pcpu, pcpu->env.tb_env->tb_offset); 103842043e4fSLaurent Vivier } 103998a8b524SAlexey Kardashevskiy } 104098a8b524SAlexey Kardashevskiy 104142043e4fSLaurent Vivier void cpu_ppc_clock_vm_state_change(void *opaque, int running, 104242043e4fSLaurent Vivier RunState state) 104342043e4fSLaurent Vivier { 104442043e4fSLaurent Vivier PPCTimebase *tb = opaque; 104542043e4fSLaurent Vivier 104642043e4fSLaurent Vivier if (running) { 104742043e4fSLaurent Vivier timebase_load(tb); 104842043e4fSLaurent Vivier } else { 104942043e4fSLaurent Vivier timebase_save(tb); 105042043e4fSLaurent Vivier } 105142043e4fSLaurent Vivier } 105242043e4fSLaurent Vivier 105342043e4fSLaurent Vivier /* 105442043e4fSLaurent Vivier * When migrating, read the clock just before migration, 105542043e4fSLaurent Vivier * so that the guest clock counts during the events 105642043e4fSLaurent Vivier * between: 105742043e4fSLaurent Vivier * 105842043e4fSLaurent Vivier * * vm_stop() 105942043e4fSLaurent Vivier * * 106042043e4fSLaurent Vivier * * pre_save() 106142043e4fSLaurent Vivier * 106242043e4fSLaurent Vivier * This reduces clock difference on migration from 5s 106342043e4fSLaurent Vivier * to 0.1s (when max_downtime == 5s), because sending the 106442043e4fSLaurent Vivier * final pages of memory (which happens between vm_stop() 106542043e4fSLaurent Vivier * and pre_save()) takes max_downtime. 106642043e4fSLaurent Vivier */ 106744b1ff31SDr. David Alan Gilbert static int timebase_pre_save(void *opaque) 106842043e4fSLaurent Vivier { 106942043e4fSLaurent Vivier PPCTimebase *tb = opaque; 107042043e4fSLaurent Vivier 107142043e4fSLaurent Vivier timebase_save(tb); 107244b1ff31SDr. David Alan Gilbert 107344b1ff31SDr. David Alan Gilbert return 0; 107498a8b524SAlexey Kardashevskiy } 107598a8b524SAlexey Kardashevskiy 107698a8b524SAlexey Kardashevskiy const VMStateDescription vmstate_ppc_timebase = { 107798a8b524SAlexey Kardashevskiy .name = "timebase", 107898a8b524SAlexey Kardashevskiy .version_id = 1, 107998a8b524SAlexey Kardashevskiy .minimum_version_id = 1, 108098a8b524SAlexey Kardashevskiy .minimum_version_id_old = 1, 108198a8b524SAlexey Kardashevskiy .pre_save = timebase_pre_save, 108298a8b524SAlexey Kardashevskiy .fields = (VMStateField []) { 108398a8b524SAlexey Kardashevskiy VMSTATE_UINT64(guest_timebase, PPCTimebase), 108498a8b524SAlexey Kardashevskiy VMSTATE_INT64(time_of_the_day_ns, PPCTimebase), 108598a8b524SAlexey Kardashevskiy VMSTATE_END_OF_LIST() 108698a8b524SAlexey Kardashevskiy }, 108798a8b524SAlexey Kardashevskiy }; 108898a8b524SAlexey Kardashevskiy 10899fddaa0cSbellard /* Set up (once) timebase frequency (in Hz) */ 1090e2684c0bSAndreas Färber clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) 10919fddaa0cSbellard { 1092db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 1093c227f099SAnthony Liguori ppc_tb_t *tb_env; 10949fddaa0cSbellard 10957267c094SAnthony Liguori tb_env = g_malloc0(sizeof(ppc_tb_t)); 10969fddaa0cSbellard env->tb_env = tb_env; 1097ddd1055bSFabien Chouteau tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; 1098d0db7cadSGreg Kurz if (is_book3s_arch2x(env)) { 1099e81a982aSAlexander Graf /* All Book3S 64bit CPUs implement level based DEC logic */ 1100e81a982aSAlexander Graf tb_env->flags |= PPC_DECR_UNDERFLOW_LEVEL; 1101e81a982aSAlexander Graf } 11029fddaa0cSbellard /* Create new timer */ 1103bc72ad67SAlex Bligh tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu); 11044b236b62SBenjamin Herrenschmidt if (env->has_hv_mode) { 1105bc72ad67SAlex Bligh tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb, 110650c680f0SAndreas Färber cpu); 1107b172c56aSj_mayer } else { 1108b172c56aSj_mayer tb_env->hdecr_timer = NULL; 1109b172c56aSj_mayer } 11108ecc7913Sj_mayer cpu_ppc_set_tb_clk(env, freq); 11119fddaa0cSbellard 11128ecc7913Sj_mayer return &cpu_ppc_set_tb_clk; 11139fddaa0cSbellard } 11149fddaa0cSbellard 111576a66253Sj_mayer /* Specific helpers for POWER & PowerPC 601 RTC */ 1116e2684c0bSAndreas Färber void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value) 11178a84de23Sj_mayer { 11188a84de23Sj_mayer _cpu_ppc_store_tbu(env, value); 11198a84de23Sj_mayer } 112076a66253Sj_mayer 1121e2684c0bSAndreas Färber uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env) 11228a84de23Sj_mayer { 11238a84de23Sj_mayer return _cpu_ppc_load_tbu(env); 11248a84de23Sj_mayer } 112576a66253Sj_mayer 1126e2684c0bSAndreas Färber void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value) 112776a66253Sj_mayer { 112876a66253Sj_mayer cpu_ppc_store_tbl(env, value & 0x3FFFFF80); 112976a66253Sj_mayer } 113076a66253Sj_mayer 1131e2684c0bSAndreas Färber uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env) 113276a66253Sj_mayer { 113376a66253Sj_mayer return cpu_ppc_load_tbl(env) & 0x3FFFFF80; 113476a66253Sj_mayer } 113576a66253Sj_mayer 1136636aaad7Sj_mayer /*****************************************************************************/ 1137ddd1055bSFabien Chouteau /* PowerPC 40x timers */ 1138636aaad7Sj_mayer 1139636aaad7Sj_mayer /* PIT, FIT & WDT */ 1140ddd1055bSFabien Chouteau typedef struct ppc40x_timer_t ppc40x_timer_t; 1141ddd1055bSFabien Chouteau struct ppc40x_timer_t { 1142636aaad7Sj_mayer uint64_t pit_reload; /* PIT auto-reload value */ 1143636aaad7Sj_mayer uint64_t fit_next; /* Tick for next FIT interrupt */ 11441246b259SStefan Weil QEMUTimer *fit_timer; 1145636aaad7Sj_mayer uint64_t wdt_next; /* Tick for next WDT interrupt */ 11461246b259SStefan Weil QEMUTimer *wdt_timer; 1147d63cb48dSEdgar E. Iglesias 1148d63cb48dSEdgar E. Iglesias /* 405 have the PIT, 440 have a DECR. */ 1149d63cb48dSEdgar E. Iglesias unsigned int decr_excp; 1150636aaad7Sj_mayer }; 1151636aaad7Sj_mayer 1152636aaad7Sj_mayer /* Fixed interval timer */ 1153636aaad7Sj_mayer static void cpu_4xx_fit_cb (void *opaque) 115476a66253Sj_mayer { 11557058581aSAndreas Färber PowerPCCPU *cpu; 1156e2684c0bSAndreas Färber CPUPPCState *env; 1157c227f099SAnthony Liguori ppc_tb_t *tb_env; 1158ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1159636aaad7Sj_mayer uint64_t now, next; 1160636aaad7Sj_mayer 1161636aaad7Sj_mayer env = opaque; 1162db70b311SRichard Henderson cpu = env_archcpu(env); 1163636aaad7Sj_mayer tb_env = env->tb_env; 1164ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 1165bc72ad67SAlex Bligh now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 1166636aaad7Sj_mayer switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { 1167636aaad7Sj_mayer case 0: 1168636aaad7Sj_mayer next = 1 << 9; 1169636aaad7Sj_mayer break; 1170636aaad7Sj_mayer case 1: 1171636aaad7Sj_mayer next = 1 << 13; 1172636aaad7Sj_mayer break; 1173636aaad7Sj_mayer case 2: 1174636aaad7Sj_mayer next = 1 << 17; 1175636aaad7Sj_mayer break; 1176636aaad7Sj_mayer case 3: 1177636aaad7Sj_mayer next = 1 << 21; 1178636aaad7Sj_mayer break; 1179636aaad7Sj_mayer default: 1180636aaad7Sj_mayer /* Cannot occur, but makes gcc happy */ 1181636aaad7Sj_mayer return; 1182636aaad7Sj_mayer } 118373bcb24dSRutuja Shah next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->tb_freq); 1184636aaad7Sj_mayer if (next == now) 1185636aaad7Sj_mayer next++; 1186bc72ad67SAlex Bligh timer_mod(ppc40x_timer->fit_timer, next); 1187636aaad7Sj_mayer env->spr[SPR_40x_TSR] |= 1 << 26; 11887058581aSAndreas Färber if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) { 11897058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_FIT, 1); 11907058581aSAndreas Färber } 119190e189ecSBlue Swirl LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, 1192e96efcfcSj_mayer (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1), 1193636aaad7Sj_mayer env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); 1194636aaad7Sj_mayer } 1195636aaad7Sj_mayer 1196636aaad7Sj_mayer /* Programmable interval timer */ 1197e2684c0bSAndreas Färber static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) 1198636aaad7Sj_mayer { 1199ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1200636aaad7Sj_mayer uint64_t now, next; 1201636aaad7Sj_mayer 1202ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 1203ddd1055bSFabien Chouteau if (ppc40x_timer->pit_reload <= 1 || 12044b6d0a4cSj_mayer !((env->spr[SPR_40x_TCR] >> 26) & 0x1) || 12054b6d0a4cSj_mayer (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { 12064b6d0a4cSj_mayer /* Stop PIT */ 1207d12d51d5Saliguori LOG_TB("%s: stop PIT\n", __func__); 1208bc72ad67SAlex Bligh timer_del(tb_env->decr_timer); 12094b6d0a4cSj_mayer } else { 1210d12d51d5Saliguori LOG_TB("%s: start PIT %016" PRIx64 "\n", 1211ddd1055bSFabien Chouteau __func__, ppc40x_timer->pit_reload); 1212bc72ad67SAlex Bligh now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 1213ddd1055bSFabien Chouteau next = now + muldiv64(ppc40x_timer->pit_reload, 121473bcb24dSRutuja Shah NANOSECONDS_PER_SECOND, tb_env->decr_freq); 12154b6d0a4cSj_mayer if (is_excp) 12164b6d0a4cSj_mayer next += tb_env->decr_next - now; 1217636aaad7Sj_mayer if (next == now) 1218636aaad7Sj_mayer next++; 1219bc72ad67SAlex Bligh timer_mod(tb_env->decr_timer, next); 1220636aaad7Sj_mayer tb_env->decr_next = next; 1221636aaad7Sj_mayer } 12224b6d0a4cSj_mayer } 12234b6d0a4cSj_mayer 12244b6d0a4cSj_mayer static void cpu_4xx_pit_cb (void *opaque) 12254b6d0a4cSj_mayer { 12267058581aSAndreas Färber PowerPCCPU *cpu; 1227e2684c0bSAndreas Färber CPUPPCState *env; 1228c227f099SAnthony Liguori ppc_tb_t *tb_env; 1229ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 12304b6d0a4cSj_mayer 12314b6d0a4cSj_mayer env = opaque; 1232db70b311SRichard Henderson cpu = env_archcpu(env); 12334b6d0a4cSj_mayer tb_env = env->tb_env; 1234ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 1235636aaad7Sj_mayer env->spr[SPR_40x_TSR] |= 1 << 27; 12367058581aSAndreas Färber if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) { 12377058581aSAndreas Färber ppc_set_irq(cpu, ppc40x_timer->decr_excp, 1); 12387058581aSAndreas Färber } 12394b6d0a4cSj_mayer start_stop_pit(env, tb_env, 1); 124090e189ecSBlue Swirl LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " 1241e96efcfcSj_mayer "%016" PRIx64 "\n", __func__, 1242e96efcfcSj_mayer (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), 1243e96efcfcSj_mayer (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1), 1244636aaad7Sj_mayer env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], 1245ddd1055bSFabien Chouteau ppc40x_timer->pit_reload); 1246636aaad7Sj_mayer } 1247636aaad7Sj_mayer 1248636aaad7Sj_mayer /* Watchdog timer */ 1249636aaad7Sj_mayer static void cpu_4xx_wdt_cb (void *opaque) 1250636aaad7Sj_mayer { 12517058581aSAndreas Färber PowerPCCPU *cpu; 1252e2684c0bSAndreas Färber CPUPPCState *env; 1253c227f099SAnthony Liguori ppc_tb_t *tb_env; 1254ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1255636aaad7Sj_mayer uint64_t now, next; 1256636aaad7Sj_mayer 1257636aaad7Sj_mayer env = opaque; 1258db70b311SRichard Henderson cpu = env_archcpu(env); 1259636aaad7Sj_mayer tb_env = env->tb_env; 1260ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 1261bc72ad67SAlex Bligh now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 1262636aaad7Sj_mayer switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { 1263636aaad7Sj_mayer case 0: 1264636aaad7Sj_mayer next = 1 << 17; 1265636aaad7Sj_mayer break; 1266636aaad7Sj_mayer case 1: 1267636aaad7Sj_mayer next = 1 << 21; 1268636aaad7Sj_mayer break; 1269636aaad7Sj_mayer case 2: 1270636aaad7Sj_mayer next = 1 << 25; 1271636aaad7Sj_mayer break; 1272636aaad7Sj_mayer case 3: 1273636aaad7Sj_mayer next = 1 << 29; 1274636aaad7Sj_mayer break; 1275636aaad7Sj_mayer default: 1276636aaad7Sj_mayer /* Cannot occur, but makes gcc happy */ 1277636aaad7Sj_mayer return; 1278636aaad7Sj_mayer } 127973bcb24dSRutuja Shah next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->decr_freq); 1280636aaad7Sj_mayer if (next == now) 1281636aaad7Sj_mayer next++; 128290e189ecSBlue Swirl LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, 1283636aaad7Sj_mayer env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); 1284636aaad7Sj_mayer switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { 1285636aaad7Sj_mayer case 0x0: 1286636aaad7Sj_mayer case 0x1: 1287bc72ad67SAlex Bligh timer_mod(ppc40x_timer->wdt_timer, next); 1288ddd1055bSFabien Chouteau ppc40x_timer->wdt_next = next; 1289a1f7f97bSPeter Maydell env->spr[SPR_40x_TSR] |= 1U << 31; 1290636aaad7Sj_mayer break; 1291636aaad7Sj_mayer case 0x2: 1292bc72ad67SAlex Bligh timer_mod(ppc40x_timer->wdt_timer, next); 1293ddd1055bSFabien Chouteau ppc40x_timer->wdt_next = next; 1294636aaad7Sj_mayer env->spr[SPR_40x_TSR] |= 1 << 30; 12957058581aSAndreas Färber if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) { 12967058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_WDT, 1); 12977058581aSAndreas Färber } 1298636aaad7Sj_mayer break; 1299636aaad7Sj_mayer case 0x3: 1300636aaad7Sj_mayer env->spr[SPR_40x_TSR] &= ~0x30000000; 1301636aaad7Sj_mayer env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000; 1302636aaad7Sj_mayer switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) { 1303636aaad7Sj_mayer case 0x0: 1304636aaad7Sj_mayer /* No reset */ 1305636aaad7Sj_mayer break; 1306636aaad7Sj_mayer case 0x1: /* Core reset */ 1307f3273ba6SAndreas Färber ppc40x_core_reset(cpu); 13088ecc7913Sj_mayer break; 1309636aaad7Sj_mayer case 0x2: /* Chip reset */ 1310f3273ba6SAndreas Färber ppc40x_chip_reset(cpu); 13118ecc7913Sj_mayer break; 1312636aaad7Sj_mayer case 0x3: /* System reset */ 1313f3273ba6SAndreas Färber ppc40x_system_reset(cpu); 13148ecc7913Sj_mayer break; 1315636aaad7Sj_mayer } 1316636aaad7Sj_mayer } 131776a66253Sj_mayer } 131876a66253Sj_mayer 1319e2684c0bSAndreas Färber void store_40x_pit (CPUPPCState *env, target_ulong val) 132076a66253Sj_mayer { 1321c227f099SAnthony Liguori ppc_tb_t *tb_env; 1322ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1323636aaad7Sj_mayer 1324636aaad7Sj_mayer tb_env = env->tb_env; 1325ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 132690e189ecSBlue Swirl LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val); 1327ddd1055bSFabien Chouteau ppc40x_timer->pit_reload = val; 13284b6d0a4cSj_mayer start_stop_pit(env, tb_env, 0); 132976a66253Sj_mayer } 133076a66253Sj_mayer 1331e2684c0bSAndreas Färber target_ulong load_40x_pit (CPUPPCState *env) 133276a66253Sj_mayer { 1333636aaad7Sj_mayer return cpu_ppc_load_decr(env); 133476a66253Sj_mayer } 133576a66253Sj_mayer 1336ddd1055bSFabien Chouteau static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) 13374b6d0a4cSj_mayer { 1338e2684c0bSAndreas Färber CPUPPCState *env = opaque; 1339c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 13404b6d0a4cSj_mayer 1341d12d51d5Saliguori LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__, 1342aae9366aSj_mayer freq); 13434b6d0a4cSj_mayer tb_env->tb_freq = freq; 1344dbdd2506Sj_mayer tb_env->decr_freq = freq; 13454b6d0a4cSj_mayer /* XXX: we should also update all timers */ 13464b6d0a4cSj_mayer } 13474b6d0a4cSj_mayer 1348e2684c0bSAndreas Färber clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, 1349d63cb48dSEdgar E. Iglesias unsigned int decr_excp) 1350636aaad7Sj_mayer { 1351c227f099SAnthony Liguori ppc_tb_t *tb_env; 1352ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1353636aaad7Sj_mayer 13547267c094SAnthony Liguori tb_env = g_malloc0(sizeof(ppc_tb_t)); 13558ecc7913Sj_mayer env->tb_env = tb_env; 1356ddd1055bSFabien Chouteau tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; 1357ddd1055bSFabien Chouteau ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t)); 13588ecc7913Sj_mayer tb_env->tb_freq = freq; 1359dbdd2506Sj_mayer tb_env->decr_freq = freq; 1360ddd1055bSFabien Chouteau tb_env->opaque = ppc40x_timer; 1361d12d51d5Saliguori LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); 1362ddd1055bSFabien Chouteau if (ppc40x_timer != NULL) { 1363636aaad7Sj_mayer /* We use decr timer for PIT */ 1364bc72ad67SAlex Bligh tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_pit_cb, env); 1365ddd1055bSFabien Chouteau ppc40x_timer->fit_timer = 1366bc72ad67SAlex Bligh timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_fit_cb, env); 1367ddd1055bSFabien Chouteau ppc40x_timer->wdt_timer = 1368bc72ad67SAlex Bligh timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_wdt_cb, env); 1369ddd1055bSFabien Chouteau ppc40x_timer->decr_excp = decr_excp; 1370636aaad7Sj_mayer } 13718ecc7913Sj_mayer 1372ddd1055bSFabien Chouteau return &ppc_40x_set_tb_clk; 137376a66253Sj_mayer } 137476a66253Sj_mayer 13752e719ba3Sj_mayer /*****************************************************************************/ 13762e719ba3Sj_mayer /* Embedded PowerPC Device Control Registers */ 1377c227f099SAnthony Liguori typedef struct ppc_dcrn_t ppc_dcrn_t; 1378c227f099SAnthony Liguori struct ppc_dcrn_t { 13792e719ba3Sj_mayer dcr_read_cb dcr_read; 13802e719ba3Sj_mayer dcr_write_cb dcr_write; 13812e719ba3Sj_mayer void *opaque; 13822e719ba3Sj_mayer }; 13832e719ba3Sj_mayer 1384a750fc0bSj_mayer /* XXX: on 460, DCR addresses are 32 bits wide, 1385a750fc0bSj_mayer * using DCRIPR to get the 22 upper bits of the DCR address 1386a750fc0bSj_mayer */ 13872e719ba3Sj_mayer #define DCRN_NB 1024 1388c227f099SAnthony Liguori struct ppc_dcr_t { 1389c227f099SAnthony Liguori ppc_dcrn_t dcrn[DCRN_NB]; 13902e719ba3Sj_mayer int (*read_error)(int dcrn); 13912e719ba3Sj_mayer int (*write_error)(int dcrn); 13922e719ba3Sj_mayer }; 13932e719ba3Sj_mayer 139473b01960SAlexander Graf int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) 13952e719ba3Sj_mayer { 1396c227f099SAnthony Liguori ppc_dcrn_t *dcr; 13972e719ba3Sj_mayer 13982e719ba3Sj_mayer if (dcrn < 0 || dcrn >= DCRN_NB) 13992e719ba3Sj_mayer goto error; 14002e719ba3Sj_mayer dcr = &dcr_env->dcrn[dcrn]; 14012e719ba3Sj_mayer if (dcr->dcr_read == NULL) 14022e719ba3Sj_mayer goto error; 14032e719ba3Sj_mayer *valp = (*dcr->dcr_read)(dcr->opaque, dcrn); 14042e719ba3Sj_mayer 14052e719ba3Sj_mayer return 0; 14062e719ba3Sj_mayer 14072e719ba3Sj_mayer error: 14082e719ba3Sj_mayer if (dcr_env->read_error != NULL) 14092e719ba3Sj_mayer return (*dcr_env->read_error)(dcrn); 14102e719ba3Sj_mayer 14112e719ba3Sj_mayer return -1; 14122e719ba3Sj_mayer } 14132e719ba3Sj_mayer 141473b01960SAlexander Graf int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) 14152e719ba3Sj_mayer { 1416c227f099SAnthony Liguori ppc_dcrn_t *dcr; 14172e719ba3Sj_mayer 14182e719ba3Sj_mayer if (dcrn < 0 || dcrn >= DCRN_NB) 14192e719ba3Sj_mayer goto error; 14202e719ba3Sj_mayer dcr = &dcr_env->dcrn[dcrn]; 14212e719ba3Sj_mayer if (dcr->dcr_write == NULL) 14222e719ba3Sj_mayer goto error; 14232e719ba3Sj_mayer (*dcr->dcr_write)(dcr->opaque, dcrn, val); 14242e719ba3Sj_mayer 14252e719ba3Sj_mayer return 0; 14262e719ba3Sj_mayer 14272e719ba3Sj_mayer error: 14282e719ba3Sj_mayer if (dcr_env->write_error != NULL) 14292e719ba3Sj_mayer return (*dcr_env->write_error)(dcrn); 14302e719ba3Sj_mayer 14312e719ba3Sj_mayer return -1; 14322e719ba3Sj_mayer } 14332e719ba3Sj_mayer 1434e2684c0bSAndreas Färber int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque, 14352e719ba3Sj_mayer dcr_read_cb dcr_read, dcr_write_cb dcr_write) 14362e719ba3Sj_mayer { 1437c227f099SAnthony Liguori ppc_dcr_t *dcr_env; 1438c227f099SAnthony Liguori ppc_dcrn_t *dcr; 14392e719ba3Sj_mayer 14402e719ba3Sj_mayer dcr_env = env->dcr_env; 14412e719ba3Sj_mayer if (dcr_env == NULL) 14422e719ba3Sj_mayer return -1; 14432e719ba3Sj_mayer if (dcrn < 0 || dcrn >= DCRN_NB) 14442e719ba3Sj_mayer return -1; 14452e719ba3Sj_mayer dcr = &dcr_env->dcrn[dcrn]; 14462e719ba3Sj_mayer if (dcr->opaque != NULL || 14472e719ba3Sj_mayer dcr->dcr_read != NULL || 14482e719ba3Sj_mayer dcr->dcr_write != NULL) 14492e719ba3Sj_mayer return -1; 14502e719ba3Sj_mayer dcr->opaque = opaque; 14512e719ba3Sj_mayer dcr->dcr_read = dcr_read; 14522e719ba3Sj_mayer dcr->dcr_write = dcr_write; 14532e719ba3Sj_mayer 14542e719ba3Sj_mayer return 0; 14552e719ba3Sj_mayer } 14562e719ba3Sj_mayer 1457e2684c0bSAndreas Färber int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn), 14582e719ba3Sj_mayer int (*write_error)(int dcrn)) 14592e719ba3Sj_mayer { 1460c227f099SAnthony Liguori ppc_dcr_t *dcr_env; 14612e719ba3Sj_mayer 14627267c094SAnthony Liguori dcr_env = g_malloc0(sizeof(ppc_dcr_t)); 14632e719ba3Sj_mayer dcr_env->read_error = read_error; 14642e719ba3Sj_mayer dcr_env->write_error = write_error; 14652e719ba3Sj_mayer env->dcr_env = dcr_env; 14662e719ba3Sj_mayer 14672e719ba3Sj_mayer return 0; 14682e719ba3Sj_mayer } 14692e719ba3Sj_mayer 147064201201Sbellard /*****************************************************************************/ 147164201201Sbellard /* Debug port */ 1472fd0bbb12Sbellard void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) 147364201201Sbellard { 147464201201Sbellard addr &= 0xF; 147564201201Sbellard switch (addr) { 147664201201Sbellard case 0: 147764201201Sbellard printf("%c", val); 147864201201Sbellard break; 147964201201Sbellard case 1: 148064201201Sbellard printf("\n"); 148164201201Sbellard fflush(stdout); 148264201201Sbellard break; 148364201201Sbellard case 2: 1484aae9366aSj_mayer printf("Set loglevel to %04" PRIx32 "\n", val); 148524537a01SPeter Maydell qemu_set_log(val | 0x100); 148664201201Sbellard break; 148764201201Sbellard } 148864201201Sbellard } 1489051e2973SCédric Le Goater 1490051e2973SCédric Le Goater PowerPCCPU *ppc_get_vcpu_by_pir(int pir) 1491051e2973SCédric Le Goater { 1492051e2973SCédric Le Goater CPUState *cs; 1493051e2973SCédric Le Goater 1494051e2973SCédric Le Goater CPU_FOREACH(cs) { 1495051e2973SCédric Le Goater PowerPCCPU *cpu = POWERPC_CPU(cs); 1496051e2973SCédric Le Goater CPUPPCState *env = &cpu->env; 1497051e2973SCédric Le Goater 1498051e2973SCédric Le Goater if (env->spr_cb[SPR_PIR].default_value == pir) { 1499051e2973SCédric Le Goater return cpu; 1500051e2973SCédric Le Goater } 1501051e2973SCédric Le Goater } 1502051e2973SCédric Le Goater 1503051e2973SCédric Le Goater return NULL; 1504051e2973SCédric Le Goater } 1505