Message ID | 1638619645-11283-19-git-send-email-yangxiaojuan@loongson.cn (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add LoongArch softmmu support. | expand |
On 04/12/2021 12:07, Xiaojuan Yang wrote: > This patch realize the PCH-PIC interrupt controller. > > Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn> > Signed-off-by: Song Gao <gaosong@loongson.cn> > --- > hw/intc/Kconfig | 4 + > hw/intc/loongarch_pch_pic.c | 357 ++++++++++++++++++++++++++++ > hw/intc/meson.build | 1 + > hw/intc/trace-events | 5 + > hw/loongarch/Kconfig | 1 + > include/hw/intc/loongarch_pch_pic.h | 61 +++++ > 6 files changed, 429 insertions(+) > create mode 100644 hw/intc/loongarch_pch_pic.c > create mode 100644 include/hw/intc/loongarch_pch_pic.h > > diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig > index 511dcac537..96da13ad1d 100644 > --- a/hw/intc/Kconfig > +++ b/hw/intc/Kconfig > @@ -76,3 +76,7 @@ config M68K_IRQC > > config LOONGARCH_IPI > bool > + > +config LOONGARCH_PCH_PIC > + bool > + select UNIMP > diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c > new file mode 100644 > index 0000000000..2ede29ceb0 > --- /dev/null > +++ b/hw/intc/loongarch_pch_pic.c > @@ -0,0 +1,357 @@ > +/* 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/loongarch.h" > +#include "hw/irq.h" > +#include "hw/intc/loongarch_pch_pic.h" > +#include "migration/vmstate.h" > +#include "trace.h" > + > +#define for_each_set_bit(bit, addr, size) \ > + for ((bit) = find_first_bit((addr), (size)); \ > + (bit) < (size); \ > + (bit) = find_next_bit((addr), (size), (bit) + 1)) > + > +static void pch_pic_update_irq(loongarch_pch_pic *s, uint64_t mask, int level) > +{ > + int i; > + uint64_t val; > + val = mask & s->intirr & (~s->int_mask); > + > + for_each_set_bit(i, &val, 64) { > + if (level == 1) { > + if ((s->intisr & (0x1ULL << i)) == 0) { > + s->intisr |= 1ULL << i; > + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 1); > + } > + } else if (level == 0) { > + if (s->intisr & (0x1ULL << i)) { > + s->intisr &= ~(0x1ULL << i); > + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 0); > + } > + } > + } > +} The normal pattern would be to use something like: for (i = 0; i < 64; i++) { if (level) { s->intisr |= 1ULL << i; } else { s->intisr &= ~(0x1ULL << i); } qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], level); } Why is it necessary to check the previous value of (s->intisr & (0x1ULL << i)) here? > +static void pch_pic_irq_handler(void *opaque, int irq, int level) > +{ > + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); > + > + assert(irq < PCH_PIC_IRQ_NUM); > + uint64_t mask = 1ULL << irq; > + > + trace_pch_pic_irq_handler(s->intedge, 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_reg_read(void *opaque, hwaddr addr, > + unsigned size) > +{ > + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); > + uint64_t val = 0; > + uint32_t offset = addr & 0xfff; > + int64_t offset_tmp; > + > + if (size == 8) { > + switch (offset) { > + case PCH_PIC_INT_ID_OFFSET: > + val = (PCH_PIC_INT_ID_NUM << 32) | PCH_PIC_INT_ID_VAL; > + break; > + case PCH_PIC_INT_MASK_OFFSET: > + val = s->int_mask; > + break; > + case PCH_PIC_INT_STATUS_OFFSET: > + val = s->intisr & (~s->int_mask); > + break; > + case PCH_PIC_INT_EDGE_OFFSET: > + val = s->intedge; > + break; > + case PCH_PIC_INT_POL_OFFSET: > + val = s->int_polarity; > + break; > + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: > + val = s->htmsi_en; > + break; > + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: > + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: > + break; > + default: > + break; > + } > + } else if (size == 4) { > + switch (offset) { > + case PCH_PIC_INT_ID_OFFSET: > + val = PCH_PIC_INT_ID_VAL; > + break; > + case PCH_PIC_INT_ID_OFFSET + 4: > + val = PCH_PIC_INT_ID_NUM; > + break; > + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: > + val = ldl_p((void *)&s->int_mask + > + (offset - PCH_PIC_INT_MASK_OFFSET)); > + break; > + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: > + val = ldl_p((void *)&s->intisr + > + (offset - PCH_PIC_INT_STATUS_OFFSET)) & (~s->int_mask); > + break; > + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: > + val = ldl_p((void *)&s->intedge + > + (offset - PCH_PIC_INT_EDGE_OFFSET)); > + break; > + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: > + val = ldl_p((void *)&s->int_polarity + > + (offset - PCH_PIC_INT_POL_OFFSET)); > + break; > + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: > + val = ldl_p((void *)&s->htmsi_en + > + (offset - PCH_PIC_HTMSI_EN_OFFSET)); > + break; > + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: > + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: > + break; > + default: > + break; > + } > + } else if (size == 1) { > + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { > + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; > + if (offset_tmp >= 0 && offset_tmp < 64) { > + val = s->htmsi_vector[offset_tmp]; > + } > + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { > + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; > + if (offset_tmp >= 0 && offset_tmp < 64) { > + val = s->route_entry[offset_tmp]; > + } > + } > + } > + > + trace_loongarch_pch_pic_read(size, (uint32_t)addr, val); > + return val; > +} > + > +static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr, > + uint64_t data, unsigned size) > +{ > + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); Perhaps use LoongArchPCHPIC for the camel case version? > + int32_t offset_tmp; > + uint32_t offset, old; > + offset = addr & 0xfff; > + > + trace_loongarch_pch_pic_write(size, (uint32_t)addr, data); > + > + if (size == 8) { > + switch (offset) { > + case PCH_PIC_INT_MASK_OFFSET: > + old = s->int_mask; > + s->int_mask = data; > + if (old & ~data) { > + pch_pic_update_irq(s, (old & ~data), 1); > + } else if (~old & data) { > + pch_pic_update_irq(s, (~old & data), 0); > + } > + break; > + case PCH_PIC_INT_STATUS_OFFSET: > + s->intisr = data; > + break; > + case PCH_PIC_INT_EDGE_OFFSET: > + s->intedge = data; > + break; > + case PCH_PIC_INT_CLEAR_OFFSET: > + s->intirr &= (~(data & s->intedge)); > + pch_pic_update_irq(s, data, 0); > + s->intisr &= (~data); > + break; > + case PCH_PIC_INT_POL_OFFSET: > + s->int_polarity = data; > + break; > + case PCH_PIC_HTMSI_EN_OFFSET: > + s->htmsi_en = data; > + break; > + case PCH_PIC_AUTO_CTRL0_OFFSET: > + case PCH_PIC_AUTO_CTRL1_OFFSET: > + break; > + default: > + break; > + } > + } else if (size == 4) { > + switch (offset) { > + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: > + offset -= PCH_PIC_INT_MASK_OFFSET; > + old = ldl_p((void *)&s->int_mask + offset); > + stl_p((void *)&s->int_mask + offset, data); > + > + if (old & ~data) { > + pch_pic_update_irq(s, (old & ~data), 1); > + } else if (~old & data) { > + pch_pic_update_irq(s, (~old & data), 0); > + } > + break; > + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: > + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_STATUS_OFFSET), > + data); > + break; > + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: > + stl_p((void *)&s->intedge + (offset - PCH_PIC_INT_EDGE_OFFSET), > + data); > + break; > + case PCH_PIC_INT_CLEAR_OFFSET...PCH_PIC_INT_CLEAR_END: > + old = s->intirr & (~(data & s->intedge)); > + stl_p((void *)&s->intirr + (offset - PCH_PIC_INT_CLEAR_OFFSET), > + old); > + pch_pic_update_irq(s, data, 0); > + old = s->intisr & (~data); > + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_CLEAR_OFFSET), > + old); > + break; > + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: > + stl_p((void *)&s->int_polarity + (offset - PCH_PIC_INT_POL_OFFSET), > + data); > + break; > + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: > + stl_p((void *)&s->htmsi_en + (offset - PCH_PIC_HTMSI_EN_OFFSET), > + data); > + break; > + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: > + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: > + break; > + default: > + break; > + } > + } else if (size == 1) { > + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { > + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; > + if (offset_tmp >= 0 && offset_tmp < 64) { > + s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff); > + } > + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { > + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; > + if (offset_tmp >= 0 && offset_tmp < 64) { > + s->route_entry[offset_tmp] = (uint8_t)(data & 0xff); > + } > + } > + } > +} > + > +static const MemoryRegionOps loongarch_pch_pic_ops = { > + .read = loongarch_pch_pic_reg_read, > + .write = loongarch_pch_pic_reg_write, > + .valid = { > + .min_access_size = 1, > + .max_access_size = 8, > + }, > + .impl = { > + .min_access_size = 1, > + .max_access_size = 8, > + }, > + .endianness = DEVICE_LITTLE_ENDIAN, > +}; It feels like this should be split into 2 separate memory regions: as per before set .min_access_size = 4 for PCH_PIC_INT_MASK_OFFSET ... PCH_PIC_AUTO_CTRL1_END and use uint32_t for the underlying types in loongarch_pch_pic. This leaves a separate memory region for accesses > PCH_PIC_HTMSI_VEC_OFFSET which can be set with .min_access_size = 1 which maps nicely to the underlying uint8_t types. Which means you should be able to access the arrays directly instead of having to use ldl_p() and stl_p(). > +static void loongarch_pch_pic_reset(DeviceState *d) > +{ > + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d); > + int i; > + > + s->int_mask = -1ULL; > + 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) > +{ > + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(obj); > + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); > + int i; > + > + memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops, > + s, TYPE_LOONGARCH_PCH_PIC, 0x1000); > + sysbus_init_mmio(sbd, &s->iomem); > + > + for (i = 0; i < PCH_PIC_IRQ_NUM; i++) { > + sysbus_init_irq(sbd, &s->parent_irq[i]); > + } > + 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, loongarch_pch_pic), > + VMSTATE_UINT64(htmsi_en, loongarch_pch_pic), > + VMSTATE_UINT64(intedge, loongarch_pch_pic), > + VMSTATE_UINT64(intclr, loongarch_pch_pic), > + VMSTATE_UINT64(auto_crtl0, loongarch_pch_pic), > + VMSTATE_UINT64(auto_crtl1, loongarch_pch_pic), > + VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 64), > + VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 64), > + VMSTATE_UINT64(last_intirr, loongarch_pch_pic), > + VMSTATE_UINT64(intirr, loongarch_pch_pic), > + VMSTATE_UINT64(intisr, loongarch_pch_pic), > + VMSTATE_UINT64(int_polarity, loongarch_pch_pic), > + 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(loongarch_pch_pic), > + .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) > diff --git a/hw/intc/meson.build b/hw/intc/meson.build > index 51f0c3988a..33ba63266e 100644 > --- a/hw/intc/meson.build > +++ b/hw/intc/meson.build > @@ -58,3 +58,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], > specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) > specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) > specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) > +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) > diff --git a/hw/intc/trace-events b/hw/intc/trace-events > index 124608e51f..52fedf82be 100644 > --- a/hw/intc/trace-events > +++ b/hw/intc/trace-events > @@ -250,3 +250,8 @@ sh_intc_set(int id, int enable) "setting interrupt group %d to %d" > # loongarch_ipi.c > loongarch_ipi_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 > loongarch_ipi_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 > + > +# loongarch_pch_pic.c > +pch_pic_irq_handler(uint32_t edge, int irq, int level) "edge 0x%02x irq %d level %d" > +loongarch_pch_pic_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 > +loongarch_pch_pic_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 > diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig > index 1591574397..c2b8046b94 100644 > --- a/hw/loongarch/Kconfig > +++ b/hw/loongarch/Kconfig > @@ -2,3 +2,4 @@ config LOONGSON3_LS7A > bool > select PCI_EXPRESS_7A > select LOONGARCH_IPI > + select LOONGARCH_PCH_PIC > diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h > new file mode 100644 > index 0000000000..bc04ed28ef > --- /dev/null > +++ b/include/hw/intc/loongarch_pch_pic.h > @@ -0,0 +1,61 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * LoongArch 7A1000 I/O interrupt controller definitions > + * > + * Copyright (c) 2021 Loongson Technology Corporation Limited > + */ > + > +#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" > +DECLARE_INSTANCE_CHECKER(struct loongarch_pch_pic, LOONGARCH_PCH_PIC, > + TYPE_LOONGARCH_PCH_PIC) > + > +#define PCH_PIC_IRQ_START 0 > +#define PCH_PIC_IRQ_END 63 > +#define PCH_PIC_IRQ_NUM 64 > +#define PCH_PIC_INT_ID_VAL 0x7000000UL > +#define PCH_PIC_INT_ID_NUM 0x3f0001UL > + > +#define PCH_PIC_INT_ID_OFFSET 0x00 > +#define PCH_PIC_INT_ID_END 0x07 > +#define PCH_PIC_INT_MASK_OFFSET 0x20 > +#define PCH_PIC_INT_MASK_END 0x27 > +#define PCH_PIC_HTMSI_EN_OFFSET 0x40 > +#define PCH_PIC_HTMSI_EN_END 0x47 > +#define PCH_PIC_INT_EDGE_OFFSET 0x60 > +#define PCH_PIC_INT_EDGE_END 0x67 > +#define PCH_PIC_INT_CLEAR_OFFSET 0x80 > +#define PCH_PIC_INT_CLEAR_END 0x87 > +#define PCH_PIC_AUTO_CTRL0_OFFSET 0xc0 > +#define PCH_PIC_AUTO_CTRL0_END 0xc7 > +#define PCH_PIC_AUTO_CTRL1_OFFSET 0xe0 > +#define PCH_PIC_AUTO_CTRL1_END 0xe8 > +#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100 > +#define PCH_PIC_ROUTE_ENTRY_END 0x13f > +#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 > +#define PCH_PIC_HTMSI_VEC_END 0x23f > +#define PCH_PIC_INT_STATUS_OFFSET 0x3a0 > +#define PCH_PIC_INT_STATUS_END 0x3a7 > +#define PCH_PIC_INT_POL_OFFSET 0x3e0 > +#define PCH_PIC_INT_POL_END 0x3e7 > + > +typedef struct loongarch_pch_pic { > + SysBusDevice parent_obj; > + qemu_irq parent_irq[64]; > + uint64_t int_mask; /*0x020 interrupt mask register*/ > + uint64_t htmsi_en;/*0x040 1=msi*/ > + uint64_t intedge; /*0x060 edge=1 level =0*/ > + uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ > + uint64_t auto_crtl0; /*0x0c0*/ > + uint64_t auto_crtl1; /*0x0e0*/ > + uint8_t route_entry[64]; /*0x100 - 0x138*/ > + uint8_t htmsi_vector[64]; /*0x200 - 0x238*/ > + uint64_t last_intirr; /* edge detection */ > + uint64_t intirr; /* 0x380 interrupt request register */ > + uint64_t intisr; /* 0x3a0 interrupt service register */ > + /* > + * 0x3e0 interrupt level polarity selection > + * register 0 for high level trigger > + */ > + uint64_t int_polarity; > + MemoryRegion iomem; > +} loongarch_pch_pic; ATB, Mark.
Hi, Mark On 12/18/2021 08:33 AM, Mark Cave-Ayland wrote: > On 04/12/2021 12:07, Xiaojuan Yang wrote: > >> This patch realize the PCH-PIC interrupt controller. >> >> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn> >> Signed-off-by: Song Gao <gaosong@loongson.cn> >> --- >> hw/intc/Kconfig | 4 + >> hw/intc/loongarch_pch_pic.c | 357 ++++++++++++++++++++++++++++ >> hw/intc/meson.build | 1 + >> hw/intc/trace-events | 5 + >> hw/loongarch/Kconfig | 1 + >> include/hw/intc/loongarch_pch_pic.h | 61 +++++ >> 6 files changed, 429 insertions(+) >> create mode 100644 hw/intc/loongarch_pch_pic.c >> create mode 100644 include/hw/intc/loongarch_pch_pic.h >> >> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig >> index 511dcac537..96da13ad1d 100644 >> --- a/hw/intc/Kconfig >> +++ b/hw/intc/Kconfig >> @@ -76,3 +76,7 @@ config M68K_IRQC >> config LOONGARCH_IPI >> bool >> + >> +config LOONGARCH_PCH_PIC >> + bool >> + select UNIMP >> diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c >> new file mode 100644 >> index 0000000000..2ede29ceb0 >> --- /dev/null >> +++ b/hw/intc/loongarch_pch_pic.c >> @@ -0,0 +1,357 @@ >> +/* 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/loongarch.h" >> +#include "hw/irq.h" >> +#include "hw/intc/loongarch_pch_pic.h" >> +#include "migration/vmstate.h" >> +#include "trace.h" >> + >> +#define for_each_set_bit(bit, addr, size) \ >> + for ((bit) = find_first_bit((addr), (size)); \ >> + (bit) < (size); \ >> + (bit) = find_next_bit((addr), (size), (bit) + 1)) >> + >> +static void pch_pic_update_irq(loongarch_pch_pic *s, uint64_t mask, int level) >> +{ >> + int i; >> + uint64_t val; >> + val = mask & s->intirr & (~s->int_mask); >> + >> + for_each_set_bit(i, &val, 64) { >> + if (level == 1) { >> + if ((s->intisr & (0x1ULL << i)) == 0) { >> + s->intisr |= 1ULL << i; >> + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 1); >> + } >> + } else if (level == 0) { >> + if (s->intisr & (0x1ULL << i)) { >> + s->intisr &= ~(0x1ULL << i); >> + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 0); >> + } >> + } >> + } >> +} > > The normal pattern would be to use something like: > > for (i = 0; i < 64; i++) { > if (level) { > s->intisr |= 1ULL << i; > } else { > s->intisr &= ~(0x1ULL << i); > } > > qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], level); > } > > Why is it necessary to check the previous value of (s->intisr & (0x1ULL << i)) here? Here check the previous value to avoid Unnecessary write. It seems make things more complicated. I will modify > >> +static void pch_pic_irq_handler(void *opaque, int irq, int level) >> +{ >> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); >> + >> + assert(irq < PCH_PIC_IRQ_NUM); >> + uint64_t mask = 1ULL << irq; >> + >> + trace_pch_pic_irq_handler(s->intedge, 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_reg_read(void *opaque, hwaddr addr, >> + unsigned size) >> +{ >> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); >> + uint64_t val = 0; >> + uint32_t offset = addr & 0xfff; >> + int64_t offset_tmp; >> + >> + if (size == 8) { >> + switch (offset) { >> + case PCH_PIC_INT_ID_OFFSET: >> + val = (PCH_PIC_INT_ID_NUM << 32) | PCH_PIC_INT_ID_VAL; >> + break; >> + case PCH_PIC_INT_MASK_OFFSET: >> + val = s->int_mask; >> + break; >> + case PCH_PIC_INT_STATUS_OFFSET: >> + val = s->intisr & (~s->int_mask); >> + break; >> + case PCH_PIC_INT_EDGE_OFFSET: >> + val = s->intedge; >> + break; >> + case PCH_PIC_INT_POL_OFFSET: >> + val = s->int_polarity; >> + break; >> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >> + val = s->htmsi_en; >> + break; >> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >> + break; >> + default: >> + break; >> + } >> + } else if (size == 4) { >> + switch (offset) { >> + case PCH_PIC_INT_ID_OFFSET: >> + val = PCH_PIC_INT_ID_VAL; >> + break; >> + case PCH_PIC_INT_ID_OFFSET + 4: >> + val = PCH_PIC_INT_ID_NUM; >> + break; >> + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: >> + val = ldl_p((void *)&s->int_mask + >> + (offset - PCH_PIC_INT_MASK_OFFSET)); >> + break; >> + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: >> + val = ldl_p((void *)&s->intisr + >> + (offset - PCH_PIC_INT_STATUS_OFFSET)) & (~s->int_mask); >> + break; >> + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: >> + val = ldl_p((void *)&s->intedge + >> + (offset - PCH_PIC_INT_EDGE_OFFSET)); >> + break; >> + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: >> + val = ldl_p((void *)&s->int_polarity + >> + (offset - PCH_PIC_INT_POL_OFFSET)); >> + break; >> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >> + val = ldl_p((void *)&s->htmsi_en + >> + (offset - PCH_PIC_HTMSI_EN_OFFSET)); >> + break; >> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >> + break; >> + default: >> + break; >> + } >> + } else if (size == 1) { >> + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { >> + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; >> + if (offset_tmp >= 0 && offset_tmp < 64) { >> + val = s->htmsi_vector[offset_tmp]; >> + } >> + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { >> + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; >> + if (offset_tmp >= 0 && offset_tmp < 64) { >> + val = s->route_entry[offset_tmp]; >> + } >> + } >> + } >> + >> + trace_loongarch_pch_pic_read(size, (uint32_t)addr, val); >> + return val; >> +} >> + >> +static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr, >> + uint64_t data, unsigned size) >> +{ >> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); > > Perhaps use LoongArchPCHPIC for the camel case version? OK, all interrupt controller will use the camel case. > >> + int32_t offset_tmp; >> + uint32_t offset, old; >> + offset = addr & 0xfff; >> + >> + trace_loongarch_pch_pic_write(size, (uint32_t)addr, data); >> + >> + if (size == 8) { >> + switch (offset) { >> + case PCH_PIC_INT_MASK_OFFSET: >> + old = s->int_mask; >> + s->int_mask = data; >> + if (old & ~data) { >> + pch_pic_update_irq(s, (old & ~data), 1); >> + } else if (~old & data) { >> + pch_pic_update_irq(s, (~old & data), 0); >> + } >> + break; >> + case PCH_PIC_INT_STATUS_OFFSET: >> + s->intisr = data; >> + break; >> + case PCH_PIC_INT_EDGE_OFFSET: >> + s->intedge = data; >> + break; >> + case PCH_PIC_INT_CLEAR_OFFSET: >> + s->intirr &= (~(data & s->intedge)); >> + pch_pic_update_irq(s, data, 0); >> + s->intisr &= (~data); >> + break; >> + case PCH_PIC_INT_POL_OFFSET: >> + s->int_polarity = data; >> + break; >> + case PCH_PIC_HTMSI_EN_OFFSET: >> + s->htmsi_en = data; >> + break; >> + case PCH_PIC_AUTO_CTRL0_OFFSET: >> + case PCH_PIC_AUTO_CTRL1_OFFSET: >> + break; >> + default: >> + break; >> + } >> + } else if (size == 4) { >> + switch (offset) { >> + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: >> + offset -= PCH_PIC_INT_MASK_OFFSET; >> + old = ldl_p((void *)&s->int_mask + offset); >> + stl_p((void *)&s->int_mask + offset, data); >> + >> + if (old & ~data) { >> + pch_pic_update_irq(s, (old & ~data), 1); >> + } else if (~old & data) { >> + pch_pic_update_irq(s, (~old & data), 0); >> + } >> + break; >> + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: >> + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_STATUS_OFFSET), >> + data); >> + break; >> + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: >> + stl_p((void *)&s->intedge + (offset - PCH_PIC_INT_EDGE_OFFSET), >> + data); >> + break; >> + case PCH_PIC_INT_CLEAR_OFFSET...PCH_PIC_INT_CLEAR_END: >> + old = s->intirr & (~(data & s->intedge)); >> + stl_p((void *)&s->intirr + (offset - PCH_PIC_INT_CLEAR_OFFSET), >> + old); >> + pch_pic_update_irq(s, data, 0); >> + old = s->intisr & (~data); >> + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_CLEAR_OFFSET), >> + old); >> + break; >> + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: >> + stl_p((void *)&s->int_polarity + (offset - PCH_PIC_INT_POL_OFFSET), >> + data); >> + break; >> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >> + stl_p((void *)&s->htmsi_en + (offset - PCH_PIC_HTMSI_EN_OFFSET), >> + data); >> + break; >> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >> + break; >> + default: >> + break; >> + } >> + } else if (size == 1) { >> + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { >> + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; >> + if (offset_tmp >= 0 && offset_tmp < 64) { >> + s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff); >> + } >> + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { >> + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; >> + if (offset_tmp >= 0 && offset_tmp < 64) { >> + s->route_entry[offset_tmp] = (uint8_t)(data & 0xff); >> + } >> + } >> + } >> +} >> + >> +static const MemoryRegionOps loongarch_pch_pic_ops = { >> + .read = loongarch_pch_pic_reg_read, >> + .write = loongarch_pch_pic_reg_write, >> + .valid = { >> + .min_access_size = 1, >> + .max_access_size = 8, >> + }, >> + .impl = { >> + .min_access_size = 1, >> + .max_access_size = 8, >> + }, >> + .endianness = DEVICE_LITTLE_ENDIAN, >> +}; > > It feels like this should be split into 2 separate memory regions: as per before set .min_access_size = 4 for PCH_PIC_INT_MASK_OFFSET ... PCH_PIC_AUTO_CTRL1_END and use uint32_t for the underlying types in loongarch_pch_pic. > > This leaves a separate memory region for accesses > PCH_PIC_HTMSI_VEC_OFFSET which can be set with .min_access_size = 1 which maps nicely to the underlying uint8_t types. Which means you should be able to access the arrays directly instead of having to use ldl_p() and stl_p(). > yeah, here the PCH_PIC_HTMSI_VEC_OFFSET region is right in the middle, I want to use a separate memory region to overlap the pch-pic region, is it ok to do like this? Thanks > +static void loongarch_pch_pic_reset(DeviceState *d) >> +{ >> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d); >> + int i; >> + >> + s->int_mask = -1ULL; >> + 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) >> +{ >> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(obj); >> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); >> + int i; >> + >> + memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops, >> + s, TYPE_LOONGARCH_PCH_PIC, 0x1000); >> + sysbus_init_mmio(sbd, &s->iomem); >> + >> + for (i = 0; i < PCH_PIC_IRQ_NUM; i++) { >> + sysbus_init_irq(sbd, &s->parent_irq[i]); >> + } >> + 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, loongarch_pch_pic), >> + VMSTATE_UINT64(htmsi_en, loongarch_pch_pic), >> + VMSTATE_UINT64(intedge, loongarch_pch_pic), >> + VMSTATE_UINT64(intclr, loongarch_pch_pic), >> + VMSTATE_UINT64(auto_crtl0, loongarch_pch_pic), >> + VMSTATE_UINT64(auto_crtl1, loongarch_pch_pic), >> + VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 64), >> + VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 64), >> + VMSTATE_UINT64(last_intirr, loongarch_pch_pic), >> + VMSTATE_UINT64(intirr, loongarch_pch_pic), >> + VMSTATE_UINT64(intisr, loongarch_pch_pic), >> + VMSTATE_UINT64(int_polarity, loongarch_pch_pic), >> + 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(loongarch_pch_pic), >> + .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) >> diff --git a/hw/intc/meson.build b/hw/intc/meson.build >> index 51f0c3988a..33ba63266e 100644 >> --- a/hw/intc/meson.build >> +++ b/hw/intc/meson.build >> @@ -58,3 +58,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], >> specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) >> specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) >> specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) >> +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) >> diff --git a/hw/intc/trace-events b/hw/intc/trace-events >> index 124608e51f..52fedf82be 100644 >> --- a/hw/intc/trace-events >> +++ b/hw/intc/trace-events >> @@ -250,3 +250,8 @@ sh_intc_set(int id, int enable) "setting interrupt group %d to %d" >> # loongarch_ipi.c >> loongarch_ipi_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 >> loongarch_ipi_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 >> + >> +# loongarch_pch_pic.c >> +pch_pic_irq_handler(uint32_t edge, int irq, int level) "edge 0x%02x irq %d level %d" >> +loongarch_pch_pic_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 >> +loongarch_pch_pic_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 >> diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig >> index 1591574397..c2b8046b94 100644 >> --- a/hw/loongarch/Kconfig >> +++ b/hw/loongarch/Kconfig >> @@ -2,3 +2,4 @@ config LOONGSON3_LS7A >> bool >> select PCI_EXPRESS_7A >> select LOONGARCH_IPI >> + select LOONGARCH_PCH_PIC >> diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h >> new file mode 100644 >> index 0000000000..bc04ed28ef >> --- /dev/null >> +++ b/include/hw/intc/loongarch_pch_pic.h >> @@ -0,0 +1,61 @@ >> +/* SPDX-License-Identifier: GPL-2.0-or-later */ >> +/* >> + * LoongArch 7A1000 I/O interrupt controller definitions >> + * >> + * Copyright (c) 2021 Loongson Technology Corporation Limited >> + */ >> + >> +#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" >> +DECLARE_INSTANCE_CHECKER(struct loongarch_pch_pic, LOONGARCH_PCH_PIC, >> + TYPE_LOONGARCH_PCH_PIC) >> + >> +#define PCH_PIC_IRQ_START 0 >> +#define PCH_PIC_IRQ_END 63 >> +#define PCH_PIC_IRQ_NUM 64 >> +#define PCH_PIC_INT_ID_VAL 0x7000000UL >> +#define PCH_PIC_INT_ID_NUM 0x3f0001UL >> + >> +#define PCH_PIC_INT_ID_OFFSET 0x00 >> +#define PCH_PIC_INT_ID_END 0x07 >> +#define PCH_PIC_INT_MASK_OFFSET 0x20 >> +#define PCH_PIC_INT_MASK_END 0x27 >> +#define PCH_PIC_HTMSI_EN_OFFSET 0x40 >> +#define PCH_PIC_HTMSI_EN_END 0x47 >> +#define PCH_PIC_INT_EDGE_OFFSET 0x60 >> +#define PCH_PIC_INT_EDGE_END 0x67 >> +#define PCH_PIC_INT_CLEAR_OFFSET 0x80 >> +#define PCH_PIC_INT_CLEAR_END 0x87 >> +#define PCH_PIC_AUTO_CTRL0_OFFSET 0xc0 >> +#define PCH_PIC_AUTO_CTRL0_END 0xc7 >> +#define PCH_PIC_AUTO_CTRL1_OFFSET 0xe0 >> +#define PCH_PIC_AUTO_CTRL1_END 0xe8 >> +#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100 >> +#define PCH_PIC_ROUTE_ENTRY_END 0x13f >> +#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 >> +#define PCH_PIC_HTMSI_VEC_END 0x23f >> +#define PCH_PIC_INT_STATUS_OFFSET 0x3a0 >> +#define PCH_PIC_INT_STATUS_END 0x3a7 >> +#define PCH_PIC_INT_POL_OFFSET 0x3e0 >> +#define PCH_PIC_INT_POL_END 0x3e7 >> + >> +typedef struct loongarch_pch_pic { >> + SysBusDevice parent_obj; >> + qemu_irq parent_irq[64]; >> + uint64_t int_mask; /*0x020 interrupt mask register*/ >> + uint64_t htmsi_en;/*0x040 1=msi*/ >> + uint64_t intedge; /*0x060 edge=1 level =0*/ >> + uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ >> + uint64_t auto_crtl0; /*0x0c0*/ >> + uint64_t auto_crtl1; /*0x0e0*/ >> + uint8_t route_entry[64]; /*0x100 - 0x138*/ >> + uint8_t htmsi_vector[64]; /*0x200 - 0x238*/ >> + uint64_t last_intirr; /* edge detection */ >> + uint64_t intirr; /* 0x380 interrupt request register */ >> + uint64_t intisr; /* 0x3a0 interrupt service register */ >> + /* >> + * 0x3e0 interrupt level polarity selection >> + * register 0 for high level trigger >> + */ >> + uint64_t int_polarity; >> + MemoryRegion iomem; >> +} loongarch_pch_pic; > > > ATB, > > Mark.
On 22/12/2021 02:38, yangxiaojuan wrote: > Hi, Mark > > On 12/18/2021 08:33 AM, Mark Cave-Ayland wrote: >> On 04/12/2021 12:07, Xiaojuan Yang wrote: >> >>> This patch realize the PCH-PIC interrupt controller. >>> >>> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn> >>> Signed-off-by: Song Gao <gaosong@loongson.cn> >>> --- >>> hw/intc/Kconfig | 4 + >>> hw/intc/loongarch_pch_pic.c | 357 ++++++++++++++++++++++++++++ >>> hw/intc/meson.build | 1 + >>> hw/intc/trace-events | 5 + >>> hw/loongarch/Kconfig | 1 + >>> include/hw/intc/loongarch_pch_pic.h | 61 +++++ >>> 6 files changed, 429 insertions(+) >>> create mode 100644 hw/intc/loongarch_pch_pic.c >>> create mode 100644 include/hw/intc/loongarch_pch_pic.h >>> >>> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig >>> index 511dcac537..96da13ad1d 100644 >>> --- a/hw/intc/Kconfig >>> +++ b/hw/intc/Kconfig >>> @@ -76,3 +76,7 @@ config M68K_IRQC >>> config LOONGARCH_IPI >>> bool >>> + >>> +config LOONGARCH_PCH_PIC >>> + bool >>> + select UNIMP >>> diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c >>> new file mode 100644 >>> index 0000000000..2ede29ceb0 >>> --- /dev/null >>> +++ b/hw/intc/loongarch_pch_pic.c >>> @@ -0,0 +1,357 @@ >>> +/* 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/loongarch.h" >>> +#include "hw/irq.h" >>> +#include "hw/intc/loongarch_pch_pic.h" >>> +#include "migration/vmstate.h" >>> +#include "trace.h" >>> + >>> +#define for_each_set_bit(bit, addr, size) \ >>> + for ((bit) = find_first_bit((addr), (size)); \ >>> + (bit) < (size); \ >>> + (bit) = find_next_bit((addr), (size), (bit) + 1)) >>> + >>> +static void pch_pic_update_irq(loongarch_pch_pic *s, uint64_t mask, int level) >>> +{ >>> + int i; >>> + uint64_t val; >>> + val = mask & s->intirr & (~s->int_mask); >>> + >>> + for_each_set_bit(i, &val, 64) { >>> + if (level == 1) { >>> + if ((s->intisr & (0x1ULL << i)) == 0) { >>> + s->intisr |= 1ULL << i; >>> + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 1); >>> + } >>> + } else if (level == 0) { >>> + if (s->intisr & (0x1ULL << i)) { >>> + s->intisr &= ~(0x1ULL << i); >>> + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 0); >>> + } >>> + } >>> + } >>> +} >> >> The normal pattern would be to use something like: >> >> for (i = 0; i < 64; i++) { >> if (level) { >> s->intisr |= 1ULL << i; >> } else { >> s->intisr &= ~(0x1ULL << i); >> } >> >> qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], level); >> } >> >> Why is it necessary to check the previous value of (s->intisr & (0x1ULL << i)) here? > > Here check the previous value to avoid Unnecessary write. It seems make things more complicated. I will modify In general a *_update_irq() function should be fine to propagate the IRQ up to the parent directly: I think this is fine in this case because you are directly manipulating the parent_irq elements rather than using e.g. a priority encoder within this device to raise an IRQ to the CPU. I'm presuming this final prioritisation and delivery is done elsewhere? >>> +static void pch_pic_irq_handler(void *opaque, int irq, int level) >>> +{ >>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); >>> + >>> + assert(irq < PCH_PIC_IRQ_NUM); >>> + uint64_t mask = 1ULL << irq; >>> + >>> + trace_pch_pic_irq_handler(s->intedge, 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_reg_read(void *opaque, hwaddr addr, >>> + unsigned size) >>> +{ >>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); >>> + uint64_t val = 0; >>> + uint32_t offset = addr & 0xfff; >>> + int64_t offset_tmp; >>> + >>> + if (size == 8) { >>> + switch (offset) { >>> + case PCH_PIC_INT_ID_OFFSET: >>> + val = (PCH_PIC_INT_ID_NUM << 32) | PCH_PIC_INT_ID_VAL; >>> + break; >>> + case PCH_PIC_INT_MASK_OFFSET: >>> + val = s->int_mask; >>> + break; >>> + case PCH_PIC_INT_STATUS_OFFSET: >>> + val = s->intisr & (~s->int_mask); >>> + break; >>> + case PCH_PIC_INT_EDGE_OFFSET: >>> + val = s->intedge; >>> + break; >>> + case PCH_PIC_INT_POL_OFFSET: >>> + val = s->int_polarity; >>> + break; >>> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >>> + val = s->htmsi_en; >>> + break; >>> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >>> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >>> + break; >>> + default: >>> + break; >>> + } >>> + } else if (size == 4) { >>> + switch (offset) { >>> + case PCH_PIC_INT_ID_OFFSET: >>> + val = PCH_PIC_INT_ID_VAL; >>> + break; >>> + case PCH_PIC_INT_ID_OFFSET + 4: >>> + val = PCH_PIC_INT_ID_NUM; >>> + break; >>> + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: >>> + val = ldl_p((void *)&s->int_mask + >>> + (offset - PCH_PIC_INT_MASK_OFFSET)); >>> + break; >>> + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: >>> + val = ldl_p((void *)&s->intisr + >>> + (offset - PCH_PIC_INT_STATUS_OFFSET)) & (~s->int_mask); >>> + break; >>> + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: >>> + val = ldl_p((void *)&s->intedge + >>> + (offset - PCH_PIC_INT_EDGE_OFFSET)); >>> + break; >>> + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: >>> + val = ldl_p((void *)&s->int_polarity + >>> + (offset - PCH_PIC_INT_POL_OFFSET)); >>> + break; >>> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >>> + val = ldl_p((void *)&s->htmsi_en + >>> + (offset - PCH_PIC_HTMSI_EN_OFFSET)); >>> + break; >>> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >>> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >>> + break; >>> + default: >>> + break; >>> + } >>> + } else if (size == 1) { >>> + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { >>> + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; >>> + if (offset_tmp >= 0 && offset_tmp < 64) { >>> + val = s->htmsi_vector[offset_tmp]; >>> + } >>> + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { >>> + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; >>> + if (offset_tmp >= 0 && offset_tmp < 64) { >>> + val = s->route_entry[offset_tmp]; >>> + } >>> + } >>> + } >>> + >>> + trace_loongarch_pch_pic_read(size, (uint32_t)addr, val); >>> + return val; >>> +} >>> + >>> +static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr, >>> + uint64_t data, unsigned size) >>> +{ >>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); >> >> Perhaps use LoongArchPCHPIC for the camel case version? > > OK, all interrupt controller will use the camel case. > >> >>> + int32_t offset_tmp; >>> + uint32_t offset, old; >>> + offset = addr & 0xfff; >>> + >>> + trace_loongarch_pch_pic_write(size, (uint32_t)addr, data); >>> + >>> + if (size == 8) { >>> + switch (offset) { >>> + case PCH_PIC_INT_MASK_OFFSET: >>> + old = s->int_mask; >>> + s->int_mask = data; >>> + if (old & ~data) { >>> + pch_pic_update_irq(s, (old & ~data), 1); >>> + } else if (~old & data) { >>> + pch_pic_update_irq(s, (~old & data), 0); >>> + } >>> + break; >>> + case PCH_PIC_INT_STATUS_OFFSET: >>> + s->intisr = data; >>> + break; >>> + case PCH_PIC_INT_EDGE_OFFSET: >>> + s->intedge = data; >>> + break; >>> + case PCH_PIC_INT_CLEAR_OFFSET: >>> + s->intirr &= (~(data & s->intedge)); >>> + pch_pic_update_irq(s, data, 0); >>> + s->intisr &= (~data); >>> + break; >>> + case PCH_PIC_INT_POL_OFFSET: >>> + s->int_polarity = data; >>> + break; >>> + case PCH_PIC_HTMSI_EN_OFFSET: >>> + s->htmsi_en = data; >>> + break; >>> + case PCH_PIC_AUTO_CTRL0_OFFSET: >>> + case PCH_PIC_AUTO_CTRL1_OFFSET: >>> + break; >>> + default: >>> + break; >>> + } >>> + } else if (size == 4) { >>> + switch (offset) { >>> + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: >>> + offset -= PCH_PIC_INT_MASK_OFFSET; >>> + old = ldl_p((void *)&s->int_mask + offset); >>> + stl_p((void *)&s->int_mask + offset, data); >>> + >>> + if (old & ~data) { >>> + pch_pic_update_irq(s, (old & ~data), 1); >>> + } else if (~old & data) { >>> + pch_pic_update_irq(s, (~old & data), 0); >>> + } >>> + break; >>> + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: >>> + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_STATUS_OFFSET), >>> + data); >>> + break; >>> + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: >>> + stl_p((void *)&s->intedge + (offset - PCH_PIC_INT_EDGE_OFFSET), >>> + data); >>> + break; >>> + case PCH_PIC_INT_CLEAR_OFFSET...PCH_PIC_INT_CLEAR_END: >>> + old = s->intirr & (~(data & s->intedge)); >>> + stl_p((void *)&s->intirr + (offset - PCH_PIC_INT_CLEAR_OFFSET), >>> + old); >>> + pch_pic_update_irq(s, data, 0); >>> + old = s->intisr & (~data); >>> + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_CLEAR_OFFSET), >>> + old); >>> + break; >>> + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: >>> + stl_p((void *)&s->int_polarity + (offset - PCH_PIC_INT_POL_OFFSET), >>> + data); >>> + break; >>> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >>> + stl_p((void *)&s->htmsi_en + (offset - PCH_PIC_HTMSI_EN_OFFSET), >>> + data); >>> + break; >>> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >>> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >>> + break; >>> + default: >>> + break; >>> + } >>> + } else if (size == 1) { >>> + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { >>> + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; >>> + if (offset_tmp >= 0 && offset_tmp < 64) { >>> + s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff); >>> + } >>> + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { >>> + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; >>> + if (offset_tmp >= 0 && offset_tmp < 64) { >>> + s->route_entry[offset_tmp] = (uint8_t)(data & 0xff); >>> + } >>> + } >>> + } >>> +} >>> + >>> +static const MemoryRegionOps loongarch_pch_pic_ops = { >>> + .read = loongarch_pch_pic_reg_read, >>> + .write = loongarch_pch_pic_reg_write, >>> + .valid = { >>> + .min_access_size = 1, >>> + .max_access_size = 8, >>> + }, >>> + .impl = { >>> + .min_access_size = 1, >>> + .max_access_size = 8, >>> + }, >>> + .endianness = DEVICE_LITTLE_ENDIAN, >>> +}; >> >> It feels like this should be split into 2 separate memory regions: as per before set .min_access_size = 4 for PCH_PIC_INT_MASK_OFFSET ... PCH_PIC_AUTO_CTRL1_END and use uint32_t for the underlying types in loongarch_pch_pic. >> >> This leaves a separate memory region for accesses > PCH_PIC_HTMSI_VEC_OFFSET which can be set with .min_access_size = 1 which maps nicely to the underlying uint8_t types. Which means you should be able to access the arrays directly instead of having to use ldl_p() and stl_p(). >> > > yeah, here the PCH_PIC_HTMSI_VEC_OFFSET region is right in the middle, I want to use a separate memory region to overlap the pch-pic region, is it ok to do like this? Thanks That should be fine. I'd also use a single container memory region so that you only have to expose a single memory region with sysbus_init_mmio() as below: >> +static void loongarch_pch_pic_reset(DeviceState *d) >>> +{ >>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d); >>> + int i; >>> + >>> + s->int_mask = -1ULL; >>> + 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) >>> +{ >>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(obj); >>> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); >>> + int i; >>> + >>> + memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops, >>> + s, TYPE_LOONGARCH_PCH_PIC, 0x1000); >>> + sysbus_init_mmio(sbd, &s->iomem); Please adjust the example below for your particular case replacing the sizes as appropriate but I would expect it to look something like: memory_region_init(&s->picmem, obj, TYPE_LOONGARCH_PCH_PIC, 0x1000); memory_region_init_io(&s->picintmem, obj, &loongarch_pch_pic_int_ops, s, "pic.int", 0xaaaa); memory_region_add_subregion(&s->picmem, INT_OFFSET, &s->picintmem); memory_region_init_io(&s->picintmem, obj, &loongarch_pch_pic_htmsi_ops, s, "pic.htmsi", 0xbbbb); memory_region_add_subregion(&s->picmem, HTMSI_OFFSET, &s->picintmem); memory_region_init_io(&s->picroutemem, obj, &loongarch_pch_pic_route_ops, s, "pic.route", 0xcccc); memory_region_add_subregion(&s->picmem, ROUTE_OFFSET, &s->picroutemem); sysbus_init_mmio(sbd, &s->picmem); You can then configure each of the *_ops memory regions with the correct .min.access and .max.access sizes accordingly, and each region will show up separately in the output of "info mtree". >>> + for (i = 0; i < PCH_PIC_IRQ_NUM; i++) { >>> + sysbus_init_irq(sbd, &s->parent_irq[i]); >>> + } >>> + 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, loongarch_pch_pic), >>> + VMSTATE_UINT64(htmsi_en, loongarch_pch_pic), >>> + VMSTATE_UINT64(intedge, loongarch_pch_pic), >>> + VMSTATE_UINT64(intclr, loongarch_pch_pic), >>> + VMSTATE_UINT64(auto_crtl0, loongarch_pch_pic), >>> + VMSTATE_UINT64(auto_crtl1, loongarch_pch_pic), >>> + VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 64), >>> + VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 64), >>> + VMSTATE_UINT64(last_intirr, loongarch_pch_pic), >>> + VMSTATE_UINT64(intirr, loongarch_pch_pic), >>> + VMSTATE_UINT64(intisr, loongarch_pch_pic), >>> + VMSTATE_UINT64(int_polarity, loongarch_pch_pic), >>> + 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(loongarch_pch_pic), >>> + .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) >>> diff --git a/hw/intc/meson.build b/hw/intc/meson.build >>> index 51f0c3988a..33ba63266e 100644 >>> --- a/hw/intc/meson.build >>> +++ b/hw/intc/meson.build >>> @@ -58,3 +58,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], >>> specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) >>> specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) >>> specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) >>> +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) >>> diff --git a/hw/intc/trace-events b/hw/intc/trace-events >>> index 124608e51f..52fedf82be 100644 >>> --- a/hw/intc/trace-events >>> +++ b/hw/intc/trace-events >>> @@ -250,3 +250,8 @@ sh_intc_set(int id, int enable) "setting interrupt group %d to %d" >>> # loongarch_ipi.c >>> loongarch_ipi_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 >>> loongarch_ipi_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 >>> + >>> +# loongarch_pch_pic.c >>> +pch_pic_irq_handler(uint32_t edge, int irq, int level) "edge 0x%02x irq %d level %d" >>> +loongarch_pch_pic_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 >>> +loongarch_pch_pic_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 >>> diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig >>> index 1591574397..c2b8046b94 100644 >>> --- a/hw/loongarch/Kconfig >>> +++ b/hw/loongarch/Kconfig >>> @@ -2,3 +2,4 @@ config LOONGSON3_LS7A >>> bool >>> select PCI_EXPRESS_7A >>> select LOONGARCH_IPI >>> + select LOONGARCH_PCH_PIC >>> diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h >>> new file mode 100644 >>> index 0000000000..bc04ed28ef >>> --- /dev/null >>> +++ b/include/hw/intc/loongarch_pch_pic.h >>> @@ -0,0 +1,61 @@ >>> +/* SPDX-License-Identifier: GPL-2.0-or-later */ >>> +/* >>> + * LoongArch 7A1000 I/O interrupt controller definitions >>> + * >>> + * Copyright (c) 2021 Loongson Technology Corporation Limited >>> + */ >>> + >>> +#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" >>> +DECLARE_INSTANCE_CHECKER(struct loongarch_pch_pic, LOONGARCH_PCH_PIC, >>> + TYPE_LOONGARCH_PCH_PIC) >>> + >>> +#define PCH_PIC_IRQ_START 0 >>> +#define PCH_PIC_IRQ_END 63 >>> +#define PCH_PIC_IRQ_NUM 64 >>> +#define PCH_PIC_INT_ID_VAL 0x7000000UL >>> +#define PCH_PIC_INT_ID_NUM 0x3f0001UL >>> + >>> +#define PCH_PIC_INT_ID_OFFSET 0x00 >>> +#define PCH_PIC_INT_ID_END 0x07 >>> +#define PCH_PIC_INT_MASK_OFFSET 0x20 >>> +#define PCH_PIC_INT_MASK_END 0x27 >>> +#define PCH_PIC_HTMSI_EN_OFFSET 0x40 >>> +#define PCH_PIC_HTMSI_EN_END 0x47 >>> +#define PCH_PIC_INT_EDGE_OFFSET 0x60 >>> +#define PCH_PIC_INT_EDGE_END 0x67 >>> +#define PCH_PIC_INT_CLEAR_OFFSET 0x80 >>> +#define PCH_PIC_INT_CLEAR_END 0x87 >>> +#define PCH_PIC_AUTO_CTRL0_OFFSET 0xc0 >>> +#define PCH_PIC_AUTO_CTRL0_END 0xc7 >>> +#define PCH_PIC_AUTO_CTRL1_OFFSET 0xe0 >>> +#define PCH_PIC_AUTO_CTRL1_END 0xe8 >>> +#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100 >>> +#define PCH_PIC_ROUTE_ENTRY_END 0x13f >>> +#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 >>> +#define PCH_PIC_HTMSI_VEC_END 0x23f >>> +#define PCH_PIC_INT_STATUS_OFFSET 0x3a0 >>> +#define PCH_PIC_INT_STATUS_END 0x3a7 >>> +#define PCH_PIC_INT_POL_OFFSET 0x3e0 >>> +#define PCH_PIC_INT_POL_END 0x3e7 >>> + >>> +typedef struct loongarch_pch_pic { >>> + SysBusDevice parent_obj; >>> + qemu_irq parent_irq[64]; >>> + uint64_t int_mask; /*0x020 interrupt mask register*/ >>> + uint64_t htmsi_en;/*0x040 1=msi*/ >>> + uint64_t intedge; /*0x060 edge=1 level =0*/ >>> + uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ >>> + uint64_t auto_crtl0; /*0x0c0*/ >>> + uint64_t auto_crtl1; /*0x0e0*/ >>> + uint8_t route_entry[64]; /*0x100 - 0x138*/ >>> + uint8_t htmsi_vector[64]; /*0x200 - 0x238*/ >>> + uint64_t last_intirr; /* edge detection */ >>> + uint64_t intirr; /* 0x380 interrupt request register */ >>> + uint64_t intisr; /* 0x3a0 interrupt service register */ >>> + /* >>> + * 0x3e0 interrupt level polarity selection >>> + * register 0 for high level trigger >>> + */ >>> + uint64_t int_polarity; >>> + MemoryRegion iomem; >>> +} loongarch_pch_pic; ATB, Mark.
Hi,Mark: Sorry for the late reply. I just saw the mail after I send the v4 patch. I sorted the mail into different folders from the qemu-devel, so I didn't see the mail in time. Sorry again. Xiaojuan On 12/23/2021 06:21 PM, Mark Cave-Ayland wrote: > On 22/12/2021 02:38, yangxiaojuan wrote: > >> Hi, Mark >> >> On 12/18/2021 08:33 AM, Mark Cave-Ayland wrote: >>> On 04/12/2021 12:07, Xiaojuan Yang wrote: >>> >>>> This patch realize the PCH-PIC interrupt controller. >>>> >>>> Signed-off-by: Xiaojuan Yang <yangxiaojuan@loongson.cn> >>>> Signed-off-by: Song Gao <gaosong@loongson.cn> >>>> --- >>>> hw/intc/Kconfig | 4 + >>>> hw/intc/loongarch_pch_pic.c | 357 ++++++++++++++++++++++++++++ >>>> hw/intc/meson.build | 1 + >>>> hw/intc/trace-events | 5 + >>>> hw/loongarch/Kconfig | 1 + >>>> include/hw/intc/loongarch_pch_pic.h | 61 +++++ >>>> 6 files changed, 429 insertions(+) >>>> create mode 100644 hw/intc/loongarch_pch_pic.c >>>> create mode 100644 include/hw/intc/loongarch_pch_pic.h >>>> >>>> diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig >>>> index 511dcac537..96da13ad1d 100644 >>>> --- a/hw/intc/Kconfig >>>> +++ b/hw/intc/Kconfig >>>> @@ -76,3 +76,7 @@ config M68K_IRQC >>>> config LOONGARCH_IPI >>>> bool >>>> + >>>> +config LOONGARCH_PCH_PIC >>>> + bool >>>> + select UNIMP >>>> diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c >>>> new file mode 100644 >>>> index 0000000000..2ede29ceb0 >>>> --- /dev/null >>>> +++ b/hw/intc/loongarch_pch_pic.c >>>> @@ -0,0 +1,357 @@ >>>> +/* 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/loongarch.h" >>>> +#include "hw/irq.h" >>>> +#include "hw/intc/loongarch_pch_pic.h" >>>> +#include "migration/vmstate.h" >>>> +#include "trace.h" >>>> + >>>> +#define for_each_set_bit(bit, addr, size) \ >>>> + for ((bit) = find_first_bit((addr), (size)); \ >>>> + (bit) < (size); \ >>>> + (bit) = find_next_bit((addr), (size), (bit) + 1)) >>>> + >>>> +static void pch_pic_update_irq(loongarch_pch_pic *s, uint64_t mask, int level) >>>> +{ >>>> + int i; >>>> + uint64_t val; >>>> + val = mask & s->intirr & (~s->int_mask); >>>> + >>>> + for_each_set_bit(i, &val, 64) { >>>> + if (level == 1) { >>>> + if ((s->intisr & (0x1ULL << i)) == 0) { >>>> + s->intisr |= 1ULL << i; >>>> + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 1); >>>> + } >>>> + } else if (level == 0) { >>>> + if (s->intisr & (0x1ULL << i)) { >>>> + s->intisr &= ~(0x1ULL << i); >>>> + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 0); >>>> + } >>>> + } >>>> + } >>>> +} >>> >>> The normal pattern would be to use something like: >>> >>> for (i = 0; i < 64; i++) { >>> if (level) { >>> s->intisr |= 1ULL << i; >>> } else { >>> s->intisr &= ~(0x1ULL << i); >>> } >>> >>> qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], level); >>> } >>> >>> Why is it necessary to check the previous value of (s->intisr & (0x1ULL << i)) here? >> >> Here check the previous value to avoid Unnecessary write. It seems make things more complicated. I will modify > > In general a *_update_irq() function should be fine to propagate the IRQ up to the parent directly: I think this is fine in this case because you are directly manipulating the parent_irq elements rather than using e.g. a priority encoder within this device to raise an IRQ to the CPU. I'm presuming this final prioritisation and delivery is done elsewhere? > >>>> +static void pch_pic_irq_handler(void *opaque, int irq, int level) >>>> +{ >>>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); >>>> + >>>> + assert(irq < PCH_PIC_IRQ_NUM); >>>> + uint64_t mask = 1ULL << irq; >>>> + >>>> + trace_pch_pic_irq_handler(s->intedge, 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_reg_read(void *opaque, hwaddr addr, >>>> + unsigned size) >>>> +{ >>>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); >>>> + uint64_t val = 0; >>>> + uint32_t offset = addr & 0xfff; >>>> + int64_t offset_tmp; >>>> + >>>> + if (size == 8) { >>>> + switch (offset) { >>>> + case PCH_PIC_INT_ID_OFFSET: >>>> + val = (PCH_PIC_INT_ID_NUM << 32) | PCH_PIC_INT_ID_VAL; >>>> + break; >>>> + case PCH_PIC_INT_MASK_OFFSET: >>>> + val = s->int_mask; >>>> + break; >>>> + case PCH_PIC_INT_STATUS_OFFSET: >>>> + val = s->intisr & (~s->int_mask); >>>> + break; >>>> + case PCH_PIC_INT_EDGE_OFFSET: >>>> + val = s->intedge; >>>> + break; >>>> + case PCH_PIC_INT_POL_OFFSET: >>>> + val = s->int_polarity; >>>> + break; >>>> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >>>> + val = s->htmsi_en; >>>> + break; >>>> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >>>> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >>>> + break; >>>> + default: >>>> + break; >>>> + } >>>> + } else if (size == 4) { >>>> + switch (offset) { >>>> + case PCH_PIC_INT_ID_OFFSET: >>>> + val = PCH_PIC_INT_ID_VAL; >>>> + break; >>>> + case PCH_PIC_INT_ID_OFFSET + 4: >>>> + val = PCH_PIC_INT_ID_NUM; >>>> + break; >>>> + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: >>>> + val = ldl_p((void *)&s->int_mask + >>>> + (offset - PCH_PIC_INT_MASK_OFFSET)); >>>> + break; >>>> + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: >>>> + val = ldl_p((void *)&s->intisr + >>>> + (offset - PCH_PIC_INT_STATUS_OFFSET)) & (~s->int_mask); >>>> + break; >>>> + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: >>>> + val = ldl_p((void *)&s->intedge + >>>> + (offset - PCH_PIC_INT_EDGE_OFFSET)); >>>> + break; >>>> + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: >>>> + val = ldl_p((void *)&s->int_polarity + >>>> + (offset - PCH_PIC_INT_POL_OFFSET)); >>>> + break; >>>> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >>>> + val = ldl_p((void *)&s->htmsi_en + >>>> + (offset - PCH_PIC_HTMSI_EN_OFFSET)); >>>> + break; >>>> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >>>> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >>>> + break; >>>> + default: >>>> + break; >>>> + } >>>> + } else if (size == 1) { >>>> + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { >>>> + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; >>>> + if (offset_tmp >= 0 && offset_tmp < 64) { >>>> + val = s->htmsi_vector[offset_tmp]; >>>> + } >>>> + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { >>>> + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; >>>> + if (offset_tmp >= 0 && offset_tmp < 64) { >>>> + val = s->route_entry[offset_tmp]; >>>> + } >>>> + } >>>> + } >>>> + >>>> + trace_loongarch_pch_pic_read(size, (uint32_t)addr, val); >>>> + return val; >>>> +} >>>> + >>>> +static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr, >>>> + uint64_t data, unsigned size) >>>> +{ >>>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); >>> >>> Perhaps use LoongArchPCHPIC for the camel case version? >> >> OK, all interrupt controller will use the camel case. >> >>> >>>> + int32_t offset_tmp; >>>> + uint32_t offset, old; >>>> + offset = addr & 0xfff; >>>> + >>>> + trace_loongarch_pch_pic_write(size, (uint32_t)addr, data); >>>> + >>>> + if (size == 8) { >>>> + switch (offset) { >>>> + case PCH_PIC_INT_MASK_OFFSET: >>>> + old = s->int_mask; >>>> + s->int_mask = data; >>>> + if (old & ~data) { >>>> + pch_pic_update_irq(s, (old & ~data), 1); >>>> + } else if (~old & data) { >>>> + pch_pic_update_irq(s, (~old & data), 0); >>>> + } >>>> + break; >>>> + case PCH_PIC_INT_STATUS_OFFSET: >>>> + s->intisr = data; >>>> + break; >>>> + case PCH_PIC_INT_EDGE_OFFSET: >>>> + s->intedge = data; >>>> + break; >>>> + case PCH_PIC_INT_CLEAR_OFFSET: >>>> + s->intirr &= (~(data & s->intedge)); >>>> + pch_pic_update_irq(s, data, 0); >>>> + s->intisr &= (~data); >>>> + break; >>>> + case PCH_PIC_INT_POL_OFFSET: >>>> + s->int_polarity = data; >>>> + break; >>>> + case PCH_PIC_HTMSI_EN_OFFSET: >>>> + s->htmsi_en = data; >>>> + break; >>>> + case PCH_PIC_AUTO_CTRL0_OFFSET: >>>> + case PCH_PIC_AUTO_CTRL1_OFFSET: >>>> + break; >>>> + default: >>>> + break; >>>> + } >>>> + } else if (size == 4) { >>>> + switch (offset) { >>>> + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: >>>> + offset -= PCH_PIC_INT_MASK_OFFSET; >>>> + old = ldl_p((void *)&s->int_mask + offset); >>>> + stl_p((void *)&s->int_mask + offset, data); >>>> + >>>> + if (old & ~data) { >>>> + pch_pic_update_irq(s, (old & ~data), 1); >>>> + } else if (~old & data) { >>>> + pch_pic_update_irq(s, (~old & data), 0); >>>> + } >>>> + break; >>>> + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: >>>> + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_STATUS_OFFSET), >>>> + data); >>>> + break; >>>> + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: >>>> + stl_p((void *)&s->intedge + (offset - PCH_PIC_INT_EDGE_OFFSET), >>>> + data); >>>> + break; >>>> + case PCH_PIC_INT_CLEAR_OFFSET...PCH_PIC_INT_CLEAR_END: >>>> + old = s->intirr & (~(data & s->intedge)); >>>> + stl_p((void *)&s->intirr + (offset - PCH_PIC_INT_CLEAR_OFFSET), >>>> + old); >>>> + pch_pic_update_irq(s, data, 0); >>>> + old = s->intisr & (~data); >>>> + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_CLEAR_OFFSET), >>>> + old); >>>> + break; >>>> + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: >>>> + stl_p((void *)&s->int_polarity + (offset - PCH_PIC_INT_POL_OFFSET), >>>> + data); >>>> + break; >>>> + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: >>>> + stl_p((void *)&s->htmsi_en + (offset - PCH_PIC_HTMSI_EN_OFFSET), >>>> + data); >>>> + break; >>>> + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: >>>> + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: >>>> + break; >>>> + default: >>>> + break; >>>> + } >>>> + } else if (size == 1) { >>>> + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { >>>> + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; >>>> + if (offset_tmp >= 0 && offset_tmp < 64) { >>>> + s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff); >>>> + } >>>> + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { >>>> + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; >>>> + if (offset_tmp >= 0 && offset_tmp < 64) { >>>> + s->route_entry[offset_tmp] = (uint8_t)(data & 0xff); >>>> + } >>>> + } >>>> + } >>>> +} >>>> + >>>> +static const MemoryRegionOps loongarch_pch_pic_ops = { >>>> + .read = loongarch_pch_pic_reg_read, >>>> + .write = loongarch_pch_pic_reg_write, >>>> + .valid = { >>>> + .min_access_size = 1, >>>> + .max_access_size = 8, >>>> + }, >>>> + .impl = { >>>> + .min_access_size = 1, >>>> + .max_access_size = 8, >>>> + }, >>>> + .endianness = DEVICE_LITTLE_ENDIAN, >>>> +}; >>> >>> It feels like this should be split into 2 separate memory regions: as per before set .min_access_size = 4 for PCH_PIC_INT_MASK_OFFSET ... PCH_PIC_AUTO_CTRL1_END and use uint32_t for the underlying types in loongarch_pch_pic. >>> >>> This leaves a separate memory region for accesses > PCH_PIC_HTMSI_VEC_OFFSET which can be set with .min_access_size = 1 which maps nicely to the underlying uint8_t types. Which means you should be able to access the arrays directly instead of having to use ldl_p() and stl_p(). >>> >> >> yeah, here the PCH_PIC_HTMSI_VEC_OFFSET region is right in the middle, I want to use a separate memory region to overlap the pch-pic region, is it ok to do like this? Thanks > > That should be fine. I'd also use a single container memory region so that you only have to expose a single memory region with sysbus_init_mmio() as below: > >>> +static void loongarch_pch_pic_reset(DeviceState *d) >>>> +{ >>>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d); >>>> + int i; >>>> + >>>> + s->int_mask = -1ULL; >>>> + 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) >>>> +{ >>>> + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(obj); >>>> + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); >>>> + int i; >>>> + >>>> + memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops, >>>> + s, TYPE_LOONGARCH_PCH_PIC, 0x1000); >>>> + sysbus_init_mmio(sbd, &s->iomem); > > Please adjust the example below for your particular case replacing the sizes as appropriate but I would expect it to look something like: > > memory_region_init(&s->picmem, obj, TYPE_LOONGARCH_PCH_PIC, 0x1000); > > memory_region_init_io(&s->picintmem, obj, &loongarch_pch_pic_int_ops, > s, "pic.int", 0xaaaa); > memory_region_add_subregion(&s->picmem, INT_OFFSET, &s->picintmem); > > memory_region_init_io(&s->picintmem, obj, &loongarch_pch_pic_htmsi_ops, > s, "pic.htmsi", 0xbbbb); > memory_region_add_subregion(&s->picmem, HTMSI_OFFSET, &s->picintmem); > > memory_region_init_io(&s->picroutemem, obj, &loongarch_pch_pic_route_ops, > s, "pic.route", 0xcccc); > memory_region_add_subregion(&s->picmem, ROUTE_OFFSET, &s->picroutemem); > > sysbus_init_mmio(sbd, &s->picmem); > > You can then configure each of the *_ops memory regions with the correct .min.access and .max.access sizes accordingly, and each region will show up separately in the output of "info mtree". > >>>> + for (i = 0; i < PCH_PIC_IRQ_NUM; i++) { >>>> + sysbus_init_irq(sbd, &s->parent_irq[i]); >>>> + } >>>> + 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, loongarch_pch_pic), >>>> + VMSTATE_UINT64(htmsi_en, loongarch_pch_pic), >>>> + VMSTATE_UINT64(intedge, loongarch_pch_pic), >>>> + VMSTATE_UINT64(intclr, loongarch_pch_pic), >>>> + VMSTATE_UINT64(auto_crtl0, loongarch_pch_pic), >>>> + VMSTATE_UINT64(auto_crtl1, loongarch_pch_pic), >>>> + VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 64), >>>> + VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 64), >>>> + VMSTATE_UINT64(last_intirr, loongarch_pch_pic), >>>> + VMSTATE_UINT64(intirr, loongarch_pch_pic), >>>> + VMSTATE_UINT64(intisr, loongarch_pch_pic), >>>> + VMSTATE_UINT64(int_polarity, loongarch_pch_pic), >>>> + 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(loongarch_pch_pic), >>>> + .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) >>>> diff --git a/hw/intc/meson.build b/hw/intc/meson.build >>>> index 51f0c3988a..33ba63266e 100644 >>>> --- a/hw/intc/meson.build >>>> +++ b/hw/intc/meson.build >>>> @@ -58,3 +58,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], >>>> specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) >>>> specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) >>>> specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) >>>> +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) >>>> diff --git a/hw/intc/trace-events b/hw/intc/trace-events >>>> index 124608e51f..52fedf82be 100644 >>>> --- a/hw/intc/trace-events >>>> +++ b/hw/intc/trace-events >>>> @@ -250,3 +250,8 @@ sh_intc_set(int id, int enable) "setting interrupt group %d to %d" >>>> # loongarch_ipi.c >>>> loongarch_ipi_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 >>>> loongarch_ipi_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 >>>> + >>>> +# loongarch_pch_pic.c >>>> +pch_pic_irq_handler(uint32_t edge, int irq, int level) "edge 0x%02x irq %d level %d" >>>> +loongarch_pch_pic_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 >>>> +loongarch_pch_pic_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 >>>> diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig >>>> index 1591574397..c2b8046b94 100644 >>>> --- a/hw/loongarch/Kconfig >>>> +++ b/hw/loongarch/Kconfig >>>> @@ -2,3 +2,4 @@ config LOONGSON3_LS7A >>>> bool >>>> select PCI_EXPRESS_7A >>>> select LOONGARCH_IPI >>>> + select LOONGARCH_PCH_PIC >>>> diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h >>>> new file mode 100644 >>>> index 0000000000..bc04ed28ef >>>> --- /dev/null >>>> +++ b/include/hw/intc/loongarch_pch_pic.h >>>> @@ -0,0 +1,61 @@ >>>> +/* SPDX-License-Identifier: GPL-2.0-or-later */ >>>> +/* >>>> + * LoongArch 7A1000 I/O interrupt controller definitions >>>> + * >>>> + * Copyright (c) 2021 Loongson Technology Corporation Limited >>>> + */ >>>> + >>>> +#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" >>>> +DECLARE_INSTANCE_CHECKER(struct loongarch_pch_pic, LOONGARCH_PCH_PIC, >>>> + TYPE_LOONGARCH_PCH_PIC) >>>> + >>>> +#define PCH_PIC_IRQ_START 0 >>>> +#define PCH_PIC_IRQ_END 63 >>>> +#define PCH_PIC_IRQ_NUM 64 >>>> +#define PCH_PIC_INT_ID_VAL 0x7000000UL >>>> +#define PCH_PIC_INT_ID_NUM 0x3f0001UL >>>> + >>>> +#define PCH_PIC_INT_ID_OFFSET 0x00 >>>> +#define PCH_PIC_INT_ID_END 0x07 >>>> +#define PCH_PIC_INT_MASK_OFFSET 0x20 >>>> +#define PCH_PIC_INT_MASK_END 0x27 >>>> +#define PCH_PIC_HTMSI_EN_OFFSET 0x40 >>>> +#define PCH_PIC_HTMSI_EN_END 0x47 >>>> +#define PCH_PIC_INT_EDGE_OFFSET 0x60 >>>> +#define PCH_PIC_INT_EDGE_END 0x67 >>>> +#define PCH_PIC_INT_CLEAR_OFFSET 0x80 >>>> +#define PCH_PIC_INT_CLEAR_END 0x87 >>>> +#define PCH_PIC_AUTO_CTRL0_OFFSET 0xc0 >>>> +#define PCH_PIC_AUTO_CTRL0_END 0xc7 >>>> +#define PCH_PIC_AUTO_CTRL1_OFFSET 0xe0 >>>> +#define PCH_PIC_AUTO_CTRL1_END 0xe8 >>>> +#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100 >>>> +#define PCH_PIC_ROUTE_ENTRY_END 0x13f >>>> +#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 >>>> +#define PCH_PIC_HTMSI_VEC_END 0x23f >>>> +#define PCH_PIC_INT_STATUS_OFFSET 0x3a0 >>>> +#define PCH_PIC_INT_STATUS_END 0x3a7 >>>> +#define PCH_PIC_INT_POL_OFFSET 0x3e0 >>>> +#define PCH_PIC_INT_POL_END 0x3e7 >>>> + >>>> +typedef struct loongarch_pch_pic { >>>> + SysBusDevice parent_obj; >>>> + qemu_irq parent_irq[64]; >>>> + uint64_t int_mask; /*0x020 interrupt mask register*/ >>>> + uint64_t htmsi_en;/*0x040 1=msi*/ >>>> + uint64_t intedge; /*0x060 edge=1 level =0*/ >>>> + uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ >>>> + uint64_t auto_crtl0; /*0x0c0*/ >>>> + uint64_t auto_crtl1; /*0x0e0*/ >>>> + uint8_t route_entry[64]; /*0x100 - 0x138*/ >>>> + uint8_t htmsi_vector[64]; /*0x200 - 0x238*/ >>>> + uint64_t last_intirr; /* edge detection */ >>>> + uint64_t intirr; /* 0x380 interrupt request register */ >>>> + uint64_t intisr; /* 0x3a0 interrupt service register */ >>>> + /* >>>> + * 0x3e0 interrupt level polarity selection >>>> + * register 0 for high level trigger >>>> + */ >>>> + uint64_t int_polarity; >>>> + MemoryRegion iomem; >>>> +} loongarch_pch_pic; > > ATB, > > Mark.
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 511dcac537..96da13ad1d 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -76,3 +76,7 @@ config M68K_IRQC config LOONGARCH_IPI bool + +config LOONGARCH_PCH_PIC + bool + select UNIMP diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c new file mode 100644 index 0000000000..2ede29ceb0 --- /dev/null +++ b/hw/intc/loongarch_pch_pic.c @@ -0,0 +1,357 @@ +/* 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/loongarch.h" +#include "hw/irq.h" +#include "hw/intc/loongarch_pch_pic.h" +#include "migration/vmstate.h" +#include "trace.h" + +#define for_each_set_bit(bit, addr, size) \ + for ((bit) = find_first_bit((addr), (size)); \ + (bit) < (size); \ + (bit) = find_next_bit((addr), (size), (bit) + 1)) + +static void pch_pic_update_irq(loongarch_pch_pic *s, uint64_t mask, int level) +{ + int i; + uint64_t val; + val = mask & s->intirr & (~s->int_mask); + + for_each_set_bit(i, &val, 64) { + if (level == 1) { + if ((s->intisr & (0x1ULL << i)) == 0) { + s->intisr |= 1ULL << i; + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 1); + } + } else if (level == 0) { + if (s->intisr & (0x1ULL << i)) { + s->intisr &= ~(0x1ULL << i); + qemu_set_irq(s->parent_irq[s->htmsi_vector[i]], 0); + } + } + } +} + +static void pch_pic_irq_handler(void *opaque, int irq, int level) +{ + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); + + assert(irq < PCH_PIC_IRQ_NUM); + uint64_t mask = 1ULL << irq; + + trace_pch_pic_irq_handler(s->intedge, 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_reg_read(void *opaque, hwaddr addr, + unsigned size) +{ + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); + uint64_t val = 0; + uint32_t offset = addr & 0xfff; + int64_t offset_tmp; + + if (size == 8) { + switch (offset) { + case PCH_PIC_INT_ID_OFFSET: + val = (PCH_PIC_INT_ID_NUM << 32) | PCH_PIC_INT_ID_VAL; + break; + case PCH_PIC_INT_MASK_OFFSET: + val = s->int_mask; + break; + case PCH_PIC_INT_STATUS_OFFSET: + val = s->intisr & (~s->int_mask); + break; + case PCH_PIC_INT_EDGE_OFFSET: + val = s->intedge; + break; + case PCH_PIC_INT_POL_OFFSET: + val = s->int_polarity; + break; + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: + val = s->htmsi_en; + break; + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: + break; + default: + break; + } + } else if (size == 4) { + switch (offset) { + case PCH_PIC_INT_ID_OFFSET: + val = PCH_PIC_INT_ID_VAL; + break; + case PCH_PIC_INT_ID_OFFSET + 4: + val = PCH_PIC_INT_ID_NUM; + break; + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: + val = ldl_p((void *)&s->int_mask + + (offset - PCH_PIC_INT_MASK_OFFSET)); + break; + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: + val = ldl_p((void *)&s->intisr + + (offset - PCH_PIC_INT_STATUS_OFFSET)) & (~s->int_mask); + break; + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: + val = ldl_p((void *)&s->intedge + + (offset - PCH_PIC_INT_EDGE_OFFSET)); + break; + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: + val = ldl_p((void *)&s->int_polarity + + (offset - PCH_PIC_INT_POL_OFFSET)); + break; + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: + val = ldl_p((void *)&s->htmsi_en + + (offset - PCH_PIC_HTMSI_EN_OFFSET)); + break; + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: + break; + default: + break; + } + } else if (size == 1) { + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + val = s->htmsi_vector[offset_tmp]; + } + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + val = s->route_entry[offset_tmp]; + } + } + } + + trace_loongarch_pch_pic_read(size, (uint32_t)addr, val); + return val; +} + +static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); + int32_t offset_tmp; + uint32_t offset, old; + offset = addr & 0xfff; + + trace_loongarch_pch_pic_write(size, (uint32_t)addr, data); + + if (size == 8) { + switch (offset) { + case PCH_PIC_INT_MASK_OFFSET: + old = s->int_mask; + s->int_mask = data; + if (old & ~data) { + pch_pic_update_irq(s, (old & ~data), 1); + } else if (~old & data) { + pch_pic_update_irq(s, (~old & data), 0); + } + break; + case PCH_PIC_INT_STATUS_OFFSET: + s->intisr = data; + break; + case PCH_PIC_INT_EDGE_OFFSET: + s->intedge = data; + break; + case PCH_PIC_INT_CLEAR_OFFSET: + s->intirr &= (~(data & s->intedge)); + pch_pic_update_irq(s, data, 0); + s->intisr &= (~data); + break; + case PCH_PIC_INT_POL_OFFSET: + s->int_polarity = data; + break; + case PCH_PIC_HTMSI_EN_OFFSET: + s->htmsi_en = data; + break; + case PCH_PIC_AUTO_CTRL0_OFFSET: + case PCH_PIC_AUTO_CTRL1_OFFSET: + break; + default: + break; + } + } else if (size == 4) { + switch (offset) { + case PCH_PIC_INT_MASK_OFFSET...PCH_PIC_INT_MASK_END: + offset -= PCH_PIC_INT_MASK_OFFSET; + old = ldl_p((void *)&s->int_mask + offset); + stl_p((void *)&s->int_mask + offset, data); + + if (old & ~data) { + pch_pic_update_irq(s, (old & ~data), 1); + } else if (~old & data) { + pch_pic_update_irq(s, (~old & data), 0); + } + break; + case PCH_PIC_INT_STATUS_OFFSET...PCH_PIC_INT_STATUS_END: + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_STATUS_OFFSET), + data); + break; + case PCH_PIC_INT_EDGE_OFFSET...PCH_PIC_INT_EDGE_END: + stl_p((void *)&s->intedge + (offset - PCH_PIC_INT_EDGE_OFFSET), + data); + break; + case PCH_PIC_INT_CLEAR_OFFSET...PCH_PIC_INT_CLEAR_END: + old = s->intirr & (~(data & s->intedge)); + stl_p((void *)&s->intirr + (offset - PCH_PIC_INT_CLEAR_OFFSET), + old); + pch_pic_update_irq(s, data, 0); + old = s->intisr & (~data); + stl_p((void *)&s->intisr + (offset - PCH_PIC_INT_CLEAR_OFFSET), + old); + break; + case PCH_PIC_INT_POL_OFFSET...PCH_PIC_INT_POL_END: + stl_p((void *)&s->int_polarity + (offset - PCH_PIC_INT_POL_OFFSET), + data); + break; + case PCH_PIC_HTMSI_EN_OFFSET...PCH_PIC_HTMSI_EN_END: + stl_p((void *)&s->htmsi_en + (offset - PCH_PIC_HTMSI_EN_OFFSET), + data); + break; + case PCH_PIC_AUTO_CTRL0_OFFSET...PCH_PIC_AUTO_CTRL0_END: + case PCH_PIC_AUTO_CTRL1_OFFSET...PCH_PIC_AUTO_CTRL1_END: + break; + default: + break; + } + } else if (size == 1) { + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff); + } + } else if (offset >= PCH_PIC_ROUTE_ENTRY_OFFSET) { + offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 64) { + s->route_entry[offset_tmp] = (uint8_t)(data & 0xff); + } + } + } +} + +static const MemoryRegionOps loongarch_pch_pic_ops = { + .read = loongarch_pch_pic_reg_read, + .write = loongarch_pch_pic_reg_write, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void loongarch_pch_pic_reset(DeviceState *d) +{ + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d); + int i; + + s->int_mask = -1ULL; + 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) +{ + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + int i; + + memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops, + s, TYPE_LOONGARCH_PCH_PIC, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + + for (i = 0; i < PCH_PIC_IRQ_NUM; i++) { + sysbus_init_irq(sbd, &s->parent_irq[i]); + } + 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, loongarch_pch_pic), + VMSTATE_UINT64(htmsi_en, loongarch_pch_pic), + VMSTATE_UINT64(intedge, loongarch_pch_pic), + VMSTATE_UINT64(intclr, loongarch_pch_pic), + VMSTATE_UINT64(auto_crtl0, loongarch_pch_pic), + VMSTATE_UINT64(auto_crtl1, loongarch_pch_pic), + VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 64), + VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 64), + VMSTATE_UINT64(last_intirr, loongarch_pch_pic), + VMSTATE_UINT64(intirr, loongarch_pch_pic), + VMSTATE_UINT64(intisr, loongarch_pch_pic), + VMSTATE_UINT64(int_polarity, loongarch_pch_pic), + 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(loongarch_pch_pic), + .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) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 51f0c3988a..33ba63266e 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -58,3 +58,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], specific_ss.add(when: 'CONFIG_GOLDFISH_PIC', if_true: files('goldfish_pic.c')) specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) +specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 124608e51f..52fedf82be 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -250,3 +250,8 @@ sh_intc_set(int id, int enable) "setting interrupt group %d to %d" # loongarch_ipi.c loongarch_ipi_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 loongarch_ipi_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%"PRIx64 + +# loongarch_pch_pic.c +pch_pic_irq_handler(uint32_t edge, int irq, int level) "edge 0x%02x irq %d level %d" +loongarch_pch_pic_read(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 +loongarch_pch_pic_write(unsigned size, uint32_t addr, unsigned long val) "size: %u addr: 0x%"PRIx32 "val: 0x%" PRIx64 diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 1591574397..c2b8046b94 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -2,3 +2,4 @@ config LOONGSON3_LS7A bool select PCI_EXPRESS_7A select LOONGARCH_IPI + select LOONGARCH_PCH_PIC diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h new file mode 100644 index 0000000000..bc04ed28ef --- /dev/null +++ b/include/hw/intc/loongarch_pch_pic.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch 7A1000 I/O interrupt controller definitions + * + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +#define TYPE_LOONGARCH_PCH_PIC "loongarch_pch_pic" +DECLARE_INSTANCE_CHECKER(struct loongarch_pch_pic, LOONGARCH_PCH_PIC, + TYPE_LOONGARCH_PCH_PIC) + +#define PCH_PIC_IRQ_START 0 +#define PCH_PIC_IRQ_END 63 +#define PCH_PIC_IRQ_NUM 64 +#define PCH_PIC_INT_ID_VAL 0x7000000UL +#define PCH_PIC_INT_ID_NUM 0x3f0001UL + +#define PCH_PIC_INT_ID_OFFSET 0x00 +#define PCH_PIC_INT_ID_END 0x07 +#define PCH_PIC_INT_MASK_OFFSET 0x20 +#define PCH_PIC_INT_MASK_END 0x27 +#define PCH_PIC_HTMSI_EN_OFFSET 0x40 +#define PCH_PIC_HTMSI_EN_END 0x47 +#define PCH_PIC_INT_EDGE_OFFSET 0x60 +#define PCH_PIC_INT_EDGE_END 0x67 +#define PCH_PIC_INT_CLEAR_OFFSET 0x80 +#define PCH_PIC_INT_CLEAR_END 0x87 +#define PCH_PIC_AUTO_CTRL0_OFFSET 0xc0 +#define PCH_PIC_AUTO_CTRL0_END 0xc7 +#define PCH_PIC_AUTO_CTRL1_OFFSET 0xe0 +#define PCH_PIC_AUTO_CTRL1_END 0xe8 +#define PCH_PIC_ROUTE_ENTRY_OFFSET 0x100 +#define PCH_PIC_ROUTE_ENTRY_END 0x13f +#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 +#define PCH_PIC_HTMSI_VEC_END 0x23f +#define PCH_PIC_INT_STATUS_OFFSET 0x3a0 +#define PCH_PIC_INT_STATUS_END 0x3a7 +#define PCH_PIC_INT_POL_OFFSET 0x3e0 +#define PCH_PIC_INT_POL_END 0x3e7 + +typedef struct loongarch_pch_pic { + SysBusDevice parent_obj; + qemu_irq parent_irq[64]; + uint64_t int_mask; /*0x020 interrupt mask register*/ + uint64_t htmsi_en;/*0x040 1=msi*/ + uint64_t intedge; /*0x060 edge=1 level =0*/ + uint64_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ + uint64_t auto_crtl0; /*0x0c0*/ + uint64_t auto_crtl1; /*0x0e0*/ + uint8_t route_entry[64]; /*0x100 - 0x138*/ + uint8_t htmsi_vector[64]; /*0x200 - 0x238*/ + uint64_t last_intirr; /* edge detection */ + uint64_t intirr; /* 0x380 interrupt request register */ + uint64_t intisr; /* 0x3a0 interrupt service register */ + /* + * 0x3e0 interrupt level polarity selection + * register 0 for high level trigger + */ + uint64_t int_polarity; + MemoryRegion iomem; +} loongarch_pch_pic;