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