Message ID | 1636594528-8175-20-git-send-email-yangxiaojuan@loongson.cn (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add Loongarch softmmu support. | expand |
On 11/11/2021 01:35, 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 | 283 ++++++++++++++++++++++++++++ > hw/intc/meson.build | 1 + > hw/loongarch/Kconfig | 1 + > include/hw/intc/loongarch_pch_pic.h | 49 +++++ > 5 files changed, 338 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 78aed93c45..3b7eca7b03 100644 > --- a/hw/intc/Kconfig > +++ b/hw/intc/Kconfig > @@ -73,3 +73,7 @@ config GOLDFISH_PIC > > config M68K_IRQC > 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..96e4c46174 > --- /dev/null > +++ b/hw/intc/loongarch_pch_pic.c > @@ -0,0 +1,283 @@ > +/* 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/irq.h" > +#include "hw/intc/loongarch_pch_pic.h" > +#include "migration/vmstate.h" > + > +#define DEBUG_LOONGARCH_PCH_PIC 0 > + > +#define DPRINTF(fmt, ...) \ > +do { \ > + if (DEBUG_LOONGARCH_PCH_PIC) { \ > + fprintf(stderr, "LOONGARCH_PCH_PIC: " fmt , ## __VA_ARGS__); \ > + } \ > +} while (0) Use of the DPRINTF() macro has been deprecated for new devices: please use trace-events instead. > + #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 update_irq(loongarch_pch_pic *s, int mask, int level) I'd suggest adding a loongarch_pch_ prefix to this function name. > +{ > + int i; > + uint64_t val; > + val = mask & s->intirr & (~s->int_mask); > + > + for_each_set_bit(i, &val, 32) { > + 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 irq_handler(void *opaque, int irq, int level) And also add a loongarch_pch_ prefix to this function name. > +{ > + loongarch_pch_pic *s = opaque; How about a QOM cast to ensure type safety: loongarch_pch_pic *s = LOONGARCH_PCH_PIC(opaque); > + assert(irq < 32); > + uint32_t mask = 1ULL << irq; > + DPRINTF("------ %s irq %d %d\n", __func__, 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; > + } > + > + } > + update_irq(s, mask, level); > +} > + > +static uint64_t loongarch_pch_pic_reg_read(void *opaque, hwaddr addr, > + unsigned size) > +{ > + loongarch_pch_pic *s = opaque; Similarly a QOM cast could be added here. > + uint32_t val = 0; > + uint32_t offset; > + int32_t offset_tmp; > + offset = addr & 0xfff; > + if (4 == size) { if (size == 4)? I know swapping the parameters like this used to help detect bugs many years ago, but I'm sure that all modern compilers can cope with this. > + switch (offset) { > + case PCH_PIC_INT_ID_OFFSET: > + val = 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: > + val = s->htmsi_en; > + break; > + case PCH_PIC_AUTO_CTRL0_OFFSET: > + case PCH_PIC_AUTO_CTRL1_OFFSET: > + break; > + default: > + break; > + } > + } else if (1 == size) { Same here. > + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { > + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; > + if (offset_tmp >= 0 && offset_tmp < 32) { > + 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 < 32) { > + val = s->route_entry[offset_tmp]; > + } > + } > + } > + > + return val; > +} > + > +static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr, > + uint64_t data, unsigned size) > +{ > + loongarch_pch_pic *s = opaque; Another QOM cast. > + int32_t offset_tmp; > + uint32_t offset, old; > + offset = addr & 0xfff; > + > + if (4 == size) { Again please swap the parameters. > + switch (offset) { > + case PCH_PIC_INT_MASK_OFFSET: > + old = s->int_mask; > + s->int_mask = data; > + if (old & ~data) { > + update_irq(s, (old & ~data), 1); > + } else if (~old & data) { > + 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)); > + 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 (1 == size) { And here. > + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { > + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; > + if (offset_tmp >= 0 && offset_tmp < 32) { > + 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 < 32) { > + 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_NATIVE_ENDIAN, Should this be DEVICE_LITTLE_ENDIAN or DEVICE_BIG_ENDIAN? > +}; > + > +static void loongarch_pch_pic_reset(DeviceState *d) > +{ > + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d); > + int i; > + > + s->int_id = 0x0; > + s->int_mask = 0xffffffff; > + s->htmsi_en = 0x0; > + s->intedge = 0x0; > + s->intclr = 0x0; > + s->auto_crtl0 = 0x0; > + s->auto_crtl1 = 0x0; > + for (i = 0; i < 32; 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 tmp; Can we use i instead of tmp for the loop counter? > + memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops, > + s, TYPE_LOONGARCH_PCH_PIC, 0x1000); > + sysbus_init_mmio(sbd, &s->iomem); > + > + for (tmp = 0; tmp < 32; tmp++) { > + sysbus_init_irq(sbd, &s->parent_irq[tmp]); > + } > + qdev_init_gpio_in(DEVICE(obj), irq_handler, 32); > +} > + > +static const VMStateDescription vmstate_loongarch_pch_pic = { > + .name = TYPE_LOONGARCH_PCH_PIC, > + .version_id = 1, > + .minimum_version_id = 1, > + .fields = (VMStateField[]) { > + VMSTATE_UINT32(int_mask, loongarch_pch_pic), > + VMSTATE_UINT32(htmsi_en, loongarch_pch_pic), > + VMSTATE_UINT32(intedge, loongarch_pch_pic), > + VMSTATE_UINT32(intclr, loongarch_pch_pic), > + VMSTATE_UINT32(auto_crtl0, loongarch_pch_pic), > + VMSTATE_UINT32(auto_crtl1, loongarch_pch_pic), > + VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 32), > + VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 32), > + VMSTATE_UINT32(last_intirr, loongarch_pch_pic), > + VMSTATE_UINT32(intirr, loongarch_pch_pic), > + VMSTATE_UINT32(intisr, loongarch_pch_pic), > + VMSTATE_UINT32(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 c89d2ca180..07b0627468 100644 > --- a/hw/intc/meson.build > +++ b/hw/intc/meson.build > @@ -57,3 +57,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], > if_true: files('spapr_xive_kvm.c')) > 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_PCH_PIC', if_true: files('loongarch_pch_pic.c')) > diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig > index 720822f32c..c6d7ebcd5b 100644 > --- a/hw/loongarch/Kconfig > +++ b/hw/loongarch/Kconfig > @@ -1,3 +1,4 @@ > config LOONGSON_3A5000 > bool > select PCI_EXPRESS_7A > + 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..b1b3e24166 > --- /dev/null > +++ b/include/hw/intc/loongarch_pch_pic.h > @@ -0,0 +1,49 @@ > +/* 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_ROUTE_ENTRY_OFFSET 0x100 > +#define PCH_PIC_INT_ID_OFFSET 0x00 > +#define PCH_PIC_INT_ID_VAL 0x7000000UL > +#define PCH_PIC_INT_ID_VER 0x1f0001UL > +#define PCH_PIC_INT_MASK_OFFSET 0x20 > +#define PCH_PIC_INT_EDGE_OFFSET 0x60 > +#define PCH_PIC_INT_CLEAR_OFFSET 0x80 > +#define PCH_PIC_INT_STATUS_OFFSET 0x3a0 > +#define PCH_PIC_INT_POL_OFFSET 0x3e0 > +#define PCH_PIC_HTMSI_EN_OFFSET 0x40 > +#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 > +#define PCH_PIC_AUTO_CTRL0_OFFSET 0xc0 > +#define PCH_PIC_AUTO_CTRL1_OFFSET 0xe0 > + > +typedef struct loongarch_pch_pic { > + SysBusDevice parent_obj; > + qemu_irq parent_irq[32]; > + uint32_t int_id; > + uint32_t int_mask; /*0x020 interrupt mask register*/ > + uint32_t htmsi_en;/*0x040 1=msi*/ > + uint32_t intedge; /*0x060 edge=1 level =0*/ > + uint32_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ > + uint32_t auto_crtl0; /*0x0c0*/ > + uint32_t auto_crtl1; /*0x0e0*/ > + uint8_t route_entry[32]; /*0x100 - 0x120*/ > + uint8_t htmsi_vector[32]; /*0x200 - 0x220*/ > + uint32_t last_intirr; /* edge detection */ > + uint32_t intirr; /* 0x380 interrupt request register */ > + uint32_t intisr; /* 0x3a0 interrupt service register */ > + /* > + * 0x3e0 interrupt level polarity selection > + * register 0 for high level trigger > + */ > + uint32_t int_polarity; > + MemoryRegion iomem; > +} loongarch_pch_pic; > + > + ATB, Mark.
diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index 78aed93c45..3b7eca7b03 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -73,3 +73,7 @@ config GOLDFISH_PIC config M68K_IRQC 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..96e4c46174 --- /dev/null +++ b/hw/intc/loongarch_pch_pic.c @@ -0,0 +1,283 @@ +/* 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/irq.h" +#include "hw/intc/loongarch_pch_pic.h" +#include "migration/vmstate.h" + +#define DEBUG_LOONGARCH_PCH_PIC 0 + +#define DPRINTF(fmt, ...) \ +do { \ + if (DEBUG_LOONGARCH_PCH_PIC) { \ + fprintf(stderr, "LOONGARCH_PCH_PIC: " fmt , ## __VA_ARGS__); \ + } \ +} while (0) + + #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 update_irq(loongarch_pch_pic *s, int mask, int level) +{ + int i; + uint64_t val; + val = mask & s->intirr & (~s->int_mask); + + for_each_set_bit(i, &val, 32) { + 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 irq_handler(void *opaque, int irq, int level) +{ + loongarch_pch_pic *s = opaque; + + assert(irq < 32); + uint32_t mask = 1ULL << irq; + DPRINTF("------ %s irq %d %d\n", __func__, 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; + } + + } + update_irq(s, mask, level); +} + +static uint64_t loongarch_pch_pic_reg_read(void *opaque, hwaddr addr, + unsigned size) +{ + loongarch_pch_pic *s = opaque; + uint32_t val = 0; + uint32_t offset; + int32_t offset_tmp; + offset = addr & 0xfff; + if (4 == size) { + switch (offset) { + case PCH_PIC_INT_ID_OFFSET: + val = 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: + val = s->htmsi_en; + break; + case PCH_PIC_AUTO_CTRL0_OFFSET: + case PCH_PIC_AUTO_CTRL1_OFFSET: + break; + default: + break; + } + } else if (1 == size) { + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 32) { + 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 < 32) { + val = s->route_entry[offset_tmp]; + } + } + } + + return val; +} + +static void loongarch_pch_pic_reg_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + loongarch_pch_pic *s = opaque; + int32_t offset_tmp; + uint32_t offset, old; + offset = addr & 0xfff; + + if (4 == size) { + switch (offset) { + case PCH_PIC_INT_MASK_OFFSET: + old = s->int_mask; + s->int_mask = data; + if (old & ~data) { + update_irq(s, (old & ~data), 1); + } else if (~old & data) { + 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)); + 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 (1 == size) { + if (offset >= PCH_PIC_HTMSI_VEC_OFFSET) { + offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET; + if (offset_tmp >= 0 && offset_tmp < 32) { + 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 < 32) { + 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_NATIVE_ENDIAN, +}; + +static void loongarch_pch_pic_reset(DeviceState *d) +{ + loongarch_pch_pic *s = LOONGARCH_PCH_PIC(d); + int i; + + s->int_id = 0x0; + s->int_mask = 0xffffffff; + s->htmsi_en = 0x0; + s->intedge = 0x0; + s->intclr = 0x0; + s->auto_crtl0 = 0x0; + s->auto_crtl1 = 0x0; + for (i = 0; i < 32; 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 tmp; + + memory_region_init_io(&s->iomem, obj, &loongarch_pch_pic_ops, + s, TYPE_LOONGARCH_PCH_PIC, 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + + for (tmp = 0; tmp < 32; tmp++) { + sysbus_init_irq(sbd, &s->parent_irq[tmp]); + } + qdev_init_gpio_in(DEVICE(obj), irq_handler, 32); +} + +static const VMStateDescription vmstate_loongarch_pch_pic = { + .name = TYPE_LOONGARCH_PCH_PIC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(int_mask, loongarch_pch_pic), + VMSTATE_UINT32(htmsi_en, loongarch_pch_pic), + VMSTATE_UINT32(intedge, loongarch_pch_pic), + VMSTATE_UINT32(intclr, loongarch_pch_pic), + VMSTATE_UINT32(auto_crtl0, loongarch_pch_pic), + VMSTATE_UINT32(auto_crtl1, loongarch_pch_pic), + VMSTATE_UINT8_ARRAY(route_entry, loongarch_pch_pic, 32), + VMSTATE_UINT8_ARRAY(htmsi_vector, loongarch_pch_pic, 32), + VMSTATE_UINT32(last_intirr, loongarch_pch_pic), + VMSTATE_UINT32(intirr, loongarch_pch_pic), + VMSTATE_UINT32(intisr, loongarch_pch_pic), + VMSTATE_UINT32(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 c89d2ca180..07b0627468 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -57,3 +57,4 @@ specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_XIVE'], if_true: files('spapr_xive_kvm.c')) 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_PCH_PIC', if_true: files('loongarch_pch_pic.c')) diff --git a/hw/loongarch/Kconfig b/hw/loongarch/Kconfig index 720822f32c..c6d7ebcd5b 100644 --- a/hw/loongarch/Kconfig +++ b/hw/loongarch/Kconfig @@ -1,3 +1,4 @@ config LOONGSON_3A5000 bool select PCI_EXPRESS_7A + 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..b1b3e24166 --- /dev/null +++ b/include/hw/intc/loongarch_pch_pic.h @@ -0,0 +1,49 @@ +/* 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_ROUTE_ENTRY_OFFSET 0x100 +#define PCH_PIC_INT_ID_OFFSET 0x00 +#define PCH_PIC_INT_ID_VAL 0x7000000UL +#define PCH_PIC_INT_ID_VER 0x1f0001UL +#define PCH_PIC_INT_MASK_OFFSET 0x20 +#define PCH_PIC_INT_EDGE_OFFSET 0x60 +#define PCH_PIC_INT_CLEAR_OFFSET 0x80 +#define PCH_PIC_INT_STATUS_OFFSET 0x3a0 +#define PCH_PIC_INT_POL_OFFSET 0x3e0 +#define PCH_PIC_HTMSI_EN_OFFSET 0x40 +#define PCH_PIC_HTMSI_VEC_OFFSET 0x200 +#define PCH_PIC_AUTO_CTRL0_OFFSET 0xc0 +#define PCH_PIC_AUTO_CTRL1_OFFSET 0xe0 + +typedef struct loongarch_pch_pic { + SysBusDevice parent_obj; + qemu_irq parent_irq[32]; + uint32_t int_id; + uint32_t int_mask; /*0x020 interrupt mask register*/ + uint32_t htmsi_en;/*0x040 1=msi*/ + uint32_t intedge; /*0x060 edge=1 level =0*/ + uint32_t intclr; /*0x080 for clean edge int,set 1 clean,set 0 is noused*/ + uint32_t auto_crtl0; /*0x0c0*/ + uint32_t auto_crtl1; /*0x0e0*/ + uint8_t route_entry[32]; /*0x100 - 0x120*/ + uint8_t htmsi_vector[32]; /*0x200 - 0x220*/ + uint32_t last_intirr; /* edge detection */ + uint32_t intirr; /* 0x380 interrupt request register */ + uint32_t intisr; /* 0x3a0 interrupt service register */ + /* + * 0x3e0 interrupt level polarity selection + * register 0 for high level trigger + */ + uint32_t int_polarity; + MemoryRegion iomem; +} loongarch_pch_pic; + +