1e8bd336dSYongbok Kim /* 2e8bd336dSYongbok Kim * This file is subject to the terms and conditions of the GNU General Public 3e8bd336dSYongbok Kim * License. See the file "COPYING" in the main directory of this archive 4e8bd336dSYongbok Kim * for more details. 5e8bd336dSYongbok Kim * 6e8bd336dSYongbok Kim * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. 7e8bd336dSYongbok Kim * Authors: Sanjay Lal <sanjayl@kymasys.com> 8e8bd336dSYongbok Kim * 9e8bd336dSYongbok Kim * Copyright (C) 2016 Imagination Technologies 10e8bd336dSYongbok Kim */ 11e8bd336dSYongbok Kim 12e8bd336dSYongbok Kim #include "qemu/osdep.h" 13e8bd336dSYongbok Kim #include "qemu/log.h" 14e8bd336dSYongbok Kim #include "qapi/error.h" 15e8bd336dSYongbok Kim #include "hw/hw.h" 16e8bd336dSYongbok Kim #include "hw/sysbus.h" 17e8bd336dSYongbok Kim #include "exec/memory.h" 18e8bd336dSYongbok Kim #include "sysemu/sysemu.h" 19e8bd336dSYongbok Kim #include "sysemu/kvm.h" 20e8bd336dSYongbok Kim #include "kvm_mips.h" 21e8bd336dSYongbok Kim #include "hw/intc/mips_gic.h" 22e8bd336dSYongbok Kim 23*2e2a1b46SPaul Burton static void mips_gic_set_vp_irq(MIPSGICState *gic, int vp, int pin) 24e8bd336dSYongbok Kim { 25*2e2a1b46SPaul Burton int ored_level = 0; 26e8bd336dSYongbok Kim int i; 27e8bd336dSYongbok Kim 28e8bd336dSYongbok Kim /* ORing pending registers sharing same pin */ 29e8bd336dSYongbok Kim for (i = 0; i < gic->num_irq; i++) { 30e8bd336dSYongbok Kim if ((gic->irq_state[i].map_pin & GIC_MAP_MSK) == pin && 31e8bd336dSYongbok Kim gic->irq_state[i].map_vp == vp && 32e8bd336dSYongbok Kim gic->irq_state[i].enabled) { 33e8bd336dSYongbok Kim ored_level |= gic->irq_state[i].pending; 34e8bd336dSYongbok Kim } 35e8bd336dSYongbok Kim if (ored_level) { 36e8bd336dSYongbok Kim /* no need to iterate all interrupts */ 37e8bd336dSYongbok Kim break; 38e8bd336dSYongbok Kim } 39e8bd336dSYongbok Kim } 40e8bd336dSYongbok Kim if (((gic->vps[vp].compare_map & GIC_MAP_MSK) == pin) && 41e8bd336dSYongbok Kim (gic->vps[vp].mask & GIC_VP_MASK_CMP_MSK)) { 42e8bd336dSYongbok Kim /* ORing with local pending register (count/compare) */ 43e8bd336dSYongbok Kim ored_level |= (gic->vps[vp].pend & GIC_VP_MASK_CMP_MSK) >> 44e8bd336dSYongbok Kim GIC_VP_MASK_CMP_SHF; 45e8bd336dSYongbok Kim } 46e8bd336dSYongbok Kim if (kvm_enabled()) { 47e8bd336dSYongbok Kim kvm_mips_set_ipi_interrupt(mips_env_get_cpu(gic->vps[vp].env), 48e8bd336dSYongbok Kim pin + GIC_CPU_PIN_OFFSET, 49e8bd336dSYongbok Kim ored_level); 50e8bd336dSYongbok Kim } else { 51e8bd336dSYongbok Kim qemu_set_irq(gic->vps[vp].env->irq[pin + GIC_CPU_PIN_OFFSET], 52e8bd336dSYongbok Kim ored_level); 53e8bd336dSYongbok Kim } 54e8bd336dSYongbok Kim } 55e8bd336dSYongbok Kim 56*2e2a1b46SPaul Burton static void gic_update_pin_for_irq(MIPSGICState *gic, int n_IRQ) 57*2e2a1b46SPaul Burton { 58*2e2a1b46SPaul Burton int vp = gic->irq_state[n_IRQ].map_vp; 59*2e2a1b46SPaul Burton int pin = gic->irq_state[n_IRQ].map_pin & GIC_MAP_MSK; 60*2e2a1b46SPaul Burton 61*2e2a1b46SPaul Burton if (vp < 0 || vp >= gic->num_vps) { 62*2e2a1b46SPaul Burton return; 63*2e2a1b46SPaul Burton } 64*2e2a1b46SPaul Burton mips_gic_set_vp_irq(gic, vp, pin); 65*2e2a1b46SPaul Burton } 66*2e2a1b46SPaul Burton 67e8bd336dSYongbok Kim static void gic_set_irq(void *opaque, int n_IRQ, int level) 68e8bd336dSYongbok Kim { 69e8bd336dSYongbok Kim MIPSGICState *gic = (MIPSGICState *) opaque; 70e8bd336dSYongbok Kim 71e8bd336dSYongbok Kim gic->irq_state[n_IRQ].pending = (uint8_t) level; 72e8bd336dSYongbok Kim if (!gic->irq_state[n_IRQ].enabled) { 73e8bd336dSYongbok Kim /* GIC interrupt source disabled */ 74e8bd336dSYongbok Kim return; 75e8bd336dSYongbok Kim } 76*2e2a1b46SPaul Burton gic_update_pin_for_irq(gic, n_IRQ); 77e8bd336dSYongbok Kim } 78e8bd336dSYongbok Kim 79e8bd336dSYongbok Kim #define OFFSET_CHECK(c) \ 80e8bd336dSYongbok Kim do { \ 81e8bd336dSYongbok Kim if (!(c)) { \ 82e8bd336dSYongbok Kim goto bad_offset; \ 83e8bd336dSYongbok Kim } \ 84e8bd336dSYongbok Kim } while (0) 85e8bd336dSYongbok Kim 86e8bd336dSYongbok Kim /* GIC Read VP Local/Other Registers */ 87e8bd336dSYongbok Kim static uint64_t gic_read_vp(MIPSGICState *gic, uint32_t vp_index, hwaddr addr, 88e8bd336dSYongbok Kim unsigned size) 89e8bd336dSYongbok Kim { 90e8bd336dSYongbok Kim switch (addr) { 91e8bd336dSYongbok Kim case GIC_VP_CTL_OFS: 92e8bd336dSYongbok Kim return gic->vps[vp_index].ctl; 93e8bd336dSYongbok Kim case GIC_VP_PEND_OFS: 94e8bd336dSYongbok Kim mips_gictimer_get_sh_count(gic->gic_timer); 95e8bd336dSYongbok Kim return gic->vps[vp_index].pend; 96e8bd336dSYongbok Kim case GIC_VP_MASK_OFS: 97e8bd336dSYongbok Kim return gic->vps[vp_index].mask; 98e8bd336dSYongbok Kim case GIC_VP_COMPARE_MAP_OFS: 99e8bd336dSYongbok Kim return gic->vps[vp_index].compare_map; 100e8bd336dSYongbok Kim case GIC_VP_OTHER_ADDR_OFS: 101e8bd336dSYongbok Kim return gic->vps[vp_index].other_addr; 102e8bd336dSYongbok Kim case GIC_VP_IDENT_OFS: 103e8bd336dSYongbok Kim return vp_index; 104e8bd336dSYongbok Kim case GIC_VP_COMPARE_LO_OFS: 105e8bd336dSYongbok Kim return mips_gictimer_get_vp_compare(gic->gic_timer, vp_index); 106e8bd336dSYongbok Kim case GIC_VP_COMPARE_HI_OFS: 107e8bd336dSYongbok Kim return 0; 108e8bd336dSYongbok Kim default: 109e8bd336dSYongbok Kim qemu_log_mask(LOG_UNIMP, "Read %d bytes at GIC offset LOCAL/OTHER 0x%" 110e8bd336dSYongbok Kim PRIx64 "\n", size, addr); 111e8bd336dSYongbok Kim break; 112e8bd336dSYongbok Kim } 113e8bd336dSYongbok Kim return 0; 114e8bd336dSYongbok Kim } 115e8bd336dSYongbok Kim 116e8bd336dSYongbok Kim static uint64_t gic_read(void *opaque, hwaddr addr, unsigned size) 117e8bd336dSYongbok Kim { 118e8bd336dSYongbok Kim MIPSGICState *gic = (MIPSGICState *) opaque; 119e8bd336dSYongbok Kim uint32_t vp_index = current_cpu->cpu_index; 120e8bd336dSYongbok Kim uint64_t ret = 0; 121e8bd336dSYongbok Kim int i, base, irq_src; 122e8bd336dSYongbok Kim uint32_t other_index; 123e8bd336dSYongbok Kim 124e8bd336dSYongbok Kim switch (addr) { 125e8bd336dSYongbok Kim case GIC_SH_CONFIG_OFS: 126e8bd336dSYongbok Kim ret = gic->sh_config | (mips_gictimer_get_countstop(gic->gic_timer) << 127e8bd336dSYongbok Kim GIC_SH_CONFIG_COUNTSTOP_SHF); 128e8bd336dSYongbok Kim break; 129e8bd336dSYongbok Kim case GIC_SH_COUNTERLO_OFS: 130e8bd336dSYongbok Kim ret = mips_gictimer_get_sh_count(gic->gic_timer); 131e8bd336dSYongbok Kim break; 132e8bd336dSYongbok Kim case GIC_SH_COUNTERHI_OFS: 133e8bd336dSYongbok Kim ret = 0; 134e8bd336dSYongbok Kim break; 135e8bd336dSYongbok Kim case GIC_SH_PEND_OFS ... GIC_SH_PEND_LAST_OFS: 136e8bd336dSYongbok Kim /* each bit represents pending status for an interrupt pin */ 137e8bd336dSYongbok Kim base = (addr - GIC_SH_PEND_OFS) * 8; 138e8bd336dSYongbok Kim OFFSET_CHECK((base + size * 8) <= gic->num_irq); 139e8bd336dSYongbok Kim for (i = 0; i < size * 8; i++) { 140e8bd336dSYongbok Kim ret |= (uint64_t) (gic->irq_state[base + i].pending) << i; 141e8bd336dSYongbok Kim } 142e8bd336dSYongbok Kim break; 143e8bd336dSYongbok Kim case GIC_SH_MASK_OFS ... GIC_SH_MASK_LAST_OFS: 144e8bd336dSYongbok Kim /* each bit represents status for an interrupt pin */ 145e8bd336dSYongbok Kim base = (addr - GIC_SH_MASK_OFS) * 8; 146e8bd336dSYongbok Kim OFFSET_CHECK((base + size * 8) <= gic->num_irq); 147e8bd336dSYongbok Kim for (i = 0; i < size * 8; i++) { 148e8bd336dSYongbok Kim ret |= (uint64_t) (gic->irq_state[base + i].enabled) << i; 149e8bd336dSYongbok Kim } 150e8bd336dSYongbok Kim break; 151e8bd336dSYongbok Kim case GIC_SH_MAP0_PIN_OFS ... GIC_SH_MAP255_PIN_OFS: 152e8bd336dSYongbok Kim /* 32 bits per a pin */ 153e8bd336dSYongbok Kim irq_src = (addr - GIC_SH_MAP0_PIN_OFS) / 4; 154e8bd336dSYongbok Kim OFFSET_CHECK(irq_src < gic->num_irq); 155e8bd336dSYongbok Kim ret = gic->irq_state[irq_src].map_pin; 156e8bd336dSYongbok Kim break; 157e8bd336dSYongbok Kim case GIC_SH_MAP0_VP_OFS ... GIC_SH_MAP255_VP_LAST_OFS: 158e8bd336dSYongbok Kim /* up to 32 bytes per a pin */ 159e8bd336dSYongbok Kim irq_src = (addr - GIC_SH_MAP0_VP_OFS) / 32; 160e8bd336dSYongbok Kim OFFSET_CHECK(irq_src < gic->num_irq); 161e8bd336dSYongbok Kim if ((gic->irq_state[irq_src].map_vp) >= 0) { 162e8bd336dSYongbok Kim ret = (uint64_t) 1 << (gic->irq_state[irq_src].map_vp); 163e8bd336dSYongbok Kim } else { 164e8bd336dSYongbok Kim ret = 0; 165e8bd336dSYongbok Kim } 166e8bd336dSYongbok Kim break; 167e8bd336dSYongbok Kim /* VP-Local Register */ 168e8bd336dSYongbok Kim case VP_LOCAL_SECTION_OFS ... (VP_LOCAL_SECTION_OFS + GIC_VL_BRK_GROUP): 169e8bd336dSYongbok Kim ret = gic_read_vp(gic, vp_index, addr - VP_LOCAL_SECTION_OFS, size); 170e8bd336dSYongbok Kim break; 171e8bd336dSYongbok Kim /* VP-Other Register */ 172e8bd336dSYongbok Kim case VP_OTHER_SECTION_OFS ... (VP_OTHER_SECTION_OFS + GIC_VL_BRK_GROUP): 173e8bd336dSYongbok Kim other_index = gic->vps[vp_index].other_addr; 174e8bd336dSYongbok Kim ret = gic_read_vp(gic, other_index, addr - VP_OTHER_SECTION_OFS, size); 175e8bd336dSYongbok Kim break; 176e8bd336dSYongbok Kim /* User-Mode Visible section */ 177e8bd336dSYongbok Kim case USM_VISIBLE_SECTION_OFS + GIC_USER_MODE_COUNTERLO: 178e8bd336dSYongbok Kim ret = mips_gictimer_get_sh_count(gic->gic_timer); 179e8bd336dSYongbok Kim break; 180e8bd336dSYongbok Kim case USM_VISIBLE_SECTION_OFS + GIC_USER_MODE_COUNTERHI: 181e8bd336dSYongbok Kim ret = 0; 182e8bd336dSYongbok Kim break; 183e8bd336dSYongbok Kim default: 184e8bd336dSYongbok Kim qemu_log_mask(LOG_UNIMP, "Read %d bytes at GIC offset 0x%" PRIx64 "\n", 185e8bd336dSYongbok Kim size, addr); 186e8bd336dSYongbok Kim break; 187e8bd336dSYongbok Kim } 188e8bd336dSYongbok Kim return ret; 189e8bd336dSYongbok Kim bad_offset: 190e8bd336dSYongbok Kim qemu_log_mask(LOG_GUEST_ERROR, "Wrong GIC offset at 0x%" PRIx64 "\n", addr); 191e8bd336dSYongbok Kim return 0; 192e8bd336dSYongbok Kim } 193e8bd336dSYongbok Kim 194e8bd336dSYongbok Kim static void gic_timer_expire_cb(void *opaque, uint32_t vp_index) 195e8bd336dSYongbok Kim { 196e8bd336dSYongbok Kim MIPSGICState *gic = opaque; 197e8bd336dSYongbok Kim 198e8bd336dSYongbok Kim gic->vps[vp_index].pend |= (1 << GIC_LOCAL_INT_COMPARE); 199e8bd336dSYongbok Kim if (gic->vps[vp_index].pend & 200e8bd336dSYongbok Kim (gic->vps[vp_index].mask & GIC_VP_MASK_CMP_MSK)) { 201e8bd336dSYongbok Kim if (gic->vps[vp_index].compare_map & GIC_MAP_TO_PIN_MSK) { 202e8bd336dSYongbok Kim /* it is safe to set the irq high regardless of other GIC IRQs */ 203e8bd336dSYongbok Kim uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK); 204e8bd336dSYongbok Kim qemu_irq_raise(gic->vps[vp_index].env->irq 205e8bd336dSYongbok Kim [pin + GIC_CPU_PIN_OFFSET]); 206e8bd336dSYongbok Kim } 207e8bd336dSYongbok Kim } 208e8bd336dSYongbok Kim } 209e8bd336dSYongbok Kim 210e8bd336dSYongbok Kim static void gic_timer_store_vp_compare(MIPSGICState *gic, uint32_t vp_index, 211e8bd336dSYongbok Kim uint64_t compare) 212e8bd336dSYongbok Kim { 213e8bd336dSYongbok Kim gic->vps[vp_index].pend &= ~(1 << GIC_LOCAL_INT_COMPARE); 214e8bd336dSYongbok Kim if (gic->vps[vp_index].compare_map & GIC_MAP_TO_PIN_MSK) { 215e8bd336dSYongbok Kim uint32_t pin = (gic->vps[vp_index].compare_map & GIC_MAP_MSK); 216*2e2a1b46SPaul Burton mips_gic_set_vp_irq(gic, vp_index, pin); 217e8bd336dSYongbok Kim } 218e8bd336dSYongbok Kim mips_gictimer_store_vp_compare(gic->gic_timer, vp_index, compare); 219e8bd336dSYongbok Kim } 220e8bd336dSYongbok Kim 221e8bd336dSYongbok Kim /* GIC Write VP Local/Other Registers */ 222e8bd336dSYongbok Kim static void gic_write_vp(MIPSGICState *gic, uint32_t vp_index, hwaddr addr, 223e8bd336dSYongbok Kim uint64_t data, unsigned size) 224e8bd336dSYongbok Kim { 225e8bd336dSYongbok Kim switch (addr) { 226e8bd336dSYongbok Kim case GIC_VP_CTL_OFS: 227e8bd336dSYongbok Kim /* EIC isn't supported */ 228e8bd336dSYongbok Kim break; 229e8bd336dSYongbok Kim case GIC_VP_RMASK_OFS: 230e8bd336dSYongbok Kim gic->vps[vp_index].mask &= ~(data & GIC_VP_SET_RESET_MSK) & 231e8bd336dSYongbok Kim GIC_VP_SET_RESET_MSK; 232e8bd336dSYongbok Kim break; 233e8bd336dSYongbok Kim case GIC_VP_SMASK_OFS: 234e8bd336dSYongbok Kim gic->vps[vp_index].mask |= data & GIC_VP_SET_RESET_MSK; 235e8bd336dSYongbok Kim break; 236e8bd336dSYongbok Kim case GIC_VP_COMPARE_MAP_OFS: 237e8bd336dSYongbok Kim /* EIC isn't supported */ 238e8bd336dSYongbok Kim OFFSET_CHECK((data & GIC_MAP_MSK) <= GIC_CPU_INT_MAX); 239e8bd336dSYongbok Kim gic->vps[vp_index].compare_map = data & GIC_MAP_TO_PIN_REG_MSK; 240e8bd336dSYongbok Kim break; 241e8bd336dSYongbok Kim case GIC_VP_OTHER_ADDR_OFS: 242e8bd336dSYongbok Kim OFFSET_CHECK(data < gic->num_vps); 243e8bd336dSYongbok Kim gic->vps[vp_index].other_addr = data; 244e8bd336dSYongbok Kim break; 245e8bd336dSYongbok Kim case GIC_VP_COMPARE_LO_OFS: 246e8bd336dSYongbok Kim gic_timer_store_vp_compare(gic, vp_index, data); 247e8bd336dSYongbok Kim break; 248e8bd336dSYongbok Kim default: 249e8bd336dSYongbok Kim qemu_log_mask(LOG_UNIMP, "Write %d bytes at GIC offset LOCAL/OTHER " 250e8bd336dSYongbok Kim "0x%" PRIx64" 0x%08" PRIx64 "\n", size, addr, data); 251e8bd336dSYongbok Kim break; 252e8bd336dSYongbok Kim } 253e8bd336dSYongbok Kim return; 254e8bd336dSYongbok Kim bad_offset: 255e8bd336dSYongbok Kim qemu_log_mask(LOG_GUEST_ERROR, "Wrong GIC offset at 0x%" PRIx64 "\n", addr); 256e8bd336dSYongbok Kim return; 257e8bd336dSYongbok Kim } 258e8bd336dSYongbok Kim 259e8bd336dSYongbok Kim static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) 260e8bd336dSYongbok Kim { 261e8bd336dSYongbok Kim int intr; 262e8bd336dSYongbok Kim MIPSGICState *gic = (MIPSGICState *) opaque; 263e8bd336dSYongbok Kim uint32_t vp_index = current_cpu->cpu_index; 264e8bd336dSYongbok Kim int i, base, irq_src; 265e8bd336dSYongbok Kim uint32_t other_index; 266e8bd336dSYongbok Kim 267e8bd336dSYongbok Kim switch (addr) { 268e8bd336dSYongbok Kim case GIC_SH_CONFIG_OFS: 269e8bd336dSYongbok Kim { 270e8bd336dSYongbok Kim uint32_t pre_cntstop = mips_gictimer_get_countstop(gic->gic_timer); 271e8bd336dSYongbok Kim uint32_t new_cntstop = (data & GIC_SH_CONFIG_COUNTSTOP_MSK) >> 272e8bd336dSYongbok Kim GIC_SH_CONFIG_COUNTSTOP_SHF; 273e8bd336dSYongbok Kim if (pre_cntstop != new_cntstop) { 274e8bd336dSYongbok Kim if (new_cntstop == 1) { 275e8bd336dSYongbok Kim mips_gictimer_stop_count(gic->gic_timer); 276e8bd336dSYongbok Kim } else { 277e8bd336dSYongbok Kim mips_gictimer_start_count(gic->gic_timer); 278e8bd336dSYongbok Kim } 279e8bd336dSYongbok Kim } 280e8bd336dSYongbok Kim } 281e8bd336dSYongbok Kim break; 282e8bd336dSYongbok Kim case GIC_SH_COUNTERLO_OFS: 283e8bd336dSYongbok Kim if (mips_gictimer_get_countstop(gic->gic_timer)) { 284e8bd336dSYongbok Kim mips_gictimer_store_sh_count(gic->gic_timer, data); 285e8bd336dSYongbok Kim } 286e8bd336dSYongbok Kim break; 287e8bd336dSYongbok Kim case GIC_SH_RMASK_OFS ... GIC_SH_RMASK_LAST_OFS: 288e8bd336dSYongbok Kim /* up to 64 bits per a pin */ 289e8bd336dSYongbok Kim base = (addr - GIC_SH_RMASK_OFS) * 8; 290e8bd336dSYongbok Kim OFFSET_CHECK((base + size * 8) <= gic->num_irq); 291e8bd336dSYongbok Kim for (i = 0; i < size * 8; i++) { 292e8bd336dSYongbok Kim gic->irq_state[base + i].enabled &= !((data >> i) & 1); 293*2e2a1b46SPaul Burton gic_update_pin_for_irq(gic, base + i); 294e8bd336dSYongbok Kim } 295e8bd336dSYongbok Kim break; 296e8bd336dSYongbok Kim case GIC_SH_WEDGE_OFS: 297e8bd336dSYongbok Kim /* Figure out which VP/HW Interrupt this maps to */ 298e8bd336dSYongbok Kim intr = data & ~GIC_SH_WEDGE_RW_MSK; 299e8bd336dSYongbok Kim /* Mask/Enabled Checks */ 300e8bd336dSYongbok Kim OFFSET_CHECK(intr < gic->num_irq); 301e8bd336dSYongbok Kim if (data & GIC_SH_WEDGE_RW_MSK) { 302e8bd336dSYongbok Kim gic_set_irq(gic, intr, 1); 303e8bd336dSYongbok Kim } else { 304e8bd336dSYongbok Kim gic_set_irq(gic, intr, 0); 305e8bd336dSYongbok Kim } 306e8bd336dSYongbok Kim break; 307e8bd336dSYongbok Kim case GIC_SH_SMASK_OFS ... GIC_SH_SMASK_LAST_OFS: 308e8bd336dSYongbok Kim /* up to 64 bits per a pin */ 309e8bd336dSYongbok Kim base = (addr - GIC_SH_SMASK_OFS) * 8; 310e8bd336dSYongbok Kim OFFSET_CHECK((base + size * 8) <= gic->num_irq); 311e8bd336dSYongbok Kim for (i = 0; i < size * 8; i++) { 312e8bd336dSYongbok Kim gic->irq_state[base + i].enabled |= (data >> i) & 1; 313*2e2a1b46SPaul Burton gic_update_pin_for_irq(gic, base + i); 314e8bd336dSYongbok Kim } 315e8bd336dSYongbok Kim break; 316e8bd336dSYongbok Kim case GIC_SH_MAP0_PIN_OFS ... GIC_SH_MAP255_PIN_OFS: 317e8bd336dSYongbok Kim /* 32 bits per a pin */ 318e8bd336dSYongbok Kim irq_src = (addr - GIC_SH_MAP0_PIN_OFS) / 4; 319e8bd336dSYongbok Kim OFFSET_CHECK(irq_src < gic->num_irq); 320e8bd336dSYongbok Kim /* EIC isn't supported */ 321e8bd336dSYongbok Kim OFFSET_CHECK((data & GIC_MAP_MSK) <= GIC_CPU_INT_MAX); 322e8bd336dSYongbok Kim gic->irq_state[irq_src].map_pin = data & GIC_MAP_TO_PIN_REG_MSK; 323e8bd336dSYongbok Kim break; 324e8bd336dSYongbok Kim case GIC_SH_MAP0_VP_OFS ... GIC_SH_MAP255_VP_LAST_OFS: 325e8bd336dSYongbok Kim /* up to 32 bytes per a pin */ 326e8bd336dSYongbok Kim irq_src = (addr - GIC_SH_MAP0_VP_OFS) / 32; 327e8bd336dSYongbok Kim OFFSET_CHECK(irq_src < gic->num_irq); 328e8bd336dSYongbok Kim data = data ? ctz64(data) : -1; 329e8bd336dSYongbok Kim OFFSET_CHECK(data < gic->num_vps); 330e8bd336dSYongbok Kim gic->irq_state[irq_src].map_vp = data; 331e8bd336dSYongbok Kim break; 332e8bd336dSYongbok Kim case VP_LOCAL_SECTION_OFS ... (VP_LOCAL_SECTION_OFS + GIC_VL_BRK_GROUP): 333e8bd336dSYongbok Kim gic_write_vp(gic, vp_index, addr - VP_LOCAL_SECTION_OFS, data, size); 334e8bd336dSYongbok Kim break; 335e8bd336dSYongbok Kim case VP_OTHER_SECTION_OFS ... (VP_OTHER_SECTION_OFS + GIC_VL_BRK_GROUP): 336e8bd336dSYongbok Kim other_index = gic->vps[vp_index].other_addr; 337e8bd336dSYongbok Kim gic_write_vp(gic, other_index, addr - VP_OTHER_SECTION_OFS, data, size); 338e8bd336dSYongbok Kim break; 339e8bd336dSYongbok Kim case USM_VISIBLE_SECTION_OFS + GIC_USER_MODE_COUNTERLO: 340e8bd336dSYongbok Kim case USM_VISIBLE_SECTION_OFS + GIC_USER_MODE_COUNTERHI: 341e8bd336dSYongbok Kim /* do nothing. Read-only section */ 342e8bd336dSYongbok Kim break; 343e8bd336dSYongbok Kim default: 344e8bd336dSYongbok Kim qemu_log_mask(LOG_UNIMP, "Write %d bytes at GIC offset 0x%" PRIx64 345e8bd336dSYongbok Kim " 0x%08" PRIx64 "\n", size, addr, data); 346e8bd336dSYongbok Kim break; 347e8bd336dSYongbok Kim } 348e8bd336dSYongbok Kim return; 349e8bd336dSYongbok Kim bad_offset: 350e8bd336dSYongbok Kim qemu_log_mask(LOG_GUEST_ERROR, "Wrong GIC offset at 0x%" PRIx64 "\n", addr); 351e8bd336dSYongbok Kim } 352e8bd336dSYongbok Kim 353e8bd336dSYongbok Kim static void gic_reset(void *opaque) 354e8bd336dSYongbok Kim { 355e8bd336dSYongbok Kim int i; 356e8bd336dSYongbok Kim MIPSGICState *gic = (MIPSGICState *) opaque; 357e8bd336dSYongbok Kim int numintrs = (gic->num_irq / 8) - 1; 358e8bd336dSYongbok Kim 359e8bd336dSYongbok Kim gic->sh_config = /* COUNTSTOP = 0 it is accessible via MIPSGICTimer*/ 360e8bd336dSYongbok Kim /* CounterHi not implemented */ 361e8bd336dSYongbok Kim (0 << GIC_SH_CONFIG_COUNTBITS_SHF) | 362e8bd336dSYongbok Kim (numintrs << GIC_SH_CONFIG_NUMINTRS_SHF) | 363e8bd336dSYongbok Kim (gic->num_vps << GIC_SH_CONFIG_PVPS_SHF); 364e8bd336dSYongbok Kim for (i = 0; i < gic->num_vps; i++) { 365e8bd336dSYongbok Kim gic->vps[i].ctl = 0x0; 366e8bd336dSYongbok Kim gic->vps[i].pend = 0x0; 367e8bd336dSYongbok Kim /* PERFCNT, TIMER and WD not implemented */ 368e8bd336dSYongbok Kim gic->vps[i].mask = 0x32; 369e8bd336dSYongbok Kim gic->vps[i].compare_map = GIC_MAP_TO_PIN_MSK; 370e8bd336dSYongbok Kim mips_gictimer_store_vp_compare(gic->gic_timer, i, 0xffffffff); 371e8bd336dSYongbok Kim gic->vps[i].other_addr = 0x0; 372e8bd336dSYongbok Kim } 373e8bd336dSYongbok Kim for (i = 0; i < gic->num_irq; i++) { 374e8bd336dSYongbok Kim gic->irq_state[i].enabled = 0; 375e8bd336dSYongbok Kim gic->irq_state[i].pending = 0; 376e8bd336dSYongbok Kim gic->irq_state[i].map_pin = GIC_MAP_TO_PIN_MSK; 377e8bd336dSYongbok Kim gic->irq_state[i].map_vp = -1; 378e8bd336dSYongbok Kim } 379e8bd336dSYongbok Kim mips_gictimer_store_sh_count(gic->gic_timer, 0); 380e8bd336dSYongbok Kim /* COUNTSTOP = 0 */ 381e8bd336dSYongbok Kim mips_gictimer_start_count(gic->gic_timer); 382e8bd336dSYongbok Kim } 383e8bd336dSYongbok Kim 384e8bd336dSYongbok Kim static const MemoryRegionOps gic_ops = { 385e8bd336dSYongbok Kim .read = gic_read, 386e8bd336dSYongbok Kim .write = gic_write, 387e8bd336dSYongbok Kim .endianness = DEVICE_NATIVE_ENDIAN, 388e8bd336dSYongbok Kim .impl = { 389e8bd336dSYongbok Kim .max_access_size = 8, 390e8bd336dSYongbok Kim }, 391e8bd336dSYongbok Kim }; 392e8bd336dSYongbok Kim 393e8bd336dSYongbok Kim static void mips_gic_init(Object *obj) 394e8bd336dSYongbok Kim { 395e8bd336dSYongbok Kim SysBusDevice *sbd = SYS_BUS_DEVICE(obj); 396e8bd336dSYongbok Kim MIPSGICState *s = MIPS_GIC(obj); 397e8bd336dSYongbok Kim 398e8bd336dSYongbok Kim memory_region_init_io(&s->mr, OBJECT(s), &gic_ops, s, 399e8bd336dSYongbok Kim "mips-gic", GIC_ADDRSPACE_SZ); 400e8bd336dSYongbok Kim sysbus_init_mmio(sbd, &s->mr); 401e8bd336dSYongbok Kim qemu_register_reset(gic_reset, s); 402e8bd336dSYongbok Kim } 403e8bd336dSYongbok Kim 404e8bd336dSYongbok Kim static void mips_gic_realize(DeviceState *dev, Error **errp) 405e8bd336dSYongbok Kim { 406e8bd336dSYongbok Kim MIPSGICState *s = MIPS_GIC(dev); 407e8bd336dSYongbok Kim CPUState *cs = first_cpu; 408e8bd336dSYongbok Kim int i; 409e8bd336dSYongbok Kim 410e8bd336dSYongbok Kim if (s->num_vps > GIC_MAX_VPS) { 411e8bd336dSYongbok Kim error_setg(errp, "Exceeded maximum CPUs %d", s->num_vps); 412e8bd336dSYongbok Kim return; 413e8bd336dSYongbok Kim } 414e8bd336dSYongbok Kim if ((s->num_irq > GIC_MAX_INTRS) || (s->num_irq % 8) || (s->num_irq <= 0)) { 415e8bd336dSYongbok Kim error_setg(errp, "GIC supports up to %d external interrupts in " 416e8bd336dSYongbok Kim "multiples of 8 : %d", GIC_MAX_INTRS, s->num_irq); 417e8bd336dSYongbok Kim return; 418e8bd336dSYongbok Kim } 419e8bd336dSYongbok Kim s->vps = g_new(MIPSGICVPState, s->num_vps); 420e8bd336dSYongbok Kim s->irq_state = g_new(MIPSGICIRQState, s->num_irq); 421e8bd336dSYongbok Kim /* Register the env for all VPs with the GIC */ 422e8bd336dSYongbok Kim for (i = 0; i < s->num_vps; i++) { 423e8bd336dSYongbok Kim if (cs != NULL) { 424e8bd336dSYongbok Kim s->vps[i].env = cs->env_ptr; 425e8bd336dSYongbok Kim cs = CPU_NEXT(cs); 426e8bd336dSYongbok Kim } else { 427e8bd336dSYongbok Kim error_setg(errp, 428e8bd336dSYongbok Kim "Unable to initialize GIC, CPUState for CPU#%d not valid.", i); 429e8bd336dSYongbok Kim return; 430e8bd336dSYongbok Kim } 431e8bd336dSYongbok Kim } 432e8bd336dSYongbok Kim s->gic_timer = mips_gictimer_init(s, s->num_vps, gic_timer_expire_cb); 433e8bd336dSYongbok Kim qdev_init_gpio_in(dev, gic_set_irq, s->num_irq); 434e8bd336dSYongbok Kim for (i = 0; i < s->num_irq; i++) { 435e8bd336dSYongbok Kim s->irq_state[i].irq = qdev_get_gpio_in(dev, i); 436e8bd336dSYongbok Kim } 437e8bd336dSYongbok Kim } 438e8bd336dSYongbok Kim 439e8bd336dSYongbok Kim static Property mips_gic_properties[] = { 440e8bd336dSYongbok Kim DEFINE_PROP_INT32("num-vp", MIPSGICState, num_vps, 1), 441e8bd336dSYongbok Kim DEFINE_PROP_INT32("num-irq", MIPSGICState, num_irq, 256), 442e8bd336dSYongbok Kim DEFINE_PROP_END_OF_LIST(), 443e8bd336dSYongbok Kim }; 444e8bd336dSYongbok Kim 445e8bd336dSYongbok Kim static void mips_gic_class_init(ObjectClass *klass, void *data) 446e8bd336dSYongbok Kim { 447e8bd336dSYongbok Kim DeviceClass *dc = DEVICE_CLASS(klass); 448e8bd336dSYongbok Kim 449e8bd336dSYongbok Kim dc->props = mips_gic_properties; 450e8bd336dSYongbok Kim dc->realize = mips_gic_realize; 451e8bd336dSYongbok Kim } 452e8bd336dSYongbok Kim 453e8bd336dSYongbok Kim static const TypeInfo mips_gic_info = { 454e8bd336dSYongbok Kim .name = TYPE_MIPS_GIC, 455e8bd336dSYongbok Kim .parent = TYPE_SYS_BUS_DEVICE, 456e8bd336dSYongbok Kim .instance_size = sizeof(MIPSGICState), 457e8bd336dSYongbok Kim .instance_init = mips_gic_init, 458e8bd336dSYongbok Kim .class_init = mips_gic_class_init, 459e8bd336dSYongbok Kim }; 460e8bd336dSYongbok Kim 461e8bd336dSYongbok Kim static void mips_gic_register_types(void) 462e8bd336dSYongbok Kim { 463e8bd336dSYongbok Kim type_register_static(&mips_gic_info); 464e8bd336dSYongbok Kim } 465e8bd336dSYongbok Kim 466e8bd336dSYongbok Kim type_init(mips_gic_register_types) 467