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 */ 2464552b6bSMarkus Armbruster 250d75590dSPeter Maydell #include "qemu/osdep.h" 264771d756SPaolo Bonzini #include "cpu.h" 2764552b6bSMarkus Armbruster #include "hw/irq.h" 280d09e41aSPaolo Bonzini #include "hw/ppc/ppc.h" 292b927571SAndreas Färber #include "hw/ppc/ppc_e500.h" 301de7afc9SPaolo Bonzini #include "qemu/timer.h" 310ce470cdSAlexey Kardashevskiy #include "sysemu/cpus.h" 321de7afc9SPaolo Bonzini #include "qemu/log.h" 33db725815SMarkus Armbruster #include "qemu/main-loop.h" 3498a8b524SAlexey Kardashevskiy #include "qemu/error-report.h" 359c17d615SPaolo Bonzini #include "sysemu/kvm.h" 3654d31236SMarkus Armbruster #include "sysemu/runstate.h" 37fc87e185SAlexander Graf #include "kvm_ppc.h" 38d6454270SMarkus Armbruster #include "migration/vmstate.h" 3998a8b524SAlexey Kardashevskiy #include "trace.h" 40a541f297Sbellard 41e9df014cSj_mayer //#define PPC_DEBUG_IRQ 424b6d0a4cSj_mayer //#define PPC_DEBUG_TB 43e9df014cSj_mayer 44d12d51d5Saliguori #ifdef PPC_DEBUG_IRQ 4593fcfe39Saliguori # define LOG_IRQ(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) 46d12d51d5Saliguori #else 47d12d51d5Saliguori # define LOG_IRQ(...) do { } while (0) 48d12d51d5Saliguori #endif 49d12d51d5Saliguori 50d12d51d5Saliguori 51d12d51d5Saliguori #ifdef PPC_DEBUG_TB 5293fcfe39Saliguori # define LOG_TB(...) qemu_log(__VA_ARGS__) 53d12d51d5Saliguori #else 54d12d51d5Saliguori # define LOG_TB(...) do { } while (0) 55d12d51d5Saliguori #endif 56d12d51d5Saliguori 57e2684c0bSAndreas Färber static void cpu_ppc_tb_stop (CPUPPCState *env); 58e2684c0bSAndreas Färber static void cpu_ppc_tb_start (CPUPPCState *env); 59dbdd2506Sj_mayer 607058581aSAndreas Färber void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level) 6147103572Sj_mayer { 62d8ed887bSAndreas Färber CPUState *cs = CPU(cpu); 637058581aSAndreas Färber CPUPPCState *env = &cpu->env; 648d04fb55SJan Kiszka unsigned int old_pending; 658d04fb55SJan Kiszka bool locked = false; 668d04fb55SJan Kiszka 678d04fb55SJan Kiszka /* We may already have the BQL if coming from the reset path */ 688d04fb55SJan Kiszka if (!qemu_mutex_iothread_locked()) { 698d04fb55SJan Kiszka locked = true; 708d04fb55SJan Kiszka qemu_mutex_lock_iothread(); 718d04fb55SJan Kiszka } 728d04fb55SJan Kiszka 738d04fb55SJan Kiszka old_pending = env->pending_interrupts; 74fc87e185SAlexander Graf 7547103572Sj_mayer if (level) { 7647103572Sj_mayer env->pending_interrupts |= 1 << n_IRQ; 77c3affe56SAndreas Färber cpu_interrupt(cs, CPU_INTERRUPT_HARD); 7847103572Sj_mayer } else { 7947103572Sj_mayer env->pending_interrupts &= ~(1 << n_IRQ); 80d8ed887bSAndreas Färber if (env->pending_interrupts == 0) { 81d8ed887bSAndreas Färber cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); 82d8ed887bSAndreas Färber } 8347103572Sj_mayer } 84fc87e185SAlexander Graf 85fc87e185SAlexander Graf if (old_pending != env->pending_interrupts) { 867058581aSAndreas Färber kvmppc_set_interrupt(cpu, n_IRQ, level); 87fc87e185SAlexander Graf } 88fc87e185SAlexander Graf 898d04fb55SJan Kiszka 90d12d51d5Saliguori LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32 91aae9366aSj_mayer "req %08x\n", __func__, env, n_IRQ, level, 92259186a7SAndreas Färber env->pending_interrupts, CPU(cpu)->interrupt_request); 938d04fb55SJan Kiszka 948d04fb55SJan Kiszka if (locked) { 958d04fb55SJan Kiszka qemu_mutex_unlock_iothread(); 968d04fb55SJan Kiszka } 97a496775fSj_mayer } 9847103572Sj_mayer 99e9df014cSj_mayer /* PowerPC 6xx / 7xx internal IRQ controller */ 100e9df014cSj_mayer static void ppc6xx_set_irq(void *opaque, int pin, int level) 101d537cf6cSpbrook { 102a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 103a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 104e9df014cSj_mayer int cur_level; 105d537cf6cSpbrook 106d12d51d5Saliguori LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 107a496775fSj_mayer env, pin, level); 108e9df014cSj_mayer cur_level = (env->irq_input_state >> pin) & 1; 109e9df014cSj_mayer /* Don't generate spurious events */ 11024be5ae3Sj_mayer if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { 111259186a7SAndreas Färber CPUState *cs = CPU(cpu); 112259186a7SAndreas Färber 113e9df014cSj_mayer switch (pin) { 114dbdd2506Sj_mayer case PPC6xx_INPUT_TBEN: 115dbdd2506Sj_mayer /* Level sensitive - active high */ 116d12d51d5Saliguori LOG_IRQ("%s: %s the time base\n", 117dbdd2506Sj_mayer __func__, level ? "start" : "stop"); 118dbdd2506Sj_mayer if (level) { 119dbdd2506Sj_mayer cpu_ppc_tb_start(env); 120dbdd2506Sj_mayer } else { 121dbdd2506Sj_mayer cpu_ppc_tb_stop(env); 122dbdd2506Sj_mayer } 123b2bd5b20SChen Qun break; 12424be5ae3Sj_mayer case PPC6xx_INPUT_INT: 12524be5ae3Sj_mayer /* Level sensitive - active high */ 126d12d51d5Saliguori LOG_IRQ("%s: set the external IRQ state to %d\n", 127a496775fSj_mayer __func__, level); 1287058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 129e9df014cSj_mayer break; 13024be5ae3Sj_mayer case PPC6xx_INPUT_SMI: 131e9df014cSj_mayer /* Level sensitive - active high */ 132d12d51d5Saliguori LOG_IRQ("%s: set the SMI IRQ state to %d\n", 133a496775fSj_mayer __func__, level); 1347058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_SMI, level); 135e9df014cSj_mayer break; 13624be5ae3Sj_mayer case PPC6xx_INPUT_MCP: 137e9df014cSj_mayer /* Negative edge sensitive */ 138e9df014cSj_mayer /* XXX: TODO: actual reaction may depends on HID0 status 139e9df014cSj_mayer * 603/604/740/750: check HID0[EMCP] 140e9df014cSj_mayer */ 141e9df014cSj_mayer if (cur_level == 1 && level == 0) { 142d12d51d5Saliguori LOG_IRQ("%s: raise machine check state\n", 143a496775fSj_mayer __func__); 1447058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); 145d537cf6cSpbrook } 146e9df014cSj_mayer break; 14724be5ae3Sj_mayer case PPC6xx_INPUT_CKSTP_IN: 148e9df014cSj_mayer /* Level sensitive - active low */ 149e9df014cSj_mayer /* XXX: TODO: relay the signal to CKSTP_OUT pin */ 150e63ecc6fSj_mayer /* XXX: Note that the only way to restart the CPU is to reset it */ 151e9df014cSj_mayer if (level) { 152d12d51d5Saliguori LOG_IRQ("%s: stop the CPU\n", __func__); 153259186a7SAndreas Färber cs->halted = 1; 154d537cf6cSpbrook } 15547103572Sj_mayer break; 15624be5ae3Sj_mayer case PPC6xx_INPUT_HRESET: 157e9df014cSj_mayer /* Level sensitive - active low */ 158e9df014cSj_mayer if (level) { 159d12d51d5Saliguori LOG_IRQ("%s: reset the CPU\n", __func__); 160c3affe56SAndreas Färber cpu_interrupt(cs, CPU_INTERRUPT_RESET); 161e9df014cSj_mayer } 16247103572Sj_mayer break; 16324be5ae3Sj_mayer case PPC6xx_INPUT_SRESET: 164d12d51d5Saliguori LOG_IRQ("%s: set the RESET IRQ state to %d\n", 165a496775fSj_mayer __func__, level); 1667058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); 16747103572Sj_mayer break; 168e9df014cSj_mayer default: 169e9df014cSj_mayer /* Unknown pin - do nothing */ 170d12d51d5Saliguori LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 17147103572Sj_mayer return; 17247103572Sj_mayer } 173e9df014cSj_mayer if (level) 174e9df014cSj_mayer env->irq_input_state |= 1 << pin; 175e9df014cSj_mayer else 176e9df014cSj_mayer env->irq_input_state &= ~(1 << pin); 177e9df014cSj_mayer } 178e9df014cSj_mayer } 179e9df014cSj_mayer 180aa5a9e24SPaolo Bonzini void ppc6xx_irq_init(PowerPCCPU *cpu) 181e9df014cSj_mayer { 182aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 183a0961245SAndreas Färber 184a0961245SAndreas Färber env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, cpu, 1857b62a955Sj_mayer PPC6xx_INPUT_NB); 18647103572Sj_mayer } 18747103572Sj_mayer 18800af685fSj_mayer #if defined(TARGET_PPC64) 189d0dfae6eSj_mayer /* PowerPC 970 internal IRQ controller */ 190d0dfae6eSj_mayer static void ppc970_set_irq(void *opaque, int pin, int level) 191d0dfae6eSj_mayer { 192a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 193a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 194d0dfae6eSj_mayer int cur_level; 195d0dfae6eSj_mayer 196d12d51d5Saliguori LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 197d0dfae6eSj_mayer env, pin, level); 198d0dfae6eSj_mayer cur_level = (env->irq_input_state >> pin) & 1; 199d0dfae6eSj_mayer /* Don't generate spurious events */ 200d0dfae6eSj_mayer if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { 201259186a7SAndreas Färber CPUState *cs = CPU(cpu); 202259186a7SAndreas Färber 203d0dfae6eSj_mayer switch (pin) { 204d0dfae6eSj_mayer case PPC970_INPUT_INT: 205d0dfae6eSj_mayer /* Level sensitive - active high */ 206d12d51d5Saliguori LOG_IRQ("%s: set the external IRQ state to %d\n", 207d0dfae6eSj_mayer __func__, level); 2087058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 209d0dfae6eSj_mayer break; 210d0dfae6eSj_mayer case PPC970_INPUT_THINT: 211d0dfae6eSj_mayer /* Level sensitive - active high */ 212d12d51d5Saliguori LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__, 213d0dfae6eSj_mayer level); 2147058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_THERM, level); 215d0dfae6eSj_mayer break; 216d0dfae6eSj_mayer case PPC970_INPUT_MCP: 217d0dfae6eSj_mayer /* Negative edge sensitive */ 218d0dfae6eSj_mayer /* XXX: TODO: actual reaction may depends on HID0 status 219d0dfae6eSj_mayer * 603/604/740/750: check HID0[EMCP] 220d0dfae6eSj_mayer */ 221d0dfae6eSj_mayer if (cur_level == 1 && level == 0) { 222d12d51d5Saliguori LOG_IRQ("%s: raise machine check state\n", 223d0dfae6eSj_mayer __func__); 2247058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_MCK, 1); 225d0dfae6eSj_mayer } 226d0dfae6eSj_mayer break; 227d0dfae6eSj_mayer case PPC970_INPUT_CKSTP: 228d0dfae6eSj_mayer /* Level sensitive - active low */ 229d0dfae6eSj_mayer /* XXX: TODO: relay the signal to CKSTP_OUT pin */ 230d0dfae6eSj_mayer if (level) { 231d12d51d5Saliguori LOG_IRQ("%s: stop the CPU\n", __func__); 232259186a7SAndreas Färber cs->halted = 1; 233d0dfae6eSj_mayer } else { 234d12d51d5Saliguori LOG_IRQ("%s: restart the CPU\n", __func__); 235259186a7SAndreas Färber cs->halted = 0; 236259186a7SAndreas Färber qemu_cpu_kick(cs); 237d0dfae6eSj_mayer } 238d0dfae6eSj_mayer break; 239d0dfae6eSj_mayer case PPC970_INPUT_HRESET: 240d0dfae6eSj_mayer /* Level sensitive - active low */ 241d0dfae6eSj_mayer if (level) { 242c3affe56SAndreas Färber cpu_interrupt(cs, CPU_INTERRUPT_RESET); 243d0dfae6eSj_mayer } 244d0dfae6eSj_mayer break; 245d0dfae6eSj_mayer case PPC970_INPUT_SRESET: 246d12d51d5Saliguori LOG_IRQ("%s: set the RESET IRQ state to %d\n", 247d0dfae6eSj_mayer __func__, level); 2487058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_RESET, level); 249d0dfae6eSj_mayer break; 250d0dfae6eSj_mayer case PPC970_INPUT_TBEN: 251d12d51d5Saliguori LOG_IRQ("%s: set the TBEN state to %d\n", __func__, 252d0dfae6eSj_mayer level); 253d0dfae6eSj_mayer /* XXX: TODO */ 254d0dfae6eSj_mayer break; 255d0dfae6eSj_mayer default: 256d0dfae6eSj_mayer /* Unknown pin - do nothing */ 257d12d51d5Saliguori LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 258d0dfae6eSj_mayer return; 259d0dfae6eSj_mayer } 260d0dfae6eSj_mayer if (level) 261d0dfae6eSj_mayer env->irq_input_state |= 1 << pin; 262d0dfae6eSj_mayer else 263d0dfae6eSj_mayer env->irq_input_state &= ~(1 << pin); 264d0dfae6eSj_mayer } 265d0dfae6eSj_mayer } 266d0dfae6eSj_mayer 267aa5a9e24SPaolo Bonzini void ppc970_irq_init(PowerPCCPU *cpu) 268d0dfae6eSj_mayer { 269aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 270a0961245SAndreas Färber 271a0961245SAndreas Färber env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, cpu, 2727b62a955Sj_mayer PPC970_INPUT_NB); 273d0dfae6eSj_mayer } 2749d52e907SDavid Gibson 2759d52e907SDavid Gibson /* POWER7 internal IRQ controller */ 2769d52e907SDavid Gibson static void power7_set_irq(void *opaque, int pin, int level) 2779d52e907SDavid Gibson { 278a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 2799d52e907SDavid Gibson 2809d52e907SDavid Gibson LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 281c1ad0b89SGreg Kurz &cpu->env, pin, level); 2829d52e907SDavid Gibson 2839d52e907SDavid Gibson switch (pin) { 2849d52e907SDavid Gibson case POWER7_INPUT_INT: 2859d52e907SDavid Gibson /* Level sensitive - active high */ 2869d52e907SDavid Gibson LOG_IRQ("%s: set the external IRQ state to %d\n", 2879d52e907SDavid Gibson __func__, level); 2887058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 2899d52e907SDavid Gibson break; 2909d52e907SDavid Gibson default: 2919d52e907SDavid Gibson /* Unknown pin - do nothing */ 2929d52e907SDavid Gibson LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 2939d52e907SDavid Gibson return; 2949d52e907SDavid Gibson } 2959d52e907SDavid Gibson } 2969d52e907SDavid Gibson 297aa5a9e24SPaolo Bonzini void ppcPOWER7_irq_init(PowerPCCPU *cpu) 2989d52e907SDavid Gibson { 299aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 300a0961245SAndreas Färber 301a0961245SAndreas Färber env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu, 3029d52e907SDavid Gibson POWER7_INPUT_NB); 3039d52e907SDavid Gibson } 30467afe775SBenjamin Herrenschmidt 30567afe775SBenjamin Herrenschmidt /* POWER9 internal IRQ controller */ 30667afe775SBenjamin Herrenschmidt static void power9_set_irq(void *opaque, int pin, int level) 30767afe775SBenjamin Herrenschmidt { 30867afe775SBenjamin Herrenschmidt PowerPCCPU *cpu = opaque; 30967afe775SBenjamin Herrenschmidt 31067afe775SBenjamin Herrenschmidt LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 311c1ad0b89SGreg Kurz &cpu->env, pin, level); 31267afe775SBenjamin Herrenschmidt 31367afe775SBenjamin Herrenschmidt switch (pin) { 31467afe775SBenjamin Herrenschmidt case POWER9_INPUT_INT: 31567afe775SBenjamin Herrenschmidt /* Level sensitive - active high */ 31667afe775SBenjamin Herrenschmidt LOG_IRQ("%s: set the external IRQ state to %d\n", 31767afe775SBenjamin Herrenschmidt __func__, level); 31867afe775SBenjamin Herrenschmidt ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 31967afe775SBenjamin Herrenschmidt break; 32067afe775SBenjamin Herrenschmidt case POWER9_INPUT_HINT: 32167afe775SBenjamin Herrenschmidt /* Level sensitive - active high */ 32267afe775SBenjamin Herrenschmidt LOG_IRQ("%s: set the external IRQ state to %d\n", 32367afe775SBenjamin Herrenschmidt __func__, level); 32467afe775SBenjamin Herrenschmidt ppc_set_irq(cpu, PPC_INTERRUPT_HVIRT, level); 32567afe775SBenjamin Herrenschmidt break; 32667afe775SBenjamin Herrenschmidt default: 32767afe775SBenjamin Herrenschmidt /* Unknown pin - do nothing */ 32867afe775SBenjamin Herrenschmidt LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 32967afe775SBenjamin Herrenschmidt return; 33067afe775SBenjamin Herrenschmidt } 33167afe775SBenjamin Herrenschmidt } 33267afe775SBenjamin Herrenschmidt 33367afe775SBenjamin Herrenschmidt void ppcPOWER9_irq_init(PowerPCCPU *cpu) 33467afe775SBenjamin Herrenschmidt { 33567afe775SBenjamin Herrenschmidt CPUPPCState *env = &cpu->env; 33667afe775SBenjamin Herrenschmidt 33767afe775SBenjamin Herrenschmidt env->irq_inputs = (void **)qemu_allocate_irqs(&power9_set_irq, cpu, 33867afe775SBenjamin Herrenschmidt POWER9_INPUT_NB); 33967afe775SBenjamin Herrenschmidt } 34000af685fSj_mayer #endif /* defined(TARGET_PPC64) */ 341d0dfae6eSj_mayer 34252144b69SThomas Huth void ppc40x_core_reset(PowerPCCPU *cpu) 34352144b69SThomas Huth { 34452144b69SThomas Huth CPUPPCState *env = &cpu->env; 34552144b69SThomas Huth target_ulong dbsr; 34652144b69SThomas Huth 34752144b69SThomas Huth qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC core\n"); 34852144b69SThomas Huth cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); 34952144b69SThomas Huth dbsr = env->spr[SPR_40x_DBSR]; 35052144b69SThomas Huth dbsr &= ~0x00000300; 35152144b69SThomas Huth dbsr |= 0x00000100; 35252144b69SThomas Huth env->spr[SPR_40x_DBSR] = dbsr; 35352144b69SThomas Huth } 35452144b69SThomas Huth 35552144b69SThomas Huth void ppc40x_chip_reset(PowerPCCPU *cpu) 35652144b69SThomas Huth { 35752144b69SThomas Huth CPUPPCState *env = &cpu->env; 35852144b69SThomas Huth target_ulong dbsr; 35952144b69SThomas Huth 36052144b69SThomas Huth qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC chip\n"); 36152144b69SThomas Huth cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); 36252144b69SThomas Huth /* XXX: TODO reset all internal peripherals */ 36352144b69SThomas Huth dbsr = env->spr[SPR_40x_DBSR]; 36452144b69SThomas Huth dbsr &= ~0x00000300; 36552144b69SThomas Huth dbsr |= 0x00000200; 36652144b69SThomas Huth env->spr[SPR_40x_DBSR] = dbsr; 36752144b69SThomas Huth } 36852144b69SThomas Huth 36952144b69SThomas Huth void ppc40x_system_reset(PowerPCCPU *cpu) 37052144b69SThomas Huth { 37152144b69SThomas Huth qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC system\n"); 37252144b69SThomas Huth qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 37352144b69SThomas Huth } 37452144b69SThomas Huth 37552144b69SThomas Huth void store_40x_dbcr0(CPUPPCState *env, uint32_t val) 37652144b69SThomas Huth { 377db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 37852144b69SThomas Huth 37952144b69SThomas Huth switch ((val >> 28) & 0x3) { 38052144b69SThomas Huth case 0x0: 38152144b69SThomas Huth /* No action */ 38252144b69SThomas Huth break; 38352144b69SThomas Huth case 0x1: 38452144b69SThomas Huth /* Core reset */ 38552144b69SThomas Huth ppc40x_core_reset(cpu); 38652144b69SThomas Huth break; 38752144b69SThomas Huth case 0x2: 38852144b69SThomas Huth /* Chip reset */ 38952144b69SThomas Huth ppc40x_chip_reset(cpu); 39052144b69SThomas Huth break; 39152144b69SThomas Huth case 0x3: 39252144b69SThomas Huth /* System reset */ 39352144b69SThomas Huth ppc40x_system_reset(cpu); 39452144b69SThomas Huth break; 39552144b69SThomas Huth } 39652144b69SThomas Huth } 39752144b69SThomas Huth 3984e290a0bSj_mayer /* PowerPC 40x internal IRQ controller */ 3994e290a0bSj_mayer static void ppc40x_set_irq(void *opaque, int pin, int level) 40024be5ae3Sj_mayer { 401a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 402a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 40324be5ae3Sj_mayer int cur_level; 40424be5ae3Sj_mayer 405d12d51d5Saliguori LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 4068ecc7913Sj_mayer env, pin, level); 40724be5ae3Sj_mayer cur_level = (env->irq_input_state >> pin) & 1; 40824be5ae3Sj_mayer /* Don't generate spurious events */ 40924be5ae3Sj_mayer if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { 410259186a7SAndreas Färber CPUState *cs = CPU(cpu); 411259186a7SAndreas Färber 41224be5ae3Sj_mayer switch (pin) { 4134e290a0bSj_mayer case PPC40x_INPUT_RESET_SYS: 4148ecc7913Sj_mayer if (level) { 415d12d51d5Saliguori LOG_IRQ("%s: reset the PowerPC system\n", 4168ecc7913Sj_mayer __func__); 417f3273ba6SAndreas Färber ppc40x_system_reset(cpu); 4188ecc7913Sj_mayer } 4198ecc7913Sj_mayer break; 4204e290a0bSj_mayer case PPC40x_INPUT_RESET_CHIP: 4218ecc7913Sj_mayer if (level) { 422d12d51d5Saliguori LOG_IRQ("%s: reset the PowerPC chip\n", __func__); 423f3273ba6SAndreas Färber ppc40x_chip_reset(cpu); 4248ecc7913Sj_mayer } 4258ecc7913Sj_mayer break; 4264e290a0bSj_mayer case PPC40x_INPUT_RESET_CORE: 42724be5ae3Sj_mayer /* XXX: TODO: update DBSR[MRR] */ 42824be5ae3Sj_mayer if (level) { 429d12d51d5Saliguori LOG_IRQ("%s: reset the PowerPC core\n", __func__); 430f3273ba6SAndreas Färber ppc40x_core_reset(cpu); 43124be5ae3Sj_mayer } 43224be5ae3Sj_mayer break; 4334e290a0bSj_mayer case PPC40x_INPUT_CINT: 43424be5ae3Sj_mayer /* Level sensitive - active high */ 435d12d51d5Saliguori LOG_IRQ("%s: set the critical IRQ state to %d\n", 4368ecc7913Sj_mayer __func__, level); 4377058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); 43824be5ae3Sj_mayer break; 4394e290a0bSj_mayer case PPC40x_INPUT_INT: 44024be5ae3Sj_mayer /* Level sensitive - active high */ 441d12d51d5Saliguori LOG_IRQ("%s: set the external IRQ state to %d\n", 442a496775fSj_mayer __func__, level); 4437058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 44424be5ae3Sj_mayer break; 4454e290a0bSj_mayer case PPC40x_INPUT_HALT: 44624be5ae3Sj_mayer /* Level sensitive - active low */ 44724be5ae3Sj_mayer if (level) { 448d12d51d5Saliguori LOG_IRQ("%s: stop the CPU\n", __func__); 449259186a7SAndreas Färber cs->halted = 1; 45024be5ae3Sj_mayer } else { 451d12d51d5Saliguori LOG_IRQ("%s: restart the CPU\n", __func__); 452259186a7SAndreas Färber cs->halted = 0; 453259186a7SAndreas Färber qemu_cpu_kick(cs); 45424be5ae3Sj_mayer } 45524be5ae3Sj_mayer break; 4564e290a0bSj_mayer case PPC40x_INPUT_DEBUG: 45724be5ae3Sj_mayer /* Level sensitive - active high */ 458d12d51d5Saliguori LOG_IRQ("%s: set the debug pin state to %d\n", 459a496775fSj_mayer __func__, level); 4607058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); 46124be5ae3Sj_mayer break; 46224be5ae3Sj_mayer default: 46324be5ae3Sj_mayer /* Unknown pin - do nothing */ 464d12d51d5Saliguori LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 46524be5ae3Sj_mayer return; 46624be5ae3Sj_mayer } 46724be5ae3Sj_mayer if (level) 46824be5ae3Sj_mayer env->irq_input_state |= 1 << pin; 46924be5ae3Sj_mayer else 47024be5ae3Sj_mayer env->irq_input_state &= ~(1 << pin); 47124be5ae3Sj_mayer } 47224be5ae3Sj_mayer } 47324be5ae3Sj_mayer 474aa5a9e24SPaolo Bonzini void ppc40x_irq_init(PowerPCCPU *cpu) 47524be5ae3Sj_mayer { 476aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 477a0961245SAndreas Färber 4784e290a0bSj_mayer env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq, 479a0961245SAndreas Färber cpu, PPC40x_INPUT_NB); 48024be5ae3Sj_mayer } 48124be5ae3Sj_mayer 4829fdc60bfSaurel32 /* PowerPC E500 internal IRQ controller */ 4839fdc60bfSaurel32 static void ppce500_set_irq(void *opaque, int pin, int level) 4849fdc60bfSaurel32 { 485a0961245SAndreas Färber PowerPCCPU *cpu = opaque; 486a0961245SAndreas Färber CPUPPCState *env = &cpu->env; 4879fdc60bfSaurel32 int cur_level; 4889fdc60bfSaurel32 4899fdc60bfSaurel32 LOG_IRQ("%s: env %p pin %d level %d\n", __func__, 4909fdc60bfSaurel32 env, pin, level); 4919fdc60bfSaurel32 cur_level = (env->irq_input_state >> pin) & 1; 4929fdc60bfSaurel32 /* Don't generate spurious events */ 4939fdc60bfSaurel32 if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) { 4949fdc60bfSaurel32 switch (pin) { 4959fdc60bfSaurel32 case PPCE500_INPUT_MCK: 4969fdc60bfSaurel32 if (level) { 4979fdc60bfSaurel32 LOG_IRQ("%s: reset the PowerPC system\n", 4989fdc60bfSaurel32 __func__); 499cf83f140SEric Blake qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 5009fdc60bfSaurel32 } 5019fdc60bfSaurel32 break; 5029fdc60bfSaurel32 case PPCE500_INPUT_RESET_CORE: 5039fdc60bfSaurel32 if (level) { 5049fdc60bfSaurel32 LOG_IRQ("%s: reset the PowerPC core\n", __func__); 5057058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_MCK, level); 5069fdc60bfSaurel32 } 5079fdc60bfSaurel32 break; 5089fdc60bfSaurel32 case PPCE500_INPUT_CINT: 5099fdc60bfSaurel32 /* Level sensitive - active high */ 5109fdc60bfSaurel32 LOG_IRQ("%s: set the critical IRQ state to %d\n", 5119fdc60bfSaurel32 __func__, level); 5127058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_CEXT, level); 5139fdc60bfSaurel32 break; 5149fdc60bfSaurel32 case PPCE500_INPUT_INT: 5159fdc60bfSaurel32 /* Level sensitive - active high */ 5169fdc60bfSaurel32 LOG_IRQ("%s: set the core IRQ state to %d\n", 5179fdc60bfSaurel32 __func__, level); 5187058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); 5199fdc60bfSaurel32 break; 5209fdc60bfSaurel32 case PPCE500_INPUT_DEBUG: 5219fdc60bfSaurel32 /* Level sensitive - active high */ 5229fdc60bfSaurel32 LOG_IRQ("%s: set the debug pin state to %d\n", 5239fdc60bfSaurel32 __func__, level); 5247058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_DEBUG, level); 5259fdc60bfSaurel32 break; 5269fdc60bfSaurel32 default: 5279fdc60bfSaurel32 /* Unknown pin - do nothing */ 5289fdc60bfSaurel32 LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); 5299fdc60bfSaurel32 return; 5309fdc60bfSaurel32 } 5319fdc60bfSaurel32 if (level) 5329fdc60bfSaurel32 env->irq_input_state |= 1 << pin; 5339fdc60bfSaurel32 else 5349fdc60bfSaurel32 env->irq_input_state &= ~(1 << pin); 5359fdc60bfSaurel32 } 5369fdc60bfSaurel32 } 5379fdc60bfSaurel32 538aa5a9e24SPaolo Bonzini void ppce500_irq_init(PowerPCCPU *cpu) 5399fdc60bfSaurel32 { 540aa5a9e24SPaolo Bonzini CPUPPCState *env = &cpu->env; 541a0961245SAndreas Färber 5429fdc60bfSaurel32 env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq, 543a0961245SAndreas Färber cpu, PPCE500_INPUT_NB); 5449fdc60bfSaurel32 } 545e49798b1SAlexander Graf 546e49798b1SAlexander Graf /* Enable or Disable the E500 EPR capability */ 547e49798b1SAlexander Graf void ppce500_set_mpic_proxy(bool enabled) 548e49798b1SAlexander Graf { 549182735efSAndreas Färber CPUState *cs; 550e49798b1SAlexander Graf 551bdc44640SAndreas Färber CPU_FOREACH(cs) { 552182735efSAndreas Färber PowerPCCPU *cpu = POWERPC_CPU(cs); 5535b95b8b9SAlexander Graf 554182735efSAndreas Färber cpu->env.mpic_proxy = enabled; 5555b95b8b9SAlexander Graf if (kvm_enabled()) { 556182735efSAndreas Färber kvmppc_set_mpic_proxy(cpu, enabled); 5575b95b8b9SAlexander Graf } 558e49798b1SAlexander Graf } 559e49798b1SAlexander Graf } 560e49798b1SAlexander Graf 5619fddaa0cSbellard /*****************************************************************************/ 562e9df014cSj_mayer /* PowerPC time base and decrementer emulation */ 5639fddaa0cSbellard 564ddd1055bSFabien Chouteau uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk, int64_t tb_offset) 5659fddaa0cSbellard { 5669fddaa0cSbellard /* TB time in tb periods */ 56773bcb24dSRutuja Shah return muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND) + tb_offset; 5689fddaa0cSbellard } 5699fddaa0cSbellard 570e2684c0bSAndreas Färber uint64_t cpu_ppc_load_tbl (CPUPPCState *env) 5719fddaa0cSbellard { 572c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 5739fddaa0cSbellard uint64_t tb; 5749fddaa0cSbellard 57590dc8812SScott Wood if (kvm_enabled()) { 57690dc8812SScott Wood return env->spr[SPR_TBL]; 57790dc8812SScott Wood } 57890dc8812SScott Wood 579bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); 580d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); 5819fddaa0cSbellard 582e3ea6529SAlexander Graf return tb; 5839fddaa0cSbellard } 5849fddaa0cSbellard 585e2684c0bSAndreas Färber static inline uint32_t _cpu_ppc_load_tbu(CPUPPCState *env) 5869fddaa0cSbellard { 587c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 5889fddaa0cSbellard uint64_t tb; 5899fddaa0cSbellard 590bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); 591d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); 59276a66253Sj_mayer 5939fddaa0cSbellard return tb >> 32; 5949fddaa0cSbellard } 5959fddaa0cSbellard 596e2684c0bSAndreas Färber uint32_t cpu_ppc_load_tbu (CPUPPCState *env) 5978a84de23Sj_mayer { 59890dc8812SScott Wood if (kvm_enabled()) { 59990dc8812SScott Wood return env->spr[SPR_TBU]; 60090dc8812SScott Wood } 60190dc8812SScott Wood 6028a84de23Sj_mayer return _cpu_ppc_load_tbu(env); 6038a84de23Sj_mayer } 6048a84de23Sj_mayer 605c227f099SAnthony Liguori static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk, 606636aa200SBlue Swirl int64_t *tb_offsetp, uint64_t value) 6079fddaa0cSbellard { 60873bcb24dSRutuja Shah *tb_offsetp = value - 60973bcb24dSRutuja Shah muldiv64(vmclk, tb_env->tb_freq, NANOSECONDS_PER_SECOND); 61073bcb24dSRutuja Shah 611d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n", 612aae9366aSj_mayer __func__, value, *tb_offsetp); 613a496775fSj_mayer } 6149fddaa0cSbellard 615e2684c0bSAndreas Färber void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value) 6169fddaa0cSbellard { 617c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 618a062e36cSj_mayer uint64_t tb; 6199fddaa0cSbellard 620bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); 621a062e36cSj_mayer tb &= 0xFFFFFFFF00000000ULL; 622bc72ad67SAlex Bligh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 623dbdd2506Sj_mayer &tb_env->tb_offset, tb | (uint64_t)value); 624a062e36cSj_mayer } 625a062e36cSj_mayer 626e2684c0bSAndreas Färber static inline void _cpu_ppc_store_tbu(CPUPPCState *env, uint32_t value) 627a062e36cSj_mayer { 628c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 629a062e36cSj_mayer uint64_t tb; 630a062e36cSj_mayer 631bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->tb_offset); 632a062e36cSj_mayer tb &= 0x00000000FFFFFFFFULL; 633bc72ad67SAlex Bligh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 634dbdd2506Sj_mayer &tb_env->tb_offset, ((uint64_t)value << 32) | tb); 635a062e36cSj_mayer } 636a062e36cSj_mayer 637e2684c0bSAndreas Färber void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value) 6388a84de23Sj_mayer { 6398a84de23Sj_mayer _cpu_ppc_store_tbu(env, value); 6408a84de23Sj_mayer } 6418a84de23Sj_mayer 642e2684c0bSAndreas Färber uint64_t cpu_ppc_load_atbl (CPUPPCState *env) 643a062e36cSj_mayer { 644c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 645a062e36cSj_mayer uint64_t tb; 646a062e36cSj_mayer 647bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); 648d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); 649a062e36cSj_mayer 650b711de95SAurelien Jarno return tb; 651a062e36cSj_mayer } 652a062e36cSj_mayer 653e2684c0bSAndreas Färber uint32_t cpu_ppc_load_atbu (CPUPPCState *env) 654a062e36cSj_mayer { 655c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 656a062e36cSj_mayer uint64_t tb; 657a062e36cSj_mayer 658bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); 659d12d51d5Saliguori LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb); 660a062e36cSj_mayer 661a062e36cSj_mayer return tb >> 32; 662a062e36cSj_mayer } 663a062e36cSj_mayer 664e2684c0bSAndreas Färber void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value) 665a062e36cSj_mayer { 666c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 667a062e36cSj_mayer uint64_t tb; 668a062e36cSj_mayer 669bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); 670a062e36cSj_mayer tb &= 0xFFFFFFFF00000000ULL; 671bc72ad67SAlex Bligh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 672dbdd2506Sj_mayer &tb_env->atb_offset, tb | (uint64_t)value); 673a062e36cSj_mayer } 674a062e36cSj_mayer 675e2684c0bSAndreas Färber void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) 676a062e36cSj_mayer { 677c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 678a062e36cSj_mayer uint64_t tb; 679a062e36cSj_mayer 680bc72ad67SAlex Bligh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), tb_env->atb_offset); 681a062e36cSj_mayer tb &= 0x00000000FFFFFFFFULL; 682bc72ad67SAlex Bligh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 683dbdd2506Sj_mayer &tb_env->atb_offset, ((uint64_t)value << 32) | tb); 684dbdd2506Sj_mayer } 685dbdd2506Sj_mayer 6865d62725bSSuraj Jitindar Singh uint64_t cpu_ppc_load_vtb(CPUPPCState *env) 6875d62725bSSuraj Jitindar Singh { 6885d62725bSSuraj Jitindar Singh ppc_tb_t *tb_env = env->tb_env; 6895d62725bSSuraj Jitindar Singh 6905d62725bSSuraj Jitindar Singh return cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 6915d62725bSSuraj Jitindar Singh tb_env->vtb_offset); 6925d62725bSSuraj Jitindar Singh } 6935d62725bSSuraj Jitindar Singh 6945d62725bSSuraj Jitindar Singh void cpu_ppc_store_vtb(CPUPPCState *env, uint64_t value) 6955d62725bSSuraj Jitindar Singh { 6965d62725bSSuraj Jitindar Singh ppc_tb_t *tb_env = env->tb_env; 6975d62725bSSuraj Jitindar Singh 6985d62725bSSuraj Jitindar Singh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 6995d62725bSSuraj Jitindar Singh &tb_env->vtb_offset, value); 7005d62725bSSuraj Jitindar Singh } 7015d62725bSSuraj Jitindar Singh 702f0ec31b1SSuraj Jitindar Singh void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value) 703f0ec31b1SSuraj Jitindar Singh { 704f0ec31b1SSuraj Jitindar Singh ppc_tb_t *tb_env = env->tb_env; 705f0ec31b1SSuraj Jitindar Singh uint64_t tb; 706f0ec31b1SSuraj Jitindar Singh 707f0ec31b1SSuraj Jitindar Singh tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 708f0ec31b1SSuraj Jitindar Singh tb_env->tb_offset); 709f0ec31b1SSuraj Jitindar Singh tb &= 0xFFFFFFUL; 710f0ec31b1SSuraj Jitindar Singh tb |= (value & ~0xFFFFFFUL); 711f0ec31b1SSuraj Jitindar Singh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 712f0ec31b1SSuraj Jitindar Singh &tb_env->tb_offset, tb); 713f0ec31b1SSuraj Jitindar Singh } 714f0ec31b1SSuraj Jitindar Singh 715e2684c0bSAndreas Färber static void cpu_ppc_tb_stop (CPUPPCState *env) 716dbdd2506Sj_mayer { 717c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 718dbdd2506Sj_mayer uint64_t tb, atb, vmclk; 719dbdd2506Sj_mayer 720dbdd2506Sj_mayer /* If the time base is already frozen, do nothing */ 721dbdd2506Sj_mayer if (tb_env->tb_freq != 0) { 722bc72ad67SAlex Bligh vmclk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 723dbdd2506Sj_mayer /* Get the time base */ 724dbdd2506Sj_mayer tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset); 725dbdd2506Sj_mayer /* Get the alternate time base */ 726dbdd2506Sj_mayer atb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->atb_offset); 727dbdd2506Sj_mayer /* Store the time base value (ie compute the current offset) */ 728dbdd2506Sj_mayer cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb); 729dbdd2506Sj_mayer /* Store the alternate time base value (compute the current offset) */ 730dbdd2506Sj_mayer cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb); 731dbdd2506Sj_mayer /* Set the time base frequency to zero */ 732dbdd2506Sj_mayer tb_env->tb_freq = 0; 733dbdd2506Sj_mayer /* Now, the time bases are frozen to tb_offset / atb_offset value */ 734dbdd2506Sj_mayer } 735dbdd2506Sj_mayer } 736dbdd2506Sj_mayer 737e2684c0bSAndreas Färber static void cpu_ppc_tb_start (CPUPPCState *env) 738dbdd2506Sj_mayer { 739c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 740dbdd2506Sj_mayer uint64_t tb, atb, vmclk; 741dbdd2506Sj_mayer 742dbdd2506Sj_mayer /* If the time base is not frozen, do nothing */ 743dbdd2506Sj_mayer if (tb_env->tb_freq == 0) { 744bc72ad67SAlex Bligh vmclk = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 745dbdd2506Sj_mayer /* Get the time base from tb_offset */ 746dbdd2506Sj_mayer tb = tb_env->tb_offset; 747dbdd2506Sj_mayer /* Get the alternate time base from atb_offset */ 748dbdd2506Sj_mayer atb = tb_env->atb_offset; 749dbdd2506Sj_mayer /* Restore the tb frequency from the decrementer frequency */ 750dbdd2506Sj_mayer tb_env->tb_freq = tb_env->decr_freq; 751dbdd2506Sj_mayer /* Store the time base value */ 752dbdd2506Sj_mayer cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb); 753dbdd2506Sj_mayer /* Store the alternate time base value */ 754dbdd2506Sj_mayer cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb); 755dbdd2506Sj_mayer } 7569fddaa0cSbellard } 7579fddaa0cSbellard 758e81a982aSAlexander Graf bool ppc_decr_clear_on_delivery(CPUPPCState *env) 759e81a982aSAlexander Graf { 760e81a982aSAlexander Graf ppc_tb_t *tb_env = env->tb_env; 761e81a982aSAlexander Graf int flags = PPC_DECR_UNDERFLOW_TRIGGERED | PPC_DECR_UNDERFLOW_LEVEL; 762e81a982aSAlexander Graf return ((tb_env->flags & flags) == PPC_DECR_UNDERFLOW_TRIGGERED); 763e81a982aSAlexander Graf } 764e81a982aSAlexander Graf 765a8dafa52SSuraj Jitindar Singh static inline int64_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) 7669fddaa0cSbellard { 767c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 768a8dafa52SSuraj Jitindar Singh int64_t decr, diff; 7699fddaa0cSbellard 770bc72ad67SAlex Bligh diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 771ddd1055bSFabien Chouteau if (diff >= 0) { 77273bcb24dSRutuja Shah decr = muldiv64(diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); 773ddd1055bSFabien Chouteau } else if (tb_env->flags & PPC_TIMER_BOOKE) { 774ddd1055bSFabien Chouteau decr = 0; 775ddd1055bSFabien Chouteau } else { 77673bcb24dSRutuja Shah decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); 777ddd1055bSFabien Chouteau } 778a8dafa52SSuraj Jitindar Singh LOG_TB("%s: %016" PRIx64 "\n", __func__, decr); 77976a66253Sj_mayer 7809fddaa0cSbellard return decr; 7819fddaa0cSbellard } 7829fddaa0cSbellard 783a8dafa52SSuraj Jitindar Singh target_ulong cpu_ppc_load_decr(CPUPPCState *env) 78458a7d328Sj_mayer { 785c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 786a8dafa52SSuraj Jitindar Singh uint64_t decr; 78758a7d328Sj_mayer 78890dc8812SScott Wood if (kvm_enabled()) { 78990dc8812SScott Wood return env->spr[SPR_DECR]; 79090dc8812SScott Wood } 79190dc8812SScott Wood 792a8dafa52SSuraj Jitindar Singh decr = _cpu_ppc_load_decr(env, tb_env->decr_next); 793a8dafa52SSuraj Jitindar Singh 794a8dafa52SSuraj Jitindar Singh /* 795a8dafa52SSuraj Jitindar Singh * If large decrementer is enabled then the decrementer is signed extened 796a8dafa52SSuraj Jitindar Singh * to 64 bits, otherwise it is a 32 bit value. 797a8dafa52SSuraj Jitindar Singh */ 798a8dafa52SSuraj Jitindar Singh if (env->spr[SPR_LPCR] & LPCR_LD) { 799a8dafa52SSuraj Jitindar Singh return decr; 800a8dafa52SSuraj Jitindar Singh } 801a8dafa52SSuraj Jitindar Singh return (uint32_t) decr; 80258a7d328Sj_mayer } 80358a7d328Sj_mayer 804a8dafa52SSuraj Jitindar Singh target_ulong cpu_ppc_load_hdecr(CPUPPCState *env) 80558a7d328Sj_mayer { 806db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 807a8dafa52SSuraj Jitindar Singh PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); 808c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 809a8dafa52SSuraj Jitindar Singh uint64_t hdecr; 81058a7d328Sj_mayer 811a8dafa52SSuraj Jitindar Singh hdecr = _cpu_ppc_load_decr(env, tb_env->hdecr_next); 812a8dafa52SSuraj Jitindar Singh 813a8dafa52SSuraj Jitindar Singh /* 814a8dafa52SSuraj Jitindar Singh * If we have a large decrementer (POWER9 or later) then hdecr is sign 815a8dafa52SSuraj Jitindar Singh * extended to 64 bits, otherwise it is 32 bits. 816a8dafa52SSuraj Jitindar Singh */ 817a8dafa52SSuraj Jitindar Singh if (pcc->lrg_decr_bits > 32) { 818a8dafa52SSuraj Jitindar Singh return hdecr; 819a8dafa52SSuraj Jitindar Singh } 820a8dafa52SSuraj Jitindar Singh return (uint32_t) hdecr; 82158a7d328Sj_mayer } 82258a7d328Sj_mayer 823e2684c0bSAndreas Färber uint64_t cpu_ppc_load_purr (CPUPPCState *env) 82458a7d328Sj_mayer { 825c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 82658a7d328Sj_mayer 8275cc7e69fSSuraj Jitindar Singh return cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 8285cc7e69fSSuraj Jitindar Singh tb_env->purr_offset); 82958a7d328Sj_mayer } 83058a7d328Sj_mayer 8319fddaa0cSbellard /* When decrementer expires, 8329fddaa0cSbellard * all we need to do is generate or queue a CPU exception 8339fddaa0cSbellard */ 8347e0a9247SAndreas Färber static inline void cpu_ppc_decr_excp(PowerPCCPU *cpu) 8359fddaa0cSbellard { 8369fddaa0cSbellard /* Raise it */ 837d12d51d5Saliguori LOG_TB("raise decrementer exception\n"); 8387058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 1); 8399fddaa0cSbellard } 8409fddaa0cSbellard 841e81a982aSAlexander Graf static inline void cpu_ppc_decr_lower(PowerPCCPU *cpu) 842e81a982aSAlexander Graf { 843e81a982aSAlexander Graf ppc_set_irq(cpu, PPC_INTERRUPT_DECR, 0); 844e81a982aSAlexander Graf } 845e81a982aSAlexander Graf 8467e0a9247SAndreas Färber static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) 84758a7d328Sj_mayer { 8484b236b62SBenjamin Herrenschmidt CPUPPCState *env = &cpu->env; 8494b236b62SBenjamin Herrenschmidt 85058a7d328Sj_mayer /* Raise it */ 8514b236b62SBenjamin Herrenschmidt LOG_TB("raise hv decrementer exception\n"); 8524b236b62SBenjamin Herrenschmidt 8534b236b62SBenjamin Herrenschmidt /* The architecture specifies that we don't deliver HDEC 8544b236b62SBenjamin Herrenschmidt * interrupts in a PM state. Not only they don't cause a 8554b236b62SBenjamin Herrenschmidt * wakeup but they also get effectively discarded. 8564b236b62SBenjamin Herrenschmidt */ 8571e7fd61dSBenjamin Herrenschmidt if (!env->resume_as_sreset) { 8587058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); 85958a7d328Sj_mayer } 8604b236b62SBenjamin Herrenschmidt } 86158a7d328Sj_mayer 862e81a982aSAlexander Graf static inline void cpu_ppc_hdecr_lower(PowerPCCPU *cpu) 863e81a982aSAlexander Graf { 864e81a982aSAlexander Graf ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 0); 865e81a982aSAlexander Graf } 866e81a982aSAlexander Graf 8677e0a9247SAndreas Färber static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, 8681246b259SStefan Weil QEMUTimer *timer, 869e81a982aSAlexander Graf void (*raise_excp)(void *), 870e81a982aSAlexander Graf void (*lower_excp)(PowerPCCPU *), 871a8dafa52SSuraj Jitindar Singh target_ulong decr, target_ulong value, 872a8dafa52SSuraj Jitindar Singh int nr_bits) 8739fddaa0cSbellard { 8747e0a9247SAndreas Färber CPUPPCState *env = &cpu->env; 875c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 8769fddaa0cSbellard uint64_t now, next; 877a8dafa52SSuraj Jitindar Singh bool negative; 8789fddaa0cSbellard 879a8dafa52SSuraj Jitindar Singh /* Truncate value to decr_width and sign extend for simplicity */ 880a8dafa52SSuraj Jitindar Singh value &= ((1ULL << nr_bits) - 1); 881a8dafa52SSuraj Jitindar Singh negative = !!(value & (1ULL << (nr_bits - 1))); 882a8dafa52SSuraj Jitindar Singh if (negative) { 883a8dafa52SSuraj Jitindar Singh value |= (0xFFFFFFFFULL << nr_bits); 884a8dafa52SSuraj Jitindar Singh } 885a8dafa52SSuraj Jitindar Singh 886a8dafa52SSuraj Jitindar Singh LOG_TB("%s: " TARGET_FMT_lx " => " TARGET_FMT_lx "\n", __func__, 887aae9366aSj_mayer decr, value); 88855f7d4b0SDavid Gibson 88955f7d4b0SDavid Gibson if (kvm_enabled()) { 89055f7d4b0SDavid Gibson /* KVM handles decrementer exceptions, we don't need our own timer */ 89155f7d4b0SDavid Gibson return; 89255f7d4b0SDavid Gibson } 89355f7d4b0SDavid Gibson 894e81a982aSAlexander Graf /* 895e81a982aSAlexander Graf * Going from 2 -> 1, 1 -> 0 or 0 -> -1 is the event to generate a DEC 896e81a982aSAlexander Graf * interrupt. 897e81a982aSAlexander Graf * 898e81a982aSAlexander Graf * If we get a really small DEC value, we can assume that by the time we 899e81a982aSAlexander Graf * handled it we should inject an interrupt already. 900e81a982aSAlexander Graf * 901e81a982aSAlexander Graf * On MSB level based DEC implementations the MSB always means the interrupt 902e81a982aSAlexander Graf * is pending, so raise it on those. 903e81a982aSAlexander Graf * 904e81a982aSAlexander Graf * On MSB edge based DEC implementations the MSB going from 0 -> 1 triggers 905e81a982aSAlexander Graf * an edge interrupt, so raise it here too. 906e81a982aSAlexander Graf */ 907e81a982aSAlexander Graf if ((value < 3) || 908a8dafa52SSuraj Jitindar Singh ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && negative) || 909a8dafa52SSuraj Jitindar Singh ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && negative 910a8dafa52SSuraj Jitindar Singh && !(decr & (1ULL << (nr_bits - 1))))) { 911e81a982aSAlexander Graf (*raise_excp)(cpu); 912e81a982aSAlexander Graf return; 913e81a982aSAlexander Graf } 914e81a982aSAlexander Graf 915e81a982aSAlexander Graf /* On MSB level based systems a 0 for the MSB stops interrupt delivery */ 916a8dafa52SSuraj Jitindar Singh if (!negative && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { 917e81a982aSAlexander Graf (*lower_excp)(cpu); 918e81a982aSAlexander Graf } 919e81a982aSAlexander Graf 920e81a982aSAlexander Graf /* Calculate the next timer event */ 921bc72ad67SAlex Bligh now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 92273bcb24dSRutuja Shah next = now + muldiv64(value, NANOSECONDS_PER_SECOND, tb_env->decr_freq); 92358a7d328Sj_mayer *nextp = next; 924e81a982aSAlexander Graf 9259fddaa0cSbellard /* Adjust timer */ 926bc72ad67SAlex Bligh timer_mod(timer, next); 927ddd1055bSFabien Chouteau } 92858a7d328Sj_mayer 929a8dafa52SSuraj Jitindar Singh static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, target_ulong decr, 930a8dafa52SSuraj Jitindar Singh target_ulong value, int nr_bits) 93158a7d328Sj_mayer { 9327e0a9247SAndreas Färber ppc_tb_t *tb_env = cpu->env.tb_env; 93358a7d328Sj_mayer 9347e0a9247SAndreas Färber __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer, 935e81a982aSAlexander Graf tb_env->decr_timer->cb, &cpu_ppc_decr_lower, decr, 936a8dafa52SSuraj Jitindar Singh value, nr_bits); 9379fddaa0cSbellard } 9389fddaa0cSbellard 939a8dafa52SSuraj Jitindar Singh void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value) 9409fddaa0cSbellard { 941db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 942a8dafa52SSuraj Jitindar Singh PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); 943a8dafa52SSuraj Jitindar Singh int nr_bits = 32; 9447e0a9247SAndreas Färber 945a8dafa52SSuraj Jitindar Singh if (env->spr[SPR_LPCR] & LPCR_LD) { 946a8dafa52SSuraj Jitindar Singh nr_bits = pcc->lrg_decr_bits; 947a8dafa52SSuraj Jitindar Singh } 948a8dafa52SSuraj Jitindar Singh 949a8dafa52SSuraj Jitindar Singh _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, nr_bits); 9509fddaa0cSbellard } 9519fddaa0cSbellard 9529fddaa0cSbellard static void cpu_ppc_decr_cb(void *opaque) 9539fddaa0cSbellard { 95450c680f0SAndreas Färber PowerPCCPU *cpu = opaque; 9557e0a9247SAndreas Färber 956e81a982aSAlexander Graf cpu_ppc_decr_excp(cpu); 9579fddaa0cSbellard } 9589fddaa0cSbellard 959a8dafa52SSuraj Jitindar Singh static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, target_ulong hdecr, 960a8dafa52SSuraj Jitindar Singh target_ulong value, int nr_bits) 96158a7d328Sj_mayer { 9627e0a9247SAndreas Färber ppc_tb_t *tb_env = cpu->env.tb_env; 96358a7d328Sj_mayer 964b172c56aSj_mayer if (tb_env->hdecr_timer != NULL) { 9657e0a9247SAndreas Färber __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer, 966e81a982aSAlexander Graf tb_env->hdecr_timer->cb, &cpu_ppc_hdecr_lower, 967a8dafa52SSuraj Jitindar Singh hdecr, value, nr_bits); 96858a7d328Sj_mayer } 969b172c56aSj_mayer } 97058a7d328Sj_mayer 971a8dafa52SSuraj Jitindar Singh void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value) 97258a7d328Sj_mayer { 973db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 974a8dafa52SSuraj Jitindar Singh PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); 9757e0a9247SAndreas Färber 976a8dafa52SSuraj Jitindar Singh _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value, 977a8dafa52SSuraj Jitindar Singh pcc->lrg_decr_bits); 97858a7d328Sj_mayer } 97958a7d328Sj_mayer 98058a7d328Sj_mayer static void cpu_ppc_hdecr_cb(void *opaque) 98158a7d328Sj_mayer { 98250c680f0SAndreas Färber PowerPCCPU *cpu = opaque; 9837e0a9247SAndreas Färber 984e81a982aSAlexander Graf cpu_ppc_hdecr_excp(cpu); 98558a7d328Sj_mayer } 98658a7d328Sj_mayer 9875cc7e69fSSuraj Jitindar Singh void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value) 98858a7d328Sj_mayer { 9895cc7e69fSSuraj Jitindar Singh ppc_tb_t *tb_env = env->tb_env; 99058a7d328Sj_mayer 9915cc7e69fSSuraj Jitindar Singh cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), 9925cc7e69fSSuraj Jitindar Singh &tb_env->purr_offset, value); 99358a7d328Sj_mayer } 99458a7d328Sj_mayer 9958ecc7913Sj_mayer static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) 9968ecc7913Sj_mayer { 997e2684c0bSAndreas Färber CPUPPCState *env = opaque; 998db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 999c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 10008ecc7913Sj_mayer 10018ecc7913Sj_mayer tb_env->tb_freq = freq; 1002dbdd2506Sj_mayer tb_env->decr_freq = freq; 10038ecc7913Sj_mayer /* There is a bug in Linux 2.4 kernels: 10048ecc7913Sj_mayer * if a decrementer exception is pending when it enables msr_ee at startup, 10058ecc7913Sj_mayer * it's not ready to handle it... 10068ecc7913Sj_mayer */ 1007a8dafa52SSuraj Jitindar Singh _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); 1008a8dafa52SSuraj Jitindar Singh _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); 10095cc7e69fSSuraj Jitindar Singh cpu_ppc_store_purr(env, 0x0000000000000000ULL); 10108ecc7913Sj_mayer } 10118ecc7913Sj_mayer 101242043e4fSLaurent Vivier static void timebase_save(PPCTimebase *tb) 101398a8b524SAlexey Kardashevskiy { 10144a7428c5SChristopher Covington uint64_t ticks = cpu_get_host_ticks(); 101598a8b524SAlexey Kardashevskiy PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); 101698a8b524SAlexey Kardashevskiy 101798a8b524SAlexey Kardashevskiy if (!first_ppc_cpu->env.tb_env) { 101898a8b524SAlexey Kardashevskiy error_report("No timebase object"); 101998a8b524SAlexey Kardashevskiy return; 102098a8b524SAlexey Kardashevskiy } 102198a8b524SAlexey Kardashevskiy 102242043e4fSLaurent Vivier /* not used anymore, we keep it for compatibility */ 102377bad151SPaolo Bonzini tb->time_of_the_day_ns = qemu_clock_get_ns(QEMU_CLOCK_HOST); 102498a8b524SAlexey Kardashevskiy /* 102542043e4fSLaurent Vivier * tb_offset is only expected to be changed by QEMU so 102698a8b524SAlexey Kardashevskiy * there is no need to update it from KVM here 102798a8b524SAlexey Kardashevskiy */ 102898a8b524SAlexey Kardashevskiy tb->guest_timebase = ticks + first_ppc_cpu->env.tb_env->tb_offset; 1029d14f3397SMaxiwell S. Garcia 1030*711dfb24SGreg Kurz tb->runstate_paused = 1031*711dfb24SGreg Kurz runstate_check(RUN_STATE_PAUSED) || runstate_check(RUN_STATE_SAVE_VM); 103298a8b524SAlexey Kardashevskiy } 103398a8b524SAlexey Kardashevskiy 103442043e4fSLaurent Vivier static void timebase_load(PPCTimebase *tb) 103598a8b524SAlexey Kardashevskiy { 103698a8b524SAlexey Kardashevskiy CPUState *cpu; 103798a8b524SAlexey Kardashevskiy PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); 103842043e4fSLaurent Vivier int64_t tb_off_adj, tb_off; 103998a8b524SAlexey Kardashevskiy unsigned long freq; 104098a8b524SAlexey Kardashevskiy 104198a8b524SAlexey Kardashevskiy if (!first_ppc_cpu->env.tb_env) { 104298a8b524SAlexey Kardashevskiy error_report("No timebase object"); 104342043e4fSLaurent Vivier return; 104498a8b524SAlexey Kardashevskiy } 104598a8b524SAlexey Kardashevskiy 104698a8b524SAlexey Kardashevskiy freq = first_ppc_cpu->env.tb_env->tb_freq; 104798a8b524SAlexey Kardashevskiy 104842043e4fSLaurent Vivier tb_off_adj = tb->guest_timebase - cpu_get_host_ticks(); 104998a8b524SAlexey Kardashevskiy 105098a8b524SAlexey Kardashevskiy tb_off = first_ppc_cpu->env.tb_env->tb_offset; 105198a8b524SAlexey Kardashevskiy trace_ppc_tb_adjust(tb_off, tb_off_adj, tb_off_adj - tb_off, 105298a8b524SAlexey Kardashevskiy (tb_off_adj - tb_off) / freq); 105398a8b524SAlexey Kardashevskiy 105498a8b524SAlexey Kardashevskiy /* Set new offset to all CPUs */ 105598a8b524SAlexey Kardashevskiy CPU_FOREACH(cpu) { 105698a8b524SAlexey Kardashevskiy PowerPCCPU *pcpu = POWERPC_CPU(cpu); 105798a8b524SAlexey Kardashevskiy pcpu->env.tb_env->tb_offset = tb_off_adj; 10589723295aSGreg Kurz kvmppc_set_reg_tb_offset(pcpu, pcpu->env.tb_env->tb_offset); 105942043e4fSLaurent Vivier } 106098a8b524SAlexey Kardashevskiy } 106198a8b524SAlexey Kardashevskiy 106242043e4fSLaurent Vivier void cpu_ppc_clock_vm_state_change(void *opaque, int running, 106342043e4fSLaurent Vivier RunState state) 106442043e4fSLaurent Vivier { 106542043e4fSLaurent Vivier PPCTimebase *tb = opaque; 106642043e4fSLaurent Vivier 106742043e4fSLaurent Vivier if (running) { 106842043e4fSLaurent Vivier timebase_load(tb); 106942043e4fSLaurent Vivier } else { 107042043e4fSLaurent Vivier timebase_save(tb); 107142043e4fSLaurent Vivier } 107242043e4fSLaurent Vivier } 107342043e4fSLaurent Vivier 107442043e4fSLaurent Vivier /* 1075d14f3397SMaxiwell S. Garcia * When migrating a running guest, read the clock just 1076d14f3397SMaxiwell S. Garcia * before migration, so that the guest clock counts 1077d14f3397SMaxiwell S. Garcia * during the events between: 107842043e4fSLaurent Vivier * 107942043e4fSLaurent Vivier * * vm_stop() 108042043e4fSLaurent Vivier * * 108142043e4fSLaurent Vivier * * pre_save() 108242043e4fSLaurent Vivier * 108342043e4fSLaurent Vivier * This reduces clock difference on migration from 5s 108442043e4fSLaurent Vivier * to 0.1s (when max_downtime == 5s), because sending the 108542043e4fSLaurent Vivier * final pages of memory (which happens between vm_stop() 108642043e4fSLaurent Vivier * and pre_save()) takes max_downtime. 108742043e4fSLaurent Vivier */ 108844b1ff31SDr. David Alan Gilbert static int timebase_pre_save(void *opaque) 108942043e4fSLaurent Vivier { 109042043e4fSLaurent Vivier PPCTimebase *tb = opaque; 109142043e4fSLaurent Vivier 1092*711dfb24SGreg Kurz /* guest_timebase won't be overridden in case of paused guest or savevm */ 1093d14f3397SMaxiwell S. Garcia if (!tb->runstate_paused) { 109442043e4fSLaurent Vivier timebase_save(tb); 1095d14f3397SMaxiwell S. Garcia } 109644b1ff31SDr. David Alan Gilbert 109744b1ff31SDr. David Alan Gilbert return 0; 109898a8b524SAlexey Kardashevskiy } 109998a8b524SAlexey Kardashevskiy 110098a8b524SAlexey Kardashevskiy const VMStateDescription vmstate_ppc_timebase = { 110198a8b524SAlexey Kardashevskiy .name = "timebase", 110298a8b524SAlexey Kardashevskiy .version_id = 1, 110398a8b524SAlexey Kardashevskiy .minimum_version_id = 1, 110498a8b524SAlexey Kardashevskiy .minimum_version_id_old = 1, 110598a8b524SAlexey Kardashevskiy .pre_save = timebase_pre_save, 110698a8b524SAlexey Kardashevskiy .fields = (VMStateField []) { 110798a8b524SAlexey Kardashevskiy VMSTATE_UINT64(guest_timebase, PPCTimebase), 110898a8b524SAlexey Kardashevskiy VMSTATE_INT64(time_of_the_day_ns, PPCTimebase), 110998a8b524SAlexey Kardashevskiy VMSTATE_END_OF_LIST() 111098a8b524SAlexey Kardashevskiy }, 111198a8b524SAlexey Kardashevskiy }; 111298a8b524SAlexey Kardashevskiy 11139fddaa0cSbellard /* Set up (once) timebase frequency (in Hz) */ 1114e2684c0bSAndreas Färber clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) 11159fddaa0cSbellard { 1116db70b311SRichard Henderson PowerPCCPU *cpu = env_archcpu(env); 1117c227f099SAnthony Liguori ppc_tb_t *tb_env; 11189fddaa0cSbellard 11197267c094SAnthony Liguori tb_env = g_malloc0(sizeof(ppc_tb_t)); 11209fddaa0cSbellard env->tb_env = tb_env; 1121ddd1055bSFabien Chouteau tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; 1122d0db7cadSGreg Kurz if (is_book3s_arch2x(env)) { 1123e81a982aSAlexander Graf /* All Book3S 64bit CPUs implement level based DEC logic */ 1124e81a982aSAlexander Graf tb_env->flags |= PPC_DECR_UNDERFLOW_LEVEL; 1125e81a982aSAlexander Graf } 11269fddaa0cSbellard /* Create new timer */ 1127bc72ad67SAlex Bligh tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_decr_cb, cpu); 11284b236b62SBenjamin Herrenschmidt if (env->has_hv_mode) { 1129bc72ad67SAlex Bligh tb_env->hdecr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_ppc_hdecr_cb, 113050c680f0SAndreas Färber cpu); 1131b172c56aSj_mayer } else { 1132b172c56aSj_mayer tb_env->hdecr_timer = NULL; 1133b172c56aSj_mayer } 11348ecc7913Sj_mayer cpu_ppc_set_tb_clk(env, freq); 11359fddaa0cSbellard 11368ecc7913Sj_mayer return &cpu_ppc_set_tb_clk; 11379fddaa0cSbellard } 11389fddaa0cSbellard 113976a66253Sj_mayer /* Specific helpers for POWER & PowerPC 601 RTC */ 1140e2684c0bSAndreas Färber void cpu_ppc601_store_rtcu (CPUPPCState *env, uint32_t value) 11418a84de23Sj_mayer { 11428a84de23Sj_mayer _cpu_ppc_store_tbu(env, value); 11438a84de23Sj_mayer } 114476a66253Sj_mayer 1145e2684c0bSAndreas Färber uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env) 11468a84de23Sj_mayer { 11478a84de23Sj_mayer return _cpu_ppc_load_tbu(env); 11488a84de23Sj_mayer } 114976a66253Sj_mayer 1150e2684c0bSAndreas Färber void cpu_ppc601_store_rtcl (CPUPPCState *env, uint32_t value) 115176a66253Sj_mayer { 115276a66253Sj_mayer cpu_ppc_store_tbl(env, value & 0x3FFFFF80); 115376a66253Sj_mayer } 115476a66253Sj_mayer 1155e2684c0bSAndreas Färber uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env) 115676a66253Sj_mayer { 115776a66253Sj_mayer return cpu_ppc_load_tbl(env) & 0x3FFFFF80; 115876a66253Sj_mayer } 115976a66253Sj_mayer 1160636aaad7Sj_mayer /*****************************************************************************/ 1161ddd1055bSFabien Chouteau /* PowerPC 40x timers */ 1162636aaad7Sj_mayer 1163636aaad7Sj_mayer /* PIT, FIT & WDT */ 1164ddd1055bSFabien Chouteau typedef struct ppc40x_timer_t ppc40x_timer_t; 1165ddd1055bSFabien Chouteau struct ppc40x_timer_t { 1166636aaad7Sj_mayer uint64_t pit_reload; /* PIT auto-reload value */ 1167636aaad7Sj_mayer uint64_t fit_next; /* Tick for next FIT interrupt */ 11681246b259SStefan Weil QEMUTimer *fit_timer; 1169636aaad7Sj_mayer uint64_t wdt_next; /* Tick for next WDT interrupt */ 11701246b259SStefan Weil QEMUTimer *wdt_timer; 1171d63cb48dSEdgar E. Iglesias 1172d63cb48dSEdgar E. Iglesias /* 405 have the PIT, 440 have a DECR. */ 1173d63cb48dSEdgar E. Iglesias unsigned int decr_excp; 1174636aaad7Sj_mayer }; 1175636aaad7Sj_mayer 1176636aaad7Sj_mayer /* Fixed interval timer */ 1177636aaad7Sj_mayer static void cpu_4xx_fit_cb (void *opaque) 117876a66253Sj_mayer { 11797058581aSAndreas Färber PowerPCCPU *cpu; 1180e2684c0bSAndreas Färber CPUPPCState *env; 1181c227f099SAnthony Liguori ppc_tb_t *tb_env; 1182ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1183636aaad7Sj_mayer uint64_t now, next; 1184636aaad7Sj_mayer 1185636aaad7Sj_mayer env = opaque; 1186db70b311SRichard Henderson cpu = env_archcpu(env); 1187636aaad7Sj_mayer tb_env = env->tb_env; 1188ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 1189bc72ad67SAlex Bligh now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 1190636aaad7Sj_mayer switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) { 1191636aaad7Sj_mayer case 0: 1192636aaad7Sj_mayer next = 1 << 9; 1193636aaad7Sj_mayer break; 1194636aaad7Sj_mayer case 1: 1195636aaad7Sj_mayer next = 1 << 13; 1196636aaad7Sj_mayer break; 1197636aaad7Sj_mayer case 2: 1198636aaad7Sj_mayer next = 1 << 17; 1199636aaad7Sj_mayer break; 1200636aaad7Sj_mayer case 3: 1201636aaad7Sj_mayer next = 1 << 21; 1202636aaad7Sj_mayer break; 1203636aaad7Sj_mayer default: 1204636aaad7Sj_mayer /* Cannot occur, but makes gcc happy */ 1205636aaad7Sj_mayer return; 1206636aaad7Sj_mayer } 120773bcb24dSRutuja Shah next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->tb_freq); 1208636aaad7Sj_mayer if (next == now) 1209636aaad7Sj_mayer next++; 1210bc72ad67SAlex Bligh timer_mod(ppc40x_timer->fit_timer, next); 1211636aaad7Sj_mayer env->spr[SPR_40x_TSR] |= 1 << 26; 12127058581aSAndreas Färber if ((env->spr[SPR_40x_TCR] >> 23) & 0x1) { 12137058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_FIT, 1); 12147058581aSAndreas Färber } 121590e189ecSBlue Swirl LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, 1216e96efcfcSj_mayer (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1), 1217636aaad7Sj_mayer env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); 1218636aaad7Sj_mayer } 1219636aaad7Sj_mayer 1220636aaad7Sj_mayer /* Programmable interval timer */ 1221e2684c0bSAndreas Färber static void start_stop_pit (CPUPPCState *env, ppc_tb_t *tb_env, int is_excp) 1222636aaad7Sj_mayer { 1223ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1224636aaad7Sj_mayer uint64_t now, next; 1225636aaad7Sj_mayer 1226ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 1227ddd1055bSFabien Chouteau if (ppc40x_timer->pit_reload <= 1 || 12284b6d0a4cSj_mayer !((env->spr[SPR_40x_TCR] >> 26) & 0x1) || 12294b6d0a4cSj_mayer (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) { 12304b6d0a4cSj_mayer /* Stop PIT */ 1231d12d51d5Saliguori LOG_TB("%s: stop PIT\n", __func__); 1232bc72ad67SAlex Bligh timer_del(tb_env->decr_timer); 12334b6d0a4cSj_mayer } else { 1234d12d51d5Saliguori LOG_TB("%s: start PIT %016" PRIx64 "\n", 1235ddd1055bSFabien Chouteau __func__, ppc40x_timer->pit_reload); 1236bc72ad67SAlex Bligh now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 1237ddd1055bSFabien Chouteau next = now + muldiv64(ppc40x_timer->pit_reload, 123873bcb24dSRutuja Shah NANOSECONDS_PER_SECOND, tb_env->decr_freq); 12394b6d0a4cSj_mayer if (is_excp) 12404b6d0a4cSj_mayer next += tb_env->decr_next - now; 1241636aaad7Sj_mayer if (next == now) 1242636aaad7Sj_mayer next++; 1243bc72ad67SAlex Bligh timer_mod(tb_env->decr_timer, next); 1244636aaad7Sj_mayer tb_env->decr_next = next; 1245636aaad7Sj_mayer } 12464b6d0a4cSj_mayer } 12474b6d0a4cSj_mayer 12484b6d0a4cSj_mayer static void cpu_4xx_pit_cb (void *opaque) 12494b6d0a4cSj_mayer { 12507058581aSAndreas Färber PowerPCCPU *cpu; 1251e2684c0bSAndreas Färber CPUPPCState *env; 1252c227f099SAnthony Liguori ppc_tb_t *tb_env; 1253ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 12544b6d0a4cSj_mayer 12554b6d0a4cSj_mayer env = opaque; 1256db70b311SRichard Henderson cpu = env_archcpu(env); 12574b6d0a4cSj_mayer tb_env = env->tb_env; 1258ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 1259636aaad7Sj_mayer env->spr[SPR_40x_TSR] |= 1 << 27; 12607058581aSAndreas Färber if ((env->spr[SPR_40x_TCR] >> 26) & 0x1) { 12617058581aSAndreas Färber ppc_set_irq(cpu, ppc40x_timer->decr_excp, 1); 12627058581aSAndreas Färber } 12634b6d0a4cSj_mayer start_stop_pit(env, tb_env, 1); 126490e189ecSBlue Swirl LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " " 1265e96efcfcSj_mayer "%016" PRIx64 "\n", __func__, 1266e96efcfcSj_mayer (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1), 1267e96efcfcSj_mayer (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1), 1268636aaad7Sj_mayer env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR], 1269ddd1055bSFabien Chouteau ppc40x_timer->pit_reload); 1270636aaad7Sj_mayer } 1271636aaad7Sj_mayer 1272636aaad7Sj_mayer /* Watchdog timer */ 1273636aaad7Sj_mayer static void cpu_4xx_wdt_cb (void *opaque) 1274636aaad7Sj_mayer { 12757058581aSAndreas Färber PowerPCCPU *cpu; 1276e2684c0bSAndreas Färber CPUPPCState *env; 1277c227f099SAnthony Liguori ppc_tb_t *tb_env; 1278ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1279636aaad7Sj_mayer uint64_t now, next; 1280636aaad7Sj_mayer 1281636aaad7Sj_mayer env = opaque; 1282db70b311SRichard Henderson cpu = env_archcpu(env); 1283636aaad7Sj_mayer tb_env = env->tb_env; 1284ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 1285bc72ad67SAlex Bligh now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); 1286636aaad7Sj_mayer switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) { 1287636aaad7Sj_mayer case 0: 1288636aaad7Sj_mayer next = 1 << 17; 1289636aaad7Sj_mayer break; 1290636aaad7Sj_mayer case 1: 1291636aaad7Sj_mayer next = 1 << 21; 1292636aaad7Sj_mayer break; 1293636aaad7Sj_mayer case 2: 1294636aaad7Sj_mayer next = 1 << 25; 1295636aaad7Sj_mayer break; 1296636aaad7Sj_mayer case 3: 1297636aaad7Sj_mayer next = 1 << 29; 1298636aaad7Sj_mayer break; 1299636aaad7Sj_mayer default: 1300636aaad7Sj_mayer /* Cannot occur, but makes gcc happy */ 1301636aaad7Sj_mayer return; 1302636aaad7Sj_mayer } 130373bcb24dSRutuja Shah next = now + muldiv64(next, NANOSECONDS_PER_SECOND, tb_env->decr_freq); 1304636aaad7Sj_mayer if (next == now) 1305636aaad7Sj_mayer next++; 130690e189ecSBlue Swirl LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__, 1307636aaad7Sj_mayer env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]); 1308636aaad7Sj_mayer switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) { 1309636aaad7Sj_mayer case 0x0: 1310636aaad7Sj_mayer case 0x1: 1311bc72ad67SAlex Bligh timer_mod(ppc40x_timer->wdt_timer, next); 1312ddd1055bSFabien Chouteau ppc40x_timer->wdt_next = next; 1313a1f7f97bSPeter Maydell env->spr[SPR_40x_TSR] |= 1U << 31; 1314636aaad7Sj_mayer break; 1315636aaad7Sj_mayer case 0x2: 1316bc72ad67SAlex Bligh timer_mod(ppc40x_timer->wdt_timer, next); 1317ddd1055bSFabien Chouteau ppc40x_timer->wdt_next = next; 1318636aaad7Sj_mayer env->spr[SPR_40x_TSR] |= 1 << 30; 13197058581aSAndreas Färber if ((env->spr[SPR_40x_TCR] >> 27) & 0x1) { 13207058581aSAndreas Färber ppc_set_irq(cpu, PPC_INTERRUPT_WDT, 1); 13217058581aSAndreas Färber } 1322636aaad7Sj_mayer break; 1323636aaad7Sj_mayer case 0x3: 1324636aaad7Sj_mayer env->spr[SPR_40x_TSR] &= ~0x30000000; 1325636aaad7Sj_mayer env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000; 1326636aaad7Sj_mayer switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) { 1327636aaad7Sj_mayer case 0x0: 1328636aaad7Sj_mayer /* No reset */ 1329636aaad7Sj_mayer break; 1330636aaad7Sj_mayer case 0x1: /* Core reset */ 1331f3273ba6SAndreas Färber ppc40x_core_reset(cpu); 13328ecc7913Sj_mayer break; 1333636aaad7Sj_mayer case 0x2: /* Chip reset */ 1334f3273ba6SAndreas Färber ppc40x_chip_reset(cpu); 13358ecc7913Sj_mayer break; 1336636aaad7Sj_mayer case 0x3: /* System reset */ 1337f3273ba6SAndreas Färber ppc40x_system_reset(cpu); 13388ecc7913Sj_mayer break; 1339636aaad7Sj_mayer } 1340636aaad7Sj_mayer } 134176a66253Sj_mayer } 134276a66253Sj_mayer 1343e2684c0bSAndreas Färber void store_40x_pit (CPUPPCState *env, target_ulong val) 134476a66253Sj_mayer { 1345c227f099SAnthony Liguori ppc_tb_t *tb_env; 1346ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1347636aaad7Sj_mayer 1348636aaad7Sj_mayer tb_env = env->tb_env; 1349ddd1055bSFabien Chouteau ppc40x_timer = tb_env->opaque; 135090e189ecSBlue Swirl LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val); 1351ddd1055bSFabien Chouteau ppc40x_timer->pit_reload = val; 13524b6d0a4cSj_mayer start_stop_pit(env, tb_env, 0); 135376a66253Sj_mayer } 135476a66253Sj_mayer 1355e2684c0bSAndreas Färber target_ulong load_40x_pit (CPUPPCState *env) 135676a66253Sj_mayer { 1357636aaad7Sj_mayer return cpu_ppc_load_decr(env); 135876a66253Sj_mayer } 135976a66253Sj_mayer 1360ddd1055bSFabien Chouteau static void ppc_40x_set_tb_clk (void *opaque, uint32_t freq) 13614b6d0a4cSj_mayer { 1362e2684c0bSAndreas Färber CPUPPCState *env = opaque; 1363c227f099SAnthony Liguori ppc_tb_t *tb_env = env->tb_env; 13644b6d0a4cSj_mayer 1365d12d51d5Saliguori LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__, 1366aae9366aSj_mayer freq); 13674b6d0a4cSj_mayer tb_env->tb_freq = freq; 1368dbdd2506Sj_mayer tb_env->decr_freq = freq; 13694b6d0a4cSj_mayer /* XXX: we should also update all timers */ 13704b6d0a4cSj_mayer } 13714b6d0a4cSj_mayer 1372e2684c0bSAndreas Färber clk_setup_cb ppc_40x_timers_init (CPUPPCState *env, uint32_t freq, 1373d63cb48dSEdgar E. Iglesias unsigned int decr_excp) 1374636aaad7Sj_mayer { 1375c227f099SAnthony Liguori ppc_tb_t *tb_env; 1376ddd1055bSFabien Chouteau ppc40x_timer_t *ppc40x_timer; 1377636aaad7Sj_mayer 13787267c094SAnthony Liguori tb_env = g_malloc0(sizeof(ppc_tb_t)); 13798ecc7913Sj_mayer env->tb_env = tb_env; 1380ddd1055bSFabien Chouteau tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; 1381ddd1055bSFabien Chouteau ppc40x_timer = g_malloc0(sizeof(ppc40x_timer_t)); 13828ecc7913Sj_mayer tb_env->tb_freq = freq; 1383dbdd2506Sj_mayer tb_env->decr_freq = freq; 1384ddd1055bSFabien Chouteau tb_env->opaque = ppc40x_timer; 1385d12d51d5Saliguori LOG_TB("%s freq %" PRIu32 "\n", __func__, freq); 1386ddd1055bSFabien Chouteau if (ppc40x_timer != NULL) { 1387636aaad7Sj_mayer /* We use decr timer for PIT */ 1388bc72ad67SAlex Bligh tb_env->decr_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_pit_cb, env); 1389ddd1055bSFabien Chouteau ppc40x_timer->fit_timer = 1390bc72ad67SAlex Bligh timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_fit_cb, env); 1391ddd1055bSFabien Chouteau ppc40x_timer->wdt_timer = 1392bc72ad67SAlex Bligh timer_new_ns(QEMU_CLOCK_VIRTUAL, &cpu_4xx_wdt_cb, env); 1393ddd1055bSFabien Chouteau ppc40x_timer->decr_excp = decr_excp; 1394636aaad7Sj_mayer } 13958ecc7913Sj_mayer 1396ddd1055bSFabien Chouteau return &ppc_40x_set_tb_clk; 139776a66253Sj_mayer } 139876a66253Sj_mayer 13992e719ba3Sj_mayer /*****************************************************************************/ 14002e719ba3Sj_mayer /* Embedded PowerPC Device Control Registers */ 1401c227f099SAnthony Liguori typedef struct ppc_dcrn_t ppc_dcrn_t; 1402c227f099SAnthony Liguori struct ppc_dcrn_t { 14032e719ba3Sj_mayer dcr_read_cb dcr_read; 14042e719ba3Sj_mayer dcr_write_cb dcr_write; 14052e719ba3Sj_mayer void *opaque; 14062e719ba3Sj_mayer }; 14072e719ba3Sj_mayer 1408a750fc0bSj_mayer /* XXX: on 460, DCR addresses are 32 bits wide, 1409a750fc0bSj_mayer * using DCRIPR to get the 22 upper bits of the DCR address 1410a750fc0bSj_mayer */ 14112e719ba3Sj_mayer #define DCRN_NB 1024 1412c227f099SAnthony Liguori struct ppc_dcr_t { 1413c227f099SAnthony Liguori ppc_dcrn_t dcrn[DCRN_NB]; 14142e719ba3Sj_mayer int (*read_error)(int dcrn); 14152e719ba3Sj_mayer int (*write_error)(int dcrn); 14162e719ba3Sj_mayer }; 14172e719ba3Sj_mayer 141873b01960SAlexander Graf int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp) 14192e719ba3Sj_mayer { 1420c227f099SAnthony Liguori ppc_dcrn_t *dcr; 14212e719ba3Sj_mayer 14222e719ba3Sj_mayer if (dcrn < 0 || dcrn >= DCRN_NB) 14232e719ba3Sj_mayer goto error; 14242e719ba3Sj_mayer dcr = &dcr_env->dcrn[dcrn]; 14252e719ba3Sj_mayer if (dcr->dcr_read == NULL) 14262e719ba3Sj_mayer goto error; 14272e719ba3Sj_mayer *valp = (*dcr->dcr_read)(dcr->opaque, dcrn); 14282e719ba3Sj_mayer 14292e719ba3Sj_mayer return 0; 14302e719ba3Sj_mayer 14312e719ba3Sj_mayer error: 14322e719ba3Sj_mayer if (dcr_env->read_error != NULL) 14332e719ba3Sj_mayer return (*dcr_env->read_error)(dcrn); 14342e719ba3Sj_mayer 14352e719ba3Sj_mayer return -1; 14362e719ba3Sj_mayer } 14372e719ba3Sj_mayer 143873b01960SAlexander Graf int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val) 14392e719ba3Sj_mayer { 1440c227f099SAnthony Liguori ppc_dcrn_t *dcr; 14412e719ba3Sj_mayer 14422e719ba3Sj_mayer if (dcrn < 0 || dcrn >= DCRN_NB) 14432e719ba3Sj_mayer goto error; 14442e719ba3Sj_mayer dcr = &dcr_env->dcrn[dcrn]; 14452e719ba3Sj_mayer if (dcr->dcr_write == NULL) 14462e719ba3Sj_mayer goto error; 14472e719ba3Sj_mayer (*dcr->dcr_write)(dcr->opaque, dcrn, val); 14482e719ba3Sj_mayer 14492e719ba3Sj_mayer return 0; 14502e719ba3Sj_mayer 14512e719ba3Sj_mayer error: 14522e719ba3Sj_mayer if (dcr_env->write_error != NULL) 14532e719ba3Sj_mayer return (*dcr_env->write_error)(dcrn); 14542e719ba3Sj_mayer 14552e719ba3Sj_mayer return -1; 14562e719ba3Sj_mayer } 14572e719ba3Sj_mayer 1458e2684c0bSAndreas Färber int ppc_dcr_register (CPUPPCState *env, int dcrn, void *opaque, 14592e719ba3Sj_mayer dcr_read_cb dcr_read, dcr_write_cb dcr_write) 14602e719ba3Sj_mayer { 1461c227f099SAnthony Liguori ppc_dcr_t *dcr_env; 1462c227f099SAnthony Liguori ppc_dcrn_t *dcr; 14632e719ba3Sj_mayer 14642e719ba3Sj_mayer dcr_env = env->dcr_env; 14652e719ba3Sj_mayer if (dcr_env == NULL) 14662e719ba3Sj_mayer return -1; 14672e719ba3Sj_mayer if (dcrn < 0 || dcrn >= DCRN_NB) 14682e719ba3Sj_mayer return -1; 14692e719ba3Sj_mayer dcr = &dcr_env->dcrn[dcrn]; 14702e719ba3Sj_mayer if (dcr->opaque != NULL || 14712e719ba3Sj_mayer dcr->dcr_read != NULL || 14722e719ba3Sj_mayer dcr->dcr_write != NULL) 14732e719ba3Sj_mayer return -1; 14742e719ba3Sj_mayer dcr->opaque = opaque; 14752e719ba3Sj_mayer dcr->dcr_read = dcr_read; 14762e719ba3Sj_mayer dcr->dcr_write = dcr_write; 14772e719ba3Sj_mayer 14782e719ba3Sj_mayer return 0; 14792e719ba3Sj_mayer } 14802e719ba3Sj_mayer 1481e2684c0bSAndreas Färber int ppc_dcr_init (CPUPPCState *env, int (*read_error)(int dcrn), 14822e719ba3Sj_mayer int (*write_error)(int dcrn)) 14832e719ba3Sj_mayer { 1484c227f099SAnthony Liguori ppc_dcr_t *dcr_env; 14852e719ba3Sj_mayer 14867267c094SAnthony Liguori dcr_env = g_malloc0(sizeof(ppc_dcr_t)); 14872e719ba3Sj_mayer dcr_env->read_error = read_error; 14882e719ba3Sj_mayer dcr_env->write_error = write_error; 14892e719ba3Sj_mayer env->dcr_env = dcr_env; 14902e719ba3Sj_mayer 14912e719ba3Sj_mayer return 0; 14922e719ba3Sj_mayer } 14932e719ba3Sj_mayer 149464201201Sbellard /*****************************************************************************/ 1495051e2973SCédric Le Goater 14964a89e204SCédric Le Goater int ppc_cpu_pir(PowerPCCPU *cpu) 14974a89e204SCédric Le Goater { 14984a89e204SCédric Le Goater CPUPPCState *env = &cpu->env; 14994a89e204SCédric Le Goater return env->spr_cb[SPR_PIR].default_value; 15004a89e204SCédric Le Goater } 15014a89e204SCédric Le Goater 1502051e2973SCédric Le Goater PowerPCCPU *ppc_get_vcpu_by_pir(int pir) 1503051e2973SCédric Le Goater { 1504051e2973SCédric Le Goater CPUState *cs; 1505051e2973SCédric Le Goater 1506051e2973SCédric Le Goater CPU_FOREACH(cs) { 1507051e2973SCédric Le Goater PowerPCCPU *cpu = POWERPC_CPU(cs); 1508051e2973SCédric Le Goater 15094a89e204SCédric Le Goater if (ppc_cpu_pir(cpu) == pir) { 1510051e2973SCédric Le Goater return cpu; 1511051e2973SCédric Le Goater } 1512051e2973SCédric Le Goater } 1513051e2973SCédric Le Goater 1514051e2973SCédric Le Goater return NULL; 1515051e2973SCédric Le Goater } 151640177438SGreg Kurz 151740177438SGreg Kurz void ppc_irq_reset(PowerPCCPU *cpu) 151840177438SGreg Kurz { 151940177438SGreg Kurz CPUPPCState *env = &cpu->env; 152040177438SGreg Kurz 152140177438SGreg Kurz env->irq_input_state = 0; 152240177438SGreg Kurz kvmppc_set_interrupt(cpu, PPC_INTERRUPT_EXT, 0); 152340177438SGreg Kurz } 1524