diff options
author | Xiaojuan Yang <yangxiaojuan@loongson.cn> | 2022-06-06 20:43:22 +0800 |
---|---|---|
committer | Richard Henderson <richard.henderson@linaro.org> | 2022-06-06 18:11:55 +0000 |
commit | 0f4fcf1845fe188901d4ff4cc807bd78690dddd0 (patch) | |
tree | cd7466fdef71bd8219e562c98335e6a78d89ede4 /hw/intc/loongarch_pch_pic.c | |
parent | f6783e34380955e9ec0656c7b9fb8936b9733a6a (diff) |
hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC)
This patch realize the PCH-PIC interrupt controller.
Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn>
Signed-off-by: Song Gao <gaosong@loongson.cn>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-Id: <20220606124333.2060567-33-yangxiaojuan@loongson.cn>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Diffstat (limited to 'hw/intc/loongarch_pch_pic.c')
-rw-r--r-- | hw/intc/loongarch_pch_pic.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c new file mode 100644 index 0000000000..3c9814a3b4 --- /dev/null +++ b/hw/intc/loongarch_pch_pic.c @@ -0,0 +1,431 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * QEMU Loongson 7A1000 I/O interrupt controller. + * + * Copyright (C) 2021 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "hw/loongarch/virt.h" +#include "hw/irq.h" +#include "hw/intc/loongarch_pch_pic.h" +#include "migration/vmstate.h" +#include "trace.h" + +static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level) +{ + unsigned long val; + int irq; + + if (level) { + val = mask & s->intirr & ~s->int_mask; + if (val) { + irq = find_first_bit(&val, 64); + s->intisr |= 0x1ULL << irq; + qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1); + } + } else { + val = mask & s->intisr; + if (val) { + irq = find_first_bit(&val, 64); + s->intisr &= ~(0x1ULL << irq); + qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0); + } + } +} + +static void pch_pic_irq_handler(void *opaque, int irq, int level) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + uint64_t mask = 1ULL << irq; + + assert(irq < PCH_PIC_IRQ_NUM); + trace_loongarch_pch_pic_irq_handler(irq, level); + + if (s->intedge & mask) { + /* Edge triggered */ + if (level) { + if ((s->last_intirr & mask) == 0) { + s->intirr |= mask; + } + s->last_intirr |= mask; + } else { + s->last_intirr &= ~mask; + } + } else { + /* Level triggered */ + if (level) { + s->intirr |= mask; + s->last_intirr |= mask; + } else { + s->intirr &= ~mask; + s->last_intirr &= ~mask; + } + } + pch_pic_update_irq(s, mask, level); +} + +static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr, + unsigned size) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + uint64_t val = 0; + uint32_t offset = addr & 0xfff; + + switch (offset) { + case PCH_PIC_INT_ID_LO: + val = PCH_PIC_INT_ID_VAL; + break; + case PCH_PIC_INT_ID_HI: + val = PCH_PIC_INT_ID_NUM; + break; + case PCH_PIC_INT_MASK_LO: + val = (uint32_t)s->int_mask; + break; + case PCH_PIC_INT_MASK_HI: + val = s->int_mask >> 32; + break; + case PCH_PIC_INT_EDGE_LO: + val = (uint32_t)s->intedge; + break; + case PCH_PIC_INT_EDGE_HI: + val = s->intedge >> 32; + break; + case PCH_PIC_HTMSI_EN_LO: + val = (uint32_t)s->htmsi_en; + break; + case PCH_PIC_HTMSI_EN_HI: + val = s->htmsi_en >> 32; + break; + case PCH_PIC_AUTO_CTRL0_LO: + case PCH_PIC_AUTO_CTRL0_HI: + case PCH_PIC_AUTO_CTRL1_LO: + case PCH_PIC_AUTO_CTRL1_HI: + break; + default: + break; + } + + trace_loongarch_pch_pic_low_readw(size, addr, val); + return val; +} + +static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi) +{ + uint64_t mask = 0xffffffff00000000; + uint64_t data = target; + + return hi ? (value & ~mask) | (data << 32) : (value & mask) | data; +} + +static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + uint32_t offset, old_valid, data = (uint32_t)value; + uint64_t old, int_mask; + offset = addr & 0xfff; + + trace_loongarch_pch_pic_low_writew(size, addr, data); + + switch (offset) { + case PCH_PIC_INT_MASK_LO: + old = s->int_mask; + s->int_mask = get_writew_val(old, data, 0); + old_valid = (uint32_t)old; + if (old_valid & ~data) { + pch_pic_update_irq(s, (old_valid & ~data), 1); + } + if (~old_valid & data) { + pch_pic_update_irq(s, (~old_valid & data), 0); + } + break; + case PCH_PIC_INT_MASK_HI: + old = s->int_mask; + s->int_mask = get_writew_val(old, data, 1); + old_valid = (uint32_t)(old >> 32); + int_mask = old_valid & ~data; + if (int_mask) { + pch_pic_update_irq(s, int_mask << 32, 1); + } + int_mask = ~old_valid & data; + if (int_mask) { + pch_pic_update_irq(s, int_mask << 32, 0); + } + break; + case PCH_PIC_INT_EDGE_LO: + s->intedge = get_writew_val(s->intedge, data, 0); + break; + case PCH_PIC_INT_EDGE_HI: + s->intedge = get_writew_val(s->intedge, data, 1); + break; + case PCH_PIC_INT_CLEAR_LO: + if (s->intedge & data) { + s->intirr &= (~data); + pch_pic_update_irq(s, data, 0); + s->intisr &= (~data); + } + break; + case PCH_PIC_INT_CLEAR_HI: + value <<= 32; + if (s->intedge & value) { + s->intirr &= (~value); + pch_pic_update_irq(s, value, 0); + s->intisr &= (~value); + } + break; + case PCH_PIC_HTMSI_EN_LO: + s->htmsi_en = get_writew_val(s->htmsi_en, data, 0); + break; + case PCH_PIC_HTMSI_EN_HI: + s->htmsi_en = get_writew_val(s->htmsi_en, data, 1); + break; + case PCH_PIC_AUTO_CTRL0_LO: + case PCH_PIC_AUTO_CTRL0_HI: + case PCH_PIC_AUTO_CTRL1_LO: + case PCH_PIC_AUTO_CTRL1_HI: + break; + default: + break; + } +} + +static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr, + unsigned size) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + uint64_t val = 0; + uint32_t offset = addr & 0xfff; + + switch (offset) { + case STATUS_LO_START: + val = (uint32_t)(s->intisr & (~s->int_mask)); + break; + case STATUS_HI_START: + val = (s->intisr & (~s->int_mask)) >> 32; + break; + case POL_LO_START: + val = (uint32_t)s->int_polarity; + break; + case POL_HI_START: + val = s->int_polarity >> 32; + break; + default: + break; + } + + trace_loongarch_pch_pic_high_readw(size, addr, val); + return val; +} + +static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + uint32_t offset, data = (uint32_t)value; + offset = addr & 0xfff; + + trace_loongarch_pch_pic_high_writew(size, addr, data); + + switch (offset) { + case STATUS_LO_START: + s->intisr = get_writew_val(s->intisr, data, 0); + break; + case STATUS_HI_START: + s->intisr = get_writew_val(s->intisr, data, 1); + break; + case POL_LO_START: + s->int_polarity = get_writew_val(s->int_polarity, data, 0); + break; + case POL_HI_START: + s->int_polarity = get_writew_val(s->int_polarity, data, 1); + break; + default: + break; + } +} + +static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr, + unsigned size) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + uint64_t val = 0; + uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET; + int64_t offset_tmp; + + switch (offset) { + case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END: + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + val = s->htmsi_vector[offset_tmp]; + } + break; + case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END: + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + val = s->route_entry[offset_tmp]; + } + break; + default: + break; + } + + trace_loongarch_pch_pic_readb(size, addr, val); + return val; +} + +static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque); + int32_t offset_tmp; + uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET; + + trace_loongarch_pch_pic_writeb(size, addr, data); + + switch (offset) { + case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END: + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff); + } + break; + case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END: + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + s->route_entry[offset_tmp] = (uint8_t)(data & 0xff); + } + break; + default: + break; + } +} + +static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = { + .read = loongarch_pch_pic_low_readw, + .write = loongarch_pch_pic_low_writew, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = { + .read = loongarch_pch_pic_high_readw, + .write = loongarch_pch_pic_high_writew, + .valid = { + .min_access_size = 4, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static const MemoryRegionOps loongarch_pch_pic_reg8_ops = { + .read = loongarch_pch_pic_readb, + .write = loongarch_pch_pic_writeb, + .valid = { + .min_access_size = 1, + .max_access_size = 1, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 1, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void loongarch_pch_pic_reset(DeviceState *d) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d); + int i; + + s->int_mask = -1; + s->htmsi_en = 0x0; + s->intedge = 0x0; + s->intclr = 0x0; + s->auto_crtl0 = 0x0; + s->auto_crtl1 = 0x0; + for (i = 0; i < 64; i++) { + s->route_entry[i] = 0x1; + s->htmsi_vector[i] = 0x0; + } + s->intirr = 0x0; + s->intisr = 0x0; + s->last_intirr = 0x0; + s->int_polarity = 0x0; +} + +static void loongarch_pch_pic_init(Object *obj) +{ + LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem32_low, obj, + &loongarch_pch_pic_reg32_low_ops, + s, PCH_PIC_NAME(.reg32_part1), 0x100); + memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops, + s, PCH_PIC_NAME(.reg8), 0x2a0); + memory_region_init_io(&s->iomem32_high, obj, + &loongarch_pch_pic_reg32_high_ops, + s, PCH_PIC_NAME(.reg32_part2), 0xc60); + sysbus_init_mmio(sbd, &s->iomem32_low); + sysbus_init_mmio(sbd, &s->iomem8); + sysbus_init_mmio(sbd, &s->iomem32_high); + + qdev_init_gpio_out(DEVICE(obj), s->parent_irq, PCH_PIC_IRQ_NUM); + qdev_init_gpio_in(DEVICE(obj), pch_pic_irq_handler, PCH_PIC_IRQ_NUM); +} + +static const VMStateDescription vmstate_loongarch_pch_pic = { + .name = TYPE_LOONGARCH_PCH_PIC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT64(int_mask, LoongArchPCHPIC), + VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC), + VMSTATE_UINT64(intedge, LoongArchPCHPIC), + VMSTATE_UINT64(intclr, LoongArchPCHPIC), + VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC), + VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC), + VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64), + VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64), + VMSTATE_UINT64(last_intirr, LoongArchPCHPIC), + VMSTATE_UINT64(intirr, LoongArchPCHPIC), + VMSTATE_UINT64(intisr, LoongArchPCHPIC), + VMSTATE_UINT64(int_polarity, LoongArchPCHPIC), + VMSTATE_END_OF_LIST() + } +}; + +static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = loongarch_pch_pic_reset; + dc->vmsd = &vmstate_loongarch_pch_pic; +} + +static const TypeInfo loongarch_pch_pic_info = { + .name = TYPE_LOONGARCH_PCH_PIC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(LoongArchPCHPIC), + .instance_init = loongarch_pch_pic_init, + .class_init = loongarch_pch_pic_class_init, +}; + +static void loongarch_pch_pic_register_types(void) +{ + type_register_static(&loongarch_pch_pic_info); +} + +type_init(loongarch_pch_pic_register_types) |