qemu/hw/intc/loongarch_pch_pic.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0-or-later */
   2/*
   3 * QEMU Loongson 7A1000 I/O interrupt controller.
   4 *
   5 * Copyright (C) 2021 Loongson Technology Corporation Limited
   6 */
   7
   8#include "qemu/osdep.h"
   9#include "hw/sysbus.h"
  10#include "hw/loongarch/virt.h"
  11#include "hw/irq.h"
  12#include "hw/intc/loongarch_pch_pic.h"
  13#include "migration/vmstate.h"
  14#include "trace.h"
  15
  16static void pch_pic_update_irq(LoongArchPCHPIC *s, uint64_t mask, int level)
  17{
  18    uint64_t val;
  19    int irq;
  20
  21    if (level) {
  22        val = mask & s->intirr & ~s->int_mask;
  23        if (val) {
  24            irq = ctz64(val);
  25            s->intisr |= MAKE_64BIT_MASK(irq, 1);
  26            qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
  27        }
  28    } else {
  29        val = mask & s->intisr;
  30        if (val) {
  31            irq = ctz64(val);
  32            s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
  33            qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
  34        }
  35    }
  36}
  37
  38static void pch_pic_irq_handler(void *opaque, int irq, int level)
  39{
  40    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
  41    uint64_t mask = 1ULL << irq;
  42
  43    assert(irq < PCH_PIC_IRQ_NUM);
  44    trace_loongarch_pch_pic_irq_handler(irq, level);
  45
  46    if (s->intedge & mask) {
  47        /* Edge triggered */
  48        if (level) {
  49            if ((s->last_intirr & mask) == 0) {
  50                s->intirr |= mask;
  51            }
  52            s->last_intirr |= mask;
  53        } else {
  54            s->last_intirr &= ~mask;
  55        }
  56    } else {
  57        /* Level triggered */
  58        if (level) {
  59            s->intirr |= mask;
  60            s->last_intirr |= mask;
  61        } else {
  62            s->intirr &= ~mask;
  63            s->last_intirr &= ~mask;
  64        }
  65    }
  66    pch_pic_update_irq(s, mask, level);
  67}
  68
  69static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
  70                                            unsigned size)
  71{
  72    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
  73    uint64_t val = 0;
  74    uint32_t offset = addr & 0xfff;
  75
  76    switch (offset) {
  77    case PCH_PIC_INT_ID_LO:
  78        val = PCH_PIC_INT_ID_VAL;
  79        break;
  80    case PCH_PIC_INT_ID_HI:
  81        val = PCH_PIC_INT_ID_NUM;
  82        break;
  83    case PCH_PIC_INT_MASK_LO:
  84        val = (uint32_t)s->int_mask;
  85        break;
  86    case PCH_PIC_INT_MASK_HI:
  87        val = s->int_mask >> 32;
  88        break;
  89    case PCH_PIC_INT_EDGE_LO:
  90        val = (uint32_t)s->intedge;
  91        break;
  92    case PCH_PIC_INT_EDGE_HI:
  93        val = s->intedge >> 32;
  94        break;
  95    case PCH_PIC_HTMSI_EN_LO:
  96        val = (uint32_t)s->htmsi_en;
  97        break;
  98    case PCH_PIC_HTMSI_EN_HI:
  99        val = s->htmsi_en >> 32;
 100        break;
 101    case PCH_PIC_AUTO_CTRL0_LO:
 102    case PCH_PIC_AUTO_CTRL0_HI:
 103    case PCH_PIC_AUTO_CTRL1_LO:
 104    case PCH_PIC_AUTO_CTRL1_HI:
 105        break;
 106    default:
 107        break;
 108    }
 109
 110    trace_loongarch_pch_pic_low_readw(size, addr, val);
 111    return val;
 112}
 113
 114static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
 115{
 116    uint64_t mask = 0xffffffff00000000;
 117    uint64_t data = target;
 118
 119    return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
 120}
 121
 122static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
 123                                         uint64_t value, unsigned size)
 124{
 125    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
 126    uint32_t offset, old_valid, data = (uint32_t)value;
 127    uint64_t old, int_mask;
 128    offset = addr & 0xfff;
 129
 130    trace_loongarch_pch_pic_low_writew(size, addr, data);
 131
 132    switch (offset) {
 133    case PCH_PIC_INT_MASK_LO:
 134        old = s->int_mask;
 135        s->int_mask = get_writew_val(old, data, 0);
 136        old_valid = (uint32_t)old;
 137        if (old_valid & ~data) {
 138            pch_pic_update_irq(s, (old_valid & ~data), 1);
 139        }
 140        if (~old_valid & data) {
 141            pch_pic_update_irq(s, (~old_valid & data), 0);
 142        }
 143        break;
 144    case PCH_PIC_INT_MASK_HI:
 145        old = s->int_mask;
 146        s->int_mask = get_writew_val(old, data, 1);
 147        old_valid = (uint32_t)(old >> 32);
 148        int_mask = old_valid & ~data;
 149        if (int_mask) {
 150            pch_pic_update_irq(s, int_mask << 32, 1);
 151        }
 152        int_mask = ~old_valid & data;
 153        if (int_mask) {
 154            pch_pic_update_irq(s, int_mask << 32, 0);
 155        }
 156        break;
 157    case PCH_PIC_INT_EDGE_LO:
 158        s->intedge = get_writew_val(s->intedge, data, 0);
 159        break;
 160    case PCH_PIC_INT_EDGE_HI:
 161        s->intedge = get_writew_val(s->intedge, data, 1);
 162        break;
 163    case PCH_PIC_INT_CLEAR_LO:
 164        if (s->intedge & data) {
 165            s->intirr &= (~data);
 166            pch_pic_update_irq(s, data, 0);
 167            s->intisr &= (~data);
 168        }
 169        break;
 170    case PCH_PIC_INT_CLEAR_HI:
 171        value <<= 32;
 172        if (s->intedge & value) {
 173            s->intirr &= (~value);
 174            pch_pic_update_irq(s, value, 0);
 175            s->intisr &= (~value);
 176        }
 177        break;
 178    case PCH_PIC_HTMSI_EN_LO:
 179        s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
 180        break;
 181    case PCH_PIC_HTMSI_EN_HI:
 182        s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
 183        break;
 184    case PCH_PIC_AUTO_CTRL0_LO:
 185    case PCH_PIC_AUTO_CTRL0_HI:
 186    case PCH_PIC_AUTO_CTRL1_LO:
 187    case PCH_PIC_AUTO_CTRL1_HI:
 188        break;
 189    default:
 190        break;
 191    }
 192}
 193
 194static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
 195                                        unsigned size)
 196{
 197    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
 198    uint64_t val = 0;
 199    uint32_t offset = addr & 0xfff;
 200
 201    switch (offset) {
 202    case STATUS_LO_START:
 203        val = (uint32_t)(s->intisr & (~s->int_mask));
 204        break;
 205    case STATUS_HI_START:
 206        val = (s->intisr & (~s->int_mask)) >> 32;
 207        break;
 208    case POL_LO_START:
 209        val = (uint32_t)s->int_polarity;
 210        break;
 211    case POL_HI_START:
 212        val = s->int_polarity >> 32;
 213        break;
 214    default:
 215        break;
 216    }
 217
 218    trace_loongarch_pch_pic_high_readw(size, addr, val);
 219    return val;
 220}
 221
 222static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
 223                                     uint64_t value, unsigned size)
 224{
 225    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
 226    uint32_t offset, data = (uint32_t)value;
 227    offset = addr & 0xfff;
 228
 229    trace_loongarch_pch_pic_high_writew(size, addr, data);
 230
 231    switch (offset) {
 232    case STATUS_LO_START:
 233        s->intisr = get_writew_val(s->intisr, data, 0);
 234        break;
 235    case STATUS_HI_START:
 236        s->intisr = get_writew_val(s->intisr, data, 1);
 237        break;
 238    case POL_LO_START:
 239        s->int_polarity = get_writew_val(s->int_polarity, data, 0);
 240        break;
 241    case POL_HI_START:
 242        s->int_polarity = get_writew_val(s->int_polarity, data, 1);
 243        break;
 244    default:
 245        break;
 246    }
 247}
 248
 249static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
 250                                        unsigned size)
 251{
 252    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
 253    uint64_t val = 0;
 254    uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
 255    int64_t offset_tmp;
 256
 257    switch (offset) {
 258    case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
 259        offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
 260        if (offset_tmp >= 0 && offset_tmp < 64) {
 261            val = s->htmsi_vector[offset_tmp];
 262        }
 263        break;
 264    case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
 265        offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
 266        if (offset_tmp >= 0 && offset_tmp < 64) {
 267            val = s->route_entry[offset_tmp];
 268        }
 269        break;
 270    default:
 271        break;
 272    }
 273
 274    trace_loongarch_pch_pic_readb(size, addr, val);
 275    return val;
 276}
 277
 278static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
 279                                     uint64_t data, unsigned size)
 280{
 281    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(opaque);
 282    int32_t offset_tmp;
 283    uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
 284
 285    trace_loongarch_pch_pic_writeb(size, addr, data);
 286
 287    switch (offset) {
 288    case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
 289        offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
 290        if (offset_tmp >= 0 && offset_tmp < 64) {
 291            s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
 292        }
 293        break;
 294    case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
 295        offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
 296        if (offset_tmp >= 0 && offset_tmp < 64) {
 297            s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
 298        }
 299        break;
 300    default:
 301        break;
 302    }
 303}
 304
 305static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
 306    .read = loongarch_pch_pic_low_readw,
 307    .write = loongarch_pch_pic_low_writew,
 308    .valid = {
 309        .min_access_size = 4,
 310        .max_access_size = 8,
 311    },
 312    .impl = {
 313        .min_access_size = 4,
 314        .max_access_size = 4,
 315    },
 316    .endianness = DEVICE_LITTLE_ENDIAN,
 317};
 318
 319static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
 320    .read = loongarch_pch_pic_high_readw,
 321    .write = loongarch_pch_pic_high_writew,
 322    .valid = {
 323        .min_access_size = 4,
 324        .max_access_size = 8,
 325    },
 326    .impl = {
 327        .min_access_size = 4,
 328        .max_access_size = 4,
 329    },
 330    .endianness = DEVICE_LITTLE_ENDIAN,
 331};
 332
 333static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
 334    .read = loongarch_pch_pic_readb,
 335    .write = loongarch_pch_pic_writeb,
 336    .valid = {
 337        .min_access_size = 1,
 338        .max_access_size = 1,
 339    },
 340    .impl = {
 341        .min_access_size = 1,
 342        .max_access_size = 1,
 343    },
 344    .endianness = DEVICE_LITTLE_ENDIAN,
 345};
 346
 347static void loongarch_pch_pic_reset(DeviceState *d)
 348{
 349    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(d);
 350    int i;
 351
 352    s->int_mask = -1;
 353    s->htmsi_en = 0x0;
 354    s->intedge  = 0x0;
 355    s->intclr   = 0x0;
 356    s->auto_crtl0 = 0x0;
 357    s->auto_crtl1 = 0x0;
 358    for (i = 0; i < 64; i++) {
 359        s->route_entry[i] = 0x1;
 360        s->htmsi_vector[i] = 0x0;
 361    }
 362    s->intirr = 0x0;
 363    s->intisr = 0x0;
 364    s->last_intirr = 0x0;
 365    s->int_polarity = 0x0;
 366}
 367
 368static void loongarch_pch_pic_init(Object *obj)
 369{
 370    LoongArchPCHPIC *s = LOONGARCH_PCH_PIC(obj);
 371    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 372
 373    memory_region_init_io(&s->iomem32_low, obj,
 374                          &loongarch_pch_pic_reg32_low_ops,
 375                          s, PCH_PIC_NAME(.reg32_part1), 0x100);
 376    memory_region_init_io(&s->iomem8, obj, &loongarch_pch_pic_reg8_ops,
 377                          s, PCH_PIC_NAME(.reg8), 0x2a0);
 378    memory_region_init_io(&s->iomem32_high, obj,
 379                          &loongarch_pch_pic_reg32_high_ops,
 380                          s, PCH_PIC_NAME(.reg32_part2), 0xc60);
 381    sysbus_init_mmio(sbd, &s->iomem32_low);
 382    sysbus_init_mmio(sbd, &s->iomem8);
 383    sysbus_init_mmio(sbd, &s->iomem32_high);
 384
 385    qdev_init_gpio_out(DEVICE(obj), s->parent_irq, PCH_PIC_IRQ_NUM);
 386    qdev_init_gpio_in(DEVICE(obj), pch_pic_irq_handler, PCH_PIC_IRQ_NUM);
 387}
 388
 389static const VMStateDescription vmstate_loongarch_pch_pic = {
 390    .name = TYPE_LOONGARCH_PCH_PIC,
 391    .version_id = 1,
 392    .minimum_version_id = 1,
 393    .fields = (VMStateField[]) {
 394        VMSTATE_UINT64(int_mask, LoongArchPCHPIC),
 395        VMSTATE_UINT64(htmsi_en, LoongArchPCHPIC),
 396        VMSTATE_UINT64(intedge, LoongArchPCHPIC),
 397        VMSTATE_UINT64(intclr, LoongArchPCHPIC),
 398        VMSTATE_UINT64(auto_crtl0, LoongArchPCHPIC),
 399        VMSTATE_UINT64(auto_crtl1, LoongArchPCHPIC),
 400        VMSTATE_UINT8_ARRAY(route_entry, LoongArchPCHPIC, 64),
 401        VMSTATE_UINT8_ARRAY(htmsi_vector, LoongArchPCHPIC, 64),
 402        VMSTATE_UINT64(last_intirr, LoongArchPCHPIC),
 403        VMSTATE_UINT64(intirr, LoongArchPCHPIC),
 404        VMSTATE_UINT64(intisr, LoongArchPCHPIC),
 405        VMSTATE_UINT64(int_polarity, LoongArchPCHPIC),
 406        VMSTATE_END_OF_LIST()
 407    }
 408};
 409
 410static void loongarch_pch_pic_class_init(ObjectClass *klass, void *data)
 411{
 412    DeviceClass *dc = DEVICE_CLASS(klass);
 413
 414    dc->reset = loongarch_pch_pic_reset;
 415    dc->vmsd = &vmstate_loongarch_pch_pic;
 416}
 417
 418static const TypeInfo loongarch_pch_pic_info = {
 419    .name          = TYPE_LOONGARCH_PCH_PIC,
 420    .parent        = TYPE_SYS_BUS_DEVICE,
 421    .instance_size = sizeof(LoongArchPCHPIC),
 422    .instance_init = loongarch_pch_pic_init,
 423    .class_init    = loongarch_pch_pic_class_init,
 424};
 425
 426static void loongarch_pch_pic_register_types(void)
 427{
 428    type_register_static(&loongarch_pch_pic_info);
 429}
 430
 431type_init(loongarch_pch_pic_register_types)
 432