diff mbox series

[RFC,v2,19/30] hw/intc: Add LoongArch ls7a interrupt controller support(PCH-PIC)

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

Commit Message

Xiaojuan Yang Nov. 11, 2021, 1:35 a.m. UTC
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

Comments

Mark Cave-Ayland Nov. 11, 2021, 2:37 p.m. UTC | #1
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 mbox series

Patch

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;
+
+