qemu/hw/intc/loongson_liointc.c
Eduardo Habkost db1015e92e Move QOM typedefs and add missing includes
Some typedefs and macros are defined after the type check macros.
This makes it difficult to automatically replace their
definitions with OBJECT_DECLARE_TYPE.

Patch generated using:

 $ ./scripts/codeconverter/converter.py -i \
   --pattern=QOMStructTypedefSplit $(git grep -l '' -- '*.[ch]')

which will split "typdef struct { ... } TypedefName"
declarations.

Followed by:

 $ ./scripts/codeconverter/converter.py -i --pattern=MoveSymbols \
    $(git grep -l '' -- '*.[ch]')

which will:
- move the typedefs and #defines above the type check macros
- add missing #include "qom/object.h" lines if necessary

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <20200831210740.126168-9-ehabkost@redhat.com>
Reviewed-by: Juan Quintela <quintela@redhat.com>
Message-Id: <20200831210740.126168-10-ehabkost@redhat.com>
Message-Id: <20200831210740.126168-11-ehabkost@redhat.com>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
2020-09-09 09:26:43 -04:00

244 lines
6.1 KiB
C

/*
* QEMU Loongson Local I/O interrupt controler.
*
* Copyright (c) 2020 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 "hw/irq.h"
#include "hw/qdev-properties.h"
#include "qom/object.h"
#define D(x)
#define NUM_IRQS 32
#define NUM_CORES 4
#define NUM_IPS 4
#define NUM_PARENTS (NUM_CORES * NUM_IPS)
#define PARENT_COREx_IPy(x, y) (NUM_IPS * x + y)
#define R_MAPPER_START 0x0
#define R_MAPPER_END 0x20
#define R_ISR R_MAPPER_END
#define R_IEN 0x24
#define R_IEN_SET 0x28
#define R_IEN_CLR 0x2c
#define R_PERCORE_ISR(x) (0x40 + 0x8 * x)
#define R_END 0x64
#define TYPE_LOONGSON_LIOINTC "loongson.liointc"
#define LOONGSON_LIOINTC(obj) \
OBJECT_CHECK(struct loongson_liointc, (obj), TYPE_LOONGSON_LIOINTC)
struct loongson_liointc {
SysBusDevice parent_obj;
MemoryRegion mmio;
qemu_irq parent_irq[NUM_PARENTS];
uint8_t mapper[NUM_IRQS]; /* 0:3 for core, 4:7 for IP */
uint32_t isr;
uint32_t ien;
uint32_t per_core_isr[NUM_CORES];
/* state of the interrupt input pins */
uint32_t pin_state;
bool parent_state[NUM_PARENTS];
};
static void update_irq(struct loongson_liointc *p)
{
uint32_t irq, core, ip;
uint32_t per_ip_isr[NUM_IPS] = {0};
/* level triggered interrupt */
p->isr = p->pin_state;
/* Clear disabled IRQs */
p->isr &= p->ien;
/* Clear per_core_isr */
for (core = 0; core < NUM_CORES; core++) {
p->per_core_isr[core] = 0;
}
/* Update per_core_isr and per_ip_isr */
for (irq = 0; irq < NUM_IRQS; irq++) {
if (!(p->isr & (1 << irq))) {
continue;
}
for (core = 0; core < NUM_CORES; core++) {
if ((p->mapper[irq] & (1 << core))) {
p->per_core_isr[core] |= (1 << irq);
}
}
for (ip = 0; ip < NUM_IPS; ip++) {
if ((p->mapper[irq] & (1 << (ip + 4)))) {
per_ip_isr[ip] |= (1 << irq);
}
}
}
/* Emit IRQ to parent! */
for (core = 0; core < NUM_CORES; core++) {
for (ip = 0; ip < NUM_IPS; ip++) {
int parent = PARENT_COREx_IPy(core, ip);
if (p->parent_state[parent] !=
(!!p->per_core_isr[core] && !!per_ip_isr[ip])) {
p->parent_state[parent] = !p->parent_state[parent];
qemu_set_irq(p->parent_irq[parent], p->parent_state[parent]);
}
}
}
}
static uint64_t
liointc_read(void *opaque, hwaddr addr, unsigned int size)
{
struct loongson_liointc *p = opaque;
uint32_t r = 0;
/* Mapper is 1 byte */
if (size == 1 && addr < R_MAPPER_END) {
r = p->mapper[addr];
goto out;
}
/* Rest is 4 byte */
if (size != 4 || (addr % 4)) {
goto out;
}
if (addr >= R_PERCORE_ISR(0) &&
addr < R_PERCORE_ISR(NUM_CORES)) {
int core = (addr - R_PERCORE_ISR(0)) / 4;
r = p->per_core_isr[core];
goto out;
}
switch (addr) {
case R_ISR:
r = p->isr;
break;
case R_IEN:
r = p->ien;
break;
default:
break;
}
out:
D(qemu_log("%s: size=%d addr=%lx val=%x\n", __func__, size, addr, r));
return r;
}
static void
liointc_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
struct loongson_liointc *p = opaque;
uint32_t value = val64;
D(qemu_log("%s: size=%d, addr=%lx val=%x\n", __func__, size, addr, value));
/* Mapper is 1 byte */
if (size == 1 && addr < R_MAPPER_END) {
p->mapper[addr] = value;
goto out;
}
/* Rest is 4 byte */
if (size != 4 || (addr % 4)) {
goto out;
}
if (addr >= R_PERCORE_ISR(0) &&
addr < R_PERCORE_ISR(NUM_CORES)) {
int core = (addr - R_PERCORE_ISR(0)) / 4;
p->per_core_isr[core] = value;
goto out;
}
switch (addr) {
case R_IEN_SET:
p->ien |= value;
break;
case R_IEN_CLR:
p->ien &= ~value;
break;
default:
break;
}
out:
update_irq(p);
}
static const MemoryRegionOps pic_ops = {
.read = liointc_read,
.write = liointc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 4
}
};
static void irq_handler(void *opaque, int irq, int level)
{
struct loongson_liointc *p = opaque;
p->pin_state &= ~(1 << irq);
p->pin_state |= level << irq;
update_irq(p);
}
static void loongson_liointc_init(Object *obj)
{
struct loongson_liointc *p = LOONGSON_LIOINTC(obj);
int i;
qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
for (i = 0; i < NUM_PARENTS; i++) {
sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq[i]);
}
memory_region_init_io(&p->mmio, obj, &pic_ops, p,
"loongson.liointc", R_END);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
}
static const TypeInfo loongson_liointc_info = {
.name = TYPE_LOONGSON_LIOINTC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(struct loongson_liointc),
.instance_init = loongson_liointc_init,
};
static void loongson_liointc_register_types(void)
{
type_register_static(&loongson_liointc_info);
}
type_init(loongson_liointc_register_types)