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