Message ID | 20210118011706.22639-2-jiaxun.yang@flygoat.com |
---|---|
State | New |
Headers | show |
Series | loongson3_virt TCG SMP support | expand |
Hi, Jiaxun, On Mon, Jan 18, 2021 at 9:17 AM Jiaxun Yang <jiaxun.yang@flygoat.com> wrote: > > Loongson IPI controller is a MMIO based simple level triggered > interrupt controller. It will trigger IRQ to it's upstream > processor when set register is written. > > It also has 4 64bit mailboxes to pass boot information to > secondary processor. > > Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com> > --- > include/hw/intc/loongson_ipi.h | 20 ++++ > hw/intc/loongson_ipi.c | 174 +++++++++++++++++++++++++++++++++ > hw/intc/Kconfig | 3 + > hw/intc/meson.build | 1 + > hw/intc/trace-events | 4 + > 5 files changed, 202 insertions(+) > create mode 100644 include/hw/intc/loongson_ipi.h > create mode 100644 hw/intc/loongson_ipi.c > > diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h > new file mode 100644 > index 0000000000..a535c467bf > --- /dev/null > +++ b/include/hw/intc/loongson_ipi.h > @@ -0,0 +1,20 @@ > +/* > + * This file is subject to the terms and conditions of the GNU General Public > + * License. See the file "COPYING" in the main directory of this archive > + * for more details. > + * > + * Copyright (c) 2020-2021 Jiaxun Yang <jiaxun.yang@flygoat.com> > + * > + */ > + > +#ifndef LOONGSON_IPI_H > +#define LOONGSON_IPI_H > + > +#include "qemu/units.h" > +#include "hw/sysbus.h" > +#include "qom/object.h" > + > +#define TYPE_LOONGSON_IPI "loongson.ipi" > +#define LOONGSON_IPI(obj) OBJECT_CHECK(struct loongson_ipi, (obj), TYPE_LOONGSON_IPI) > + > +#endif /* LOONGSON_IPI_H */ > diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c > new file mode 100644 > index 0000000000..cce1da8fb9 > --- /dev/null > +++ b/hw/intc/loongson_ipi.c > @@ -0,0 +1,174 @@ > +/* > + * QEMU Loongson Inter Processor Interrupt Controller > + * > + * Copyright (c) 2020-2021 Jiaxun Yang <jiaxun.yang@flygoat.com> > + * > + * This program is free software: you can redistribute it and/or modify > + * it under the terms of the GNU General Public License as published by > + * the Free Software Foundation, either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program. If not, see <https://www.gnu.org/licenses/>. > + * > + */ > + > +#include "qemu/osdep.h" > +#include "hw/sysbus.h" > +#include "qemu/module.h" > +#include "qemu/log.h" > +#include "hw/irq.h" > +#include "hw/qdev-properties.h" > +#include "hw/intc/loongson_ipi.h" > +#include "sysemu/reset.h" > +#include "trace.h" > + > +/* > + * Mailboxes are actually 4 64bit registers but we treat it as 8 32bit > + * to simplify implementation. > + */ > +#define NUM_MBOX 8 > + > +enum { > + R_ISR = 0, > + R_IEN = 1, > + R_SET = 2, > + R_CLR = 3, > + /* No register at 0x10~0x20 */ > + R_MBOX0 = 8, > + R_END = R_MBOX0 + NUM_MBOX > +}; > + > +struct loongson_ipi { > + SysBusDevice parent_obj; > + > + MemoryRegion mmio; > + qemu_irq parent_irq; > + > + uint32_t isr; > + uint32_t ien; > + uint32_t mbox[NUM_MBOX]; > +}; > + > +static uint64_t > +ipi_read(void *opaque, hwaddr addr, unsigned int size) > +{ > + struct loongson_ipi *p = opaque; > + uint64_t r = 0; > + > + addr >>= 2; > + switch (addr) { > + case R_ISR: > + r = p->isr; > + break; > + case R_IEN: > + r = p->ien; > + break; > + case R_SET: > + case R_CLR: > + qemu_log_mask(LOG_GUEST_ERROR, > + "%s: Reading write only reg: 0x%" HWADDR_PRIx "\n", > + __func__, addr); > + break; > + case R_MBOX0 ... (R_END - 1): > + r = p->mbox[addr - R_MBOX0]; > + break; > + default: > + break; > + } > + > + trace_loongson_ipi_read(addr, r); > + > + return r; > +} > + > +static void > +ipi_write(void *opaque, hwaddr addr, > + uint64_t val64, unsigned int size) > +{ > + struct loongson_ipi *p = opaque; > + uint32_t value = val64; > + > + addr >>= 2; > + switch (addr) { > + case R_ISR: > + /* Do nothing */ > + break; > + case R_IEN: > + p->ien = value; > + break; > + case R_SET: > + p->isr |= value; > + break; > + case R_CLR: > + p->isr &= ~value; > + break; > + case R_MBOX0 ... (R_END - 1): > + p->mbox[addr - R_MBOX0] = value; > + break; > + default: > + break; > + } > + p->isr &= p->ien; > + > + trace_loongson_ipi_write(addr, value); > + > + qemu_set_irq(p->parent_irq, !!p->isr); > +} > + > +static void ipi_reset(void *opaque) > +{ > + int i; > + struct loongson_ipi *p = opaque; > + > + p->ien = 0; > + p->isr = 0; > + for (i = 0; i < NUM_MBOX; i++) { > + p->mbox[i] = 0; > + } > +} > + > +static const MemoryRegionOps pic_mmio_ops = { > + .read = ipi_read, > + .write = ipi_write, > + .endianness = DEVICE_NATIVE_ENDIAN, > + .impl = { > + .min_access_size = 4, > + .max_access_size = 4 > + }, > + .valid = { > + .min_access_size = 4, > + .max_access_size = 8 > + }, > +}; > + > +static void loongson_ipi_init(Object *obj) > +{ > + struct loongson_ipi *p = LOONGSON_IPI(obj); > + > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq); > + > + memory_region_init_io(&p->mmio, obj, &pic_mmio_ops, p, > + "loongson.ipi", R_END * 4); > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); > + qemu_register_reset(ipi_reset, p); > +} > + > +static const TypeInfo loongson_ipi_info = { > + .name = TYPE_LOONGSON_IPI, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(struct loongson_ipi), > + .instance_init = loongson_ipi_init, > +}; > + > +static void loongson_ipi_register_types(void) > +{ > + type_register_static(&loongson_ipi_info); > +} > + > +type_init(loongson_ipi_register_types) > diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig > index c18d11142a..0e15102662 100644 > --- a/hw/intc/Kconfig > +++ b/hw/intc/Kconfig > @@ -59,6 +59,9 @@ config RX_ICU > config LOONGSON_LIOINTC > bool > > +config LOONGSON_IPI > + bool > + > config SIFIVE_CLINT > bool > > diff --git a/hw/intc/meson.build b/hw/intc/meson.build > index 53cba11569..5257c5fb94 100644 > --- a/hw/intc/meson.build > +++ b/hw/intc/meson.build > @@ -36,6 +36,7 @@ specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) > specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_plic.c')) > specific_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) > specific_ss.add(when: 'CONFIG_LOONGSON_LIOINTC', if_true: files('loongson_liointc.c')) > +specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) Maybe sort by alpha-betical is better (put IPI before LIOINTC)? Huacai > specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gic.c')) > specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) > specific_ss.add(when: 'CONFIG_OMPIC', if_true: files('ompic.c')) > diff --git a/hw/intc/trace-events b/hw/intc/trace-events > index 8ed397a0d5..e43e318be0 100644 > --- a/hw/intc/trace-events > +++ b/hw/intc/trace-events > @@ -60,6 +60,10 @@ lm32_pic_set_ip(uint32_t ip) "ip 0x%08x" > lm32_pic_get_im(uint32_t im) "im 0x%08x" > lm32_pic_get_ip(uint32_t ip) "ip 0x%08x" > > +# loongson_ipi.c > +loongson_ipi_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 > +loongson_ipi_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 > + > # xics.c > xics_icp_check_ipi(int server, uint8_t mfrr) "CPU %d can take IPI mfrr=0x%x" > xics_icp_accept(uint32_t old_xirr, uint32_t new_xirr) "icp_accept: XIRR 0x%"PRIx32"->0x%"PRIx32 > -- > 2.30.0 >
Hi Jiaxun, On 1/18/21 2:17 AM, Jiaxun Yang wrote: > Loongson IPI controller is a MMIO based simple level triggered > interrupt controller. It will trigger IRQ to it's upstream > processor when set register is written. > > It also has 4 64bit mailboxes to pass boot information to > secondary processor. > > Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com> > --- > include/hw/intc/loongson_ipi.h | 20 ++++ > hw/intc/loongson_ipi.c | 174 +++++++++++++++++++++++++++++++++ > hw/intc/Kconfig | 3 + > hw/intc/meson.build | 1 + > hw/intc/trace-events | 4 + > 5 files changed, 202 insertions(+) > create mode 100644 include/hw/intc/loongson_ipi.h > create mode 100644 hw/intc/loongson_ipi.c ... > +static void loongson_ipi_init(Object *obj) > +{ > + struct loongson_ipi *p = LOONGSON_IPI(obj); > + > + sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq); > + > + memory_region_init_io(&p->mmio, obj, &pic_mmio_ops, p, > + "loongson.ipi", R_END * 4); > + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); > + qemu_register_reset(ipi_reset, p); You forgot to address 2 comments from v2: - Use DeviceReset instead of qemu_register_reset() - Missing VMState https://www.mail-archive.com/qemu-devel@nongnu.org/msg772949.html > +} > + > +static const TypeInfo loongson_ipi_info = { > + .name = TYPE_LOONGSON_IPI, > + .parent = TYPE_SYS_BUS_DEVICE, > + .instance_size = sizeof(struct loongson_ipi), > + .instance_init = loongson_ipi_init, > +};
diff --git a/include/hw/intc/loongson_ipi.h b/include/hw/intc/loongson_ipi.h new file mode 100644 index 0000000000..a535c467bf --- /dev/null +++ b/include/hw/intc/loongson_ipi.h @@ -0,0 +1,20 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (c) 2020-2021 Jiaxun Yang <jiaxun.yang@flygoat.com> + * + */ + +#ifndef LOONGSON_IPI_H +#define LOONGSON_IPI_H + +#include "qemu/units.h" +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_LOONGSON_IPI "loongson.ipi" +#define LOONGSON_IPI(obj) OBJECT_CHECK(struct loongson_ipi, (obj), TYPE_LOONGSON_IPI) + +#endif /* LOONGSON_IPI_H */ diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c new file mode 100644 index 0000000000..cce1da8fb9 --- /dev/null +++ b/hw/intc/loongson_ipi.c @@ -0,0 +1,174 @@ +/* + * QEMU Loongson Inter Processor Interrupt Controller + * + * Copyright (c) 2020-2021 Jiaxun Yang <jiaxun.yang@flygoat.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "hw/sysbus.h" +#include "qemu/module.h" +#include "qemu/log.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/intc/loongson_ipi.h" +#include "sysemu/reset.h" +#include "trace.h" + +/* + * Mailboxes are actually 4 64bit registers but we treat it as 8 32bit + * to simplify implementation. + */ +#define NUM_MBOX 8 + +enum { + R_ISR = 0, + R_IEN = 1, + R_SET = 2, + R_CLR = 3, + /* No register at 0x10~0x20 */ + R_MBOX0 = 8, + R_END = R_MBOX0 + NUM_MBOX +}; + +struct loongson_ipi { + SysBusDevice parent_obj; + + MemoryRegion mmio; + qemu_irq parent_irq; + + uint32_t isr; + uint32_t ien; + uint32_t mbox[NUM_MBOX]; +}; + +static uint64_t +ipi_read(void *opaque, hwaddr addr, unsigned int size) +{ + struct loongson_ipi *p = opaque; + uint64_t r = 0; + + addr >>= 2; + switch (addr) { + case R_ISR: + r = p->isr; + break; + case R_IEN: + r = p->ien; + break; + case R_SET: + case R_CLR: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Reading write only reg: 0x%" HWADDR_PRIx "\n", + __func__, addr); + break; + case R_MBOX0 ... (R_END - 1): + r = p->mbox[addr - R_MBOX0]; + break; + default: + break; + } + + trace_loongson_ipi_read(addr, r); + + return r; +} + +static void +ipi_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + struct loongson_ipi *p = opaque; + uint32_t value = val64; + + addr >>= 2; + switch (addr) { + case R_ISR: + /* Do nothing */ + break; + case R_IEN: + p->ien = value; + break; + case R_SET: + p->isr |= value; + break; + case R_CLR: + p->isr &= ~value; + break; + case R_MBOX0 ... (R_END - 1): + p->mbox[addr - R_MBOX0] = value; + break; + default: + break; + } + p->isr &= p->ien; + + trace_loongson_ipi_write(addr, value); + + qemu_set_irq(p->parent_irq, !!p->isr); +} + +static void ipi_reset(void *opaque) +{ + int i; + struct loongson_ipi *p = opaque; + + p->ien = 0; + p->isr = 0; + for (i = 0; i < NUM_MBOX; i++) { + p->mbox[i] = 0; + } +} + +static const MemoryRegionOps pic_mmio_ops = { + .read = ipi_read, + .write = ipi_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4 + }, + .valid = { + .min_access_size = 4, + .max_access_size = 8 + }, +}; + +static void loongson_ipi_init(Object *obj) +{ + struct loongson_ipi *p = LOONGSON_IPI(obj); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq); + + memory_region_init_io(&p->mmio, obj, &pic_mmio_ops, p, + "loongson.ipi", R_END * 4); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio); + qemu_register_reset(ipi_reset, p); +} + +static const TypeInfo loongson_ipi_info = { + .name = TYPE_LOONGSON_IPI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(struct loongson_ipi), + .instance_init = loongson_ipi_init, +}; + +static void loongson_ipi_register_types(void) +{ + type_register_static(&loongson_ipi_info); +} + +type_init(loongson_ipi_register_types) diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig index c18d11142a..0e15102662 100644 --- a/hw/intc/Kconfig +++ b/hw/intc/Kconfig @@ -59,6 +59,9 @@ config RX_ICU config LOONGSON_LIOINTC bool +config LOONGSON_IPI + bool + config SIFIVE_CLINT bool diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 53cba11569..5257c5fb94 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -36,6 +36,7 @@ specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) specific_ss.add(when: 'CONFIG_IBEX', if_true: files('ibex_plic.c')) specific_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) specific_ss.add(when: 'CONFIG_LOONGSON_LIOINTC', if_true: files('loongson_liointc.c')) +specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) specific_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('mips_gic.c')) specific_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_intc.c')) specific_ss.add(when: 'CONFIG_OMPIC', if_true: files('ompic.c')) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 8ed397a0d5..e43e318be0 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -60,6 +60,10 @@ lm32_pic_set_ip(uint32_t ip) "ip 0x%08x" lm32_pic_get_im(uint32_t im) "im 0x%08x" lm32_pic_get_ip(uint32_t ip) "ip 0x%08x" +# loongson_ipi.c +loongson_ipi_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 +loongson_ipi_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 + # xics.c xics_icp_check_ipi(int server, uint8_t mfrr) "CPU %d can take IPI mfrr=0x%x" xics_icp_accept(uint32_t old_xirr, uint32_t new_xirr) "icp_accept: XIRR 0x%"PRIx32"->0x%"PRIx32
Loongson IPI controller is a MMIO based simple level triggered interrupt controller. It will trigger IRQ to it's upstream processor when set register is written. It also has 4 64bit mailboxes to pass boot information to secondary processor. Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com> --- include/hw/intc/loongson_ipi.h | 20 ++++ hw/intc/loongson_ipi.c | 174 +++++++++++++++++++++++++++++++++ hw/intc/Kconfig | 3 + hw/intc/meson.build | 1 + hw/intc/trace-events | 4 + 5 files changed, 202 insertions(+) create mode 100644 include/hw/intc/loongson_ipi.h create mode 100644 hw/intc/loongson_ipi.c