Commit e0dfe5b1 authored by Scott Wood's avatar Scott Wood Committed by Alexander Graf
Browse files

openpic: add basic support for MPIC v4.2



Besides the new value in the version register, this provides:
- ILR support, which includes:
  - IDR becoming a pure CPU bitmap, allowing 32 CPUs
  - machine check output support (though other parts of QEMU need to
    be fixed for it to do something other than immediately reboot the
    guest)
- dummy error interrupt support (EISR0/EIMR0 read as zero)
  - actually all FSL MPICs get all summary registers returning zero for now,
    which includes EISR0/EIMR0

Various refactoring is done to support these changes and to ease
new functionality (e.g. a more flexible way of declaring regions).

Just as the code was already not a full implementation of MPIC v2.0,
this is not a full implementation of MPIC v4.2 -- e.g. it still has only
one bank of MSIs.

Signed-off-by: default avatarScott Wood <scottwood@freescale.com>
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
parent 03274d44
Loading
Loading
Loading
Loading
+226 −112
Original line number Diff line number Diff line
@@ -56,7 +56,7 @@ static const int debug_openpic = 0;
        } \
    } while (0)

#define MAX_CPU     15
#define MAX_CPU     32
#define MAX_SRC     256
#define MAX_TMR     4
#define MAX_IPI     4
@@ -66,6 +66,7 @@ static const int debug_openpic = 0;

/* OpenPIC capability flags */
#define OPENPIC_FLAG_IDR_CRIT     (1 << 0)
#define OPENPIC_FLAG_ILR          (2 << 0)

/* OpenPIC address map */
#define OPENPIC_GLB_REG_START        0x0
@@ -74,6 +75,8 @@ static const int debug_openpic = 0;
#define OPENPIC_TMR_REG_SIZE         0x220
#define OPENPIC_MSI_REG_START        0x1600
#define OPENPIC_MSI_REG_SIZE         0x200
#define OPENPIC_SUMMARY_REG_START   0x3800
#define OPENPIC_SUMMARY_REG_SIZE    0x800
#define OPENPIC_SRC_REG_START        0x10000
#define OPENPIC_SRC_REG_SIZE         (MAX_SRC * 0x20)
#define OPENPIC_CPU_REG_START        0x20000
@@ -94,33 +97,17 @@ static const int debug_openpic = 0;
/* First doorbell IRQ */
#define RAVEN_DBL_IRQ    (RAVEN_IPI_IRQ + (RAVEN_MAX_CPU * RAVEN_MAX_IPI))

/* FSL_MPIC_20 */
#define FSL_MPIC_20_MAX_CPU      1
#define FSL_MPIC_20_MAX_EXT     12
#define FSL_MPIC_20_MAX_INT     64
#define FSL_MPIC_20_MAX_IRQ     MAX_IRQ
typedef struct FslMpicInfo {
    int max_ext;
} FslMpicInfo;

/* Interrupt definitions */
/* IRQs, accessible through the IRQ region */
#define FSL_MPIC_20_EXT_IRQ      0x00
#define FSL_MPIC_20_INT_IRQ      0x10
#define FSL_MPIC_20_MSG_IRQ      0xb0
#define FSL_MPIC_20_MSI_IRQ      0xe0
/* These are available through separate regions, but
   for simplicity's sake mapped into the same number space */
#define FSL_MPIC_20_TMR_IRQ      0x100
#define FSL_MPIC_20_IPI_IRQ      0x104
static FslMpicInfo fsl_mpic_20 = {
    .max_ext = 12,
};

/*
 * Block Revision Register1 (BRR1): QEMU does not fully emulate
 * any version on MPIC. So to start with, set the IP version to 0.
 *
 * NOTE: This is Freescale MPIC specific register. Keep it here till
 * this code is refactored for different variants of OPENPIC and MPIC.
 */
#define FSL_BRR1_IPID (0x0040 << 16) /* 16 bit IP-block ID */
#define FSL_BRR1_IPMJ (0x00 << 8) /* 8 bit IP major number */
#define FSL_BRR1_IPMN 0x00 /* 8 bit IP minor number */
static FslMpicInfo fsl_mpic_42 = {
    .max_ext = 12,
};

#define FRR_NIRQ_SHIFT    16
#define FRR_NCPU_SHIFT     8
@@ -146,6 +133,49 @@ static const int debug_openpic = 0;
#define IDR_P1_SHIFT      1
#define IDR_P0_SHIFT      0

#define ILR_INTTGT_MASK   0x000000ff
#define ILR_INTTGT_INT    0x00
#define ILR_INTTGT_CINT   0x01 /* critical */
#define ILR_INTTGT_MCP    0x02 /* machine check */

/* The currently supported INTTGT values happen to be the same as QEMU's
 * openpic output codes, but don't depend on this.  The output codes
 * could change (unlikely, but...) or support could be added for
 * more INTTGT values.
 */
static const int inttgt_output[][2] = {
    { ILR_INTTGT_INT, OPENPIC_OUTPUT_INT },
    { ILR_INTTGT_CINT, OPENPIC_OUTPUT_CINT },
    { ILR_INTTGT_MCP, OPENPIC_OUTPUT_MCK },
};

static int inttgt_to_output(int inttgt)
{
    int i;

    for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) {
        if (inttgt_output[i][0] == inttgt) {
            return inttgt_output[i][1];
        }
    }

    fprintf(stderr, "%s: unsupported inttgt %d\n", __func__, inttgt);
    return OPENPIC_OUTPUT_INT;
}

static int output_to_inttgt(int output)
{
    int i;

    for (i = 0; i < ARRAY_SIZE(inttgt_output); i++) {
        if (inttgt_output[i][1] == output) {
            return inttgt_output[i][0];
        }
    }

    abort();
}

#define MSIIR_OFFSET       0x140
#define MSIIR_SRS_SHIFT    29
#define MSIIR_SRS_MASK     (0x7 << MSIIR_SRS_SHIFT)
@@ -230,6 +260,7 @@ typedef struct OpenPICState {
    MemoryRegion mem;

    /* Behavior control */
    FslMpicInfo *fsl;
    uint32_t model;
    uint32_t flags;
    uint32_t nb_irqs;
@@ -243,7 +274,7 @@ typedef struct OpenPICState {
    uint32_t mpic_mode_mask;

    /* Sub-regions */
    MemoryRegion sub_io_mem[5];
    MemoryRegion sub_io_mem[6];

    /* Global registers */
    uint32_t frr; /* Feature reporting register */
@@ -558,6 +589,15 @@ static inline uint32_t read_IRQreg_idr(OpenPICState *opp, int n_IRQ)
    return opp->src[n_IRQ].idr;
}

static inline uint32_t read_IRQreg_ilr(OpenPICState *opp, int n_IRQ)
{
    if (opp->flags & OPENPIC_FLAG_ILR) {
        return output_to_inttgt(opp->src[n_IRQ].output);
    }

    return 0xffffffff;
}

static inline uint32_t read_IRQreg_ivpr(OpenPICState *opp, int n_IRQ)
{
    return opp->src[n_IRQ].ivpr;
@@ -608,6 +648,19 @@ static inline void write_IRQreg_idr(OpenPICState *opp, int n_IRQ, uint32_t val)
    }
}

static inline void write_IRQreg_ilr(OpenPICState *opp, int n_IRQ, uint32_t val)
{
    if (opp->flags & OPENPIC_FLAG_ILR) {
        IRQSource *src = &opp->src[n_IRQ];

        src->output = inttgt_to_output(val & ILR_INTTGT_MASK);
        DPRINTF("Set ILR %d to 0x%08x, output %d\n", n_IRQ, src->idr,
                src->output);

        /* TODO: on MPIC v4.0 only, set nomask for non-INT */
    }
}

static inline void write_IRQreg_ivpr(OpenPICState *opp, int n_IRQ, uint32_t val)
{
    uint32_t mask;
@@ -874,17 +927,20 @@ static void openpic_src_write(void *opaque, hwaddr addr, uint64_t val,

    DPRINTF("%s: addr %#" HWADDR_PRIx " <= %08" PRIx64 "\n",
            __func__, addr, val);
    if (addr & 0xF) {
        return;
    }
    addr = addr & 0xFFF0;

    addr = addr & 0xffff;
    idx = addr >> 5;
    if (addr & 0x10) {
        /* EXDE / IFEDE / IEEDE */
        write_IRQreg_idr(opp, idx, val);
    } else {
        /* EXVP / IFEVP / IEEVP */

    switch (addr & 0x1f) {
    case 0x00:
        write_IRQreg_ivpr(opp, idx, val);
        break;
    case 0x10:
        write_IRQreg_idr(opp, idx, val);
        break;
    case 0x18:
        write_IRQreg_ilr(opp, idx, val);
        break;
    }
}

@@ -896,20 +952,23 @@ static uint64_t openpic_src_read(void *opaque, uint64_t addr, unsigned len)

    DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr);
    retval = 0xFFFFFFFF;
    if (addr & 0xF) {
        return retval;
    }
    addr = addr & 0xFFF0;

    addr = addr & 0xffff;
    idx = addr >> 5;
    if (addr & 0x10) {
        /* EXDE / IFEDE / IEEDE */
        retval = read_IRQreg_idr(opp, idx);
    } else {
        /* EXVP / IFEVP / IEEVP */

    switch (addr & 0x1f) {
    case 0x00:
        retval = read_IRQreg_ivpr(opp, idx);
        break;
    case 0x10:
        retval = read_IRQreg_idr(opp, idx);
        break;
    case 0x18:
        retval = read_IRQreg_ilr(opp, idx);
        break;
    }
    DPRINTF("%s: => 0x%08x\n", __func__, retval);

    DPRINTF("%s: => 0x%08x\n", __func__, retval);
    return retval;
}

@@ -977,6 +1036,26 @@ static uint64_t openpic_msi_read(void *opaque, hwaddr addr, unsigned size)
    return r;
}

static uint64_t openpic_summary_read(void *opaque, hwaddr addr, unsigned size)
{
    uint64_t r = 0;

    DPRINTF("%s: addr %#" HWADDR_PRIx "\n", __func__, addr);

    /* TODO: EISR/EIMR */

    return r;
}

static void openpic_summary_write(void *opaque, hwaddr addr, uint64_t val,
                                  unsigned size)
{
    DPRINTF("%s: addr %#" HWADDR_PRIx " <= 0x%08" PRIx64 "\n",
            __func__, addr, val);

    /* TODO: EISR/EIMR */
}

static void openpic_cpu_write_internal(void *opaque, hwaddr addr,
                                       uint32_t val, int idx)
{
@@ -1242,19 +1321,19 @@ static const MemoryRegionOps openpic_src_ops_be = {
    },
};

static const MemoryRegionOps openpic_msi_ops_le = {
static const MemoryRegionOps openpic_msi_ops_be = {
    .read = openpic_msi_read,
    .write = openpic_msi_write,
    .endianness = DEVICE_LITTLE_ENDIAN,
    .endianness = DEVICE_BIG_ENDIAN,
    .impl = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
};

static const MemoryRegionOps openpic_msi_ops_be = {
    .read = openpic_msi_read,
    .write = openpic_msi_write,
static const MemoryRegionOps openpic_summary_ops_be = {
    .read = openpic_summary_read,
    .write = openpic_summary_write,
    .endianness = DEVICE_BIG_ENDIAN,
    .impl = {
        .min_access_size = 4,
@@ -1387,78 +1466,128 @@ static int openpic_load(QEMUFile* f, void *opaque, int version_id)
typedef struct MemReg {
    const char             *name;
    MemoryRegionOps const  *ops;
    bool                   map;
    hwaddr      start_addr;
    ram_addr_t              size;
} MemReg;

static void fsl_common_init(OpenPICState *opp)
{
    int i;
    int virq = MAX_SRC;

    opp->vid = VID_REVISION_1_2;
    opp->vir = VIR_GENERIC;
    opp->vector_mask = 0xFFFF;
    opp->tfrr_reset = 0;
    opp->ivpr_reset = IVPR_MASK_MASK;
    opp->idr_reset = 1 << 0;
    opp->max_irq = MAX_IRQ;

    opp->irq_ipi0 = virq;
    virq += MAX_IPI;
    opp->irq_tim0 = virq;
    virq += MAX_TMR;

    assert(virq <= MAX_IRQ);

    opp->irq_msi = 224;

    msi_supported = true;
    for (i = 0; i < opp->fsl->max_ext; i++) {
        opp->src[i].level = false;
    }

    /* Internal interrupts, including message and MSI */
    for (i = 16; i < MAX_SRC; i++) {
        opp->src[i].type = IRQ_TYPE_FSLINT;
        opp->src[i].level = true;
    }

    /* timers and IPIs */
    for (i = MAX_SRC; i < virq; i++) {
        opp->src[i].type = IRQ_TYPE_FSLSPECIAL;
        opp->src[i].level = false;
    }
}

static void map_list(OpenPICState *opp, const MemReg *list, int *count)
{
    while (list->name) {
        assert(*count < ARRAY_SIZE(opp->sub_io_mem));

        memory_region_init_io(&opp->sub_io_mem[*count], list->ops, opp,
                              list->name, list->size);

        memory_region_add_subregion(&opp->mem, list->start_addr,
                                    &opp->sub_io_mem[*count]);

        (*count)++;
        list++;
    }
}

static int openpic_init(SysBusDevice *dev)
{
    OpenPICState *opp = FROM_SYSBUS(typeof (*opp), dev);
    int i, j;
    MemReg list_le[] = {
        {"glb", &openpic_glb_ops_le, true,
    int list_count = 0;
    static const MemReg list_le[] = {
        {"glb", &openpic_glb_ops_le,
                OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
        {"tmr", &openpic_tmr_ops_le, true,
        {"tmr", &openpic_tmr_ops_le,
                OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
        {"msi", &openpic_msi_ops_le, true,
                OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE},
        {"src", &openpic_src_ops_le, true,
        {"src", &openpic_src_ops_le,
                OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
        {"cpu", &openpic_cpu_ops_le, true,
        {"cpu", &openpic_cpu_ops_le,
                OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
        {NULL}
    };
    MemReg list_be[] = {
        {"glb", &openpic_glb_ops_be, true,
    static const MemReg list_be[] = {
        {"glb", &openpic_glb_ops_be,
                OPENPIC_GLB_REG_START, OPENPIC_GLB_REG_SIZE},
        {"tmr", &openpic_tmr_ops_be, true,
        {"tmr", &openpic_tmr_ops_be,
                OPENPIC_TMR_REG_START, OPENPIC_TMR_REG_SIZE},
        {"msi", &openpic_msi_ops_be, true,
                OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE},
        {"src", &openpic_src_ops_be, true,
        {"src", &openpic_src_ops_be,
                OPENPIC_SRC_REG_START, OPENPIC_SRC_REG_SIZE},
        {"cpu", &openpic_cpu_ops_be, true,
        {"cpu", &openpic_cpu_ops_be,
                OPENPIC_CPU_REG_START, OPENPIC_CPU_REG_SIZE},
        {NULL}
    };
    static const MemReg list_fsl[] = {
        {"msi", &openpic_msi_ops_be,
                OPENPIC_MSI_REG_START, OPENPIC_MSI_REG_SIZE},
        {"summary", &openpic_summary_ops_be,
                OPENPIC_SUMMARY_REG_START, OPENPIC_SUMMARY_REG_SIZE},
        {NULL}
    };
    MemReg *list;

    memory_region_init(&opp->mem, "openpic", 0x40000);

    switch (opp->model) {
    case OPENPIC_MODEL_FSL_MPIC_20:
    default:
        opp->fsl = &fsl_mpic_20;
        opp->brr1 = 0x00400200;
        opp->flags |= OPENPIC_FLAG_IDR_CRIT;
        opp->nb_irqs = 80;
        opp->vid = VID_REVISION_1_2;
        opp->vir = VIR_GENERIC;
        opp->vector_mask = 0xFFFF;
        opp->tfrr_reset = 0;
        opp->ivpr_reset = IVPR_MASK_MASK;
        opp->idr_reset = 1 << 0;
        opp->max_irq = FSL_MPIC_20_MAX_IRQ;
        opp->irq_ipi0 = FSL_MPIC_20_IPI_IRQ;
        opp->irq_tim0 = FSL_MPIC_20_TMR_IRQ;
        opp->irq_msi = FSL_MPIC_20_MSI_IRQ;
        opp->brr1 = FSL_BRR1_IPID | FSL_BRR1_IPMJ | FSL_BRR1_IPMN;
        /* XXX really only available as of MPIC 4.0 */
        opp->mpic_mode_mask = GCR_MODE_PROXY;
        opp->mpic_mode_mask = GCR_MODE_MIXED;

        msi_supported = true;
        list = list_be;
        fsl_common_init(opp);
        map_list(opp, list_be, &list_count);
        map_list(opp, list_fsl, &list_count);

        for (i = 0; i < FSL_MPIC_20_MAX_EXT; i++) {
            opp->src[i].level = false;
        }
        break;

        /* Internal interrupts, including message and MSI */
        for (i = 16; i < MAX_SRC; i++) {
            opp->src[i].type = IRQ_TYPE_FSLINT;
            opp->src[i].level = true;
        }
    case OPENPIC_MODEL_FSL_MPIC_42:
        opp->fsl = &fsl_mpic_42;
        opp->brr1 = 0x00400402;
        opp->flags |= OPENPIC_FLAG_ILR;
        opp->nb_irqs = 196;
        opp->mpic_mode_mask = GCR_MODE_PROXY;

        /* timers and IPIs */
        for (i = MAX_SRC; i < MAX_IRQ; i++) {
            opp->src[i].type = IRQ_TYPE_FSLSPECIAL;
            opp->src[i].level = false;
        }
        fsl_common_init(opp);
        map_list(opp, list_be, &list_count);
        map_list(opp, list_fsl, &list_count);

        break;

@@ -1475,29 +1604,14 @@ static int openpic_init(SysBusDevice *dev)
        opp->irq_tim0 = RAVEN_TMR_IRQ;
        opp->brr1 = -1;
        opp->mpic_mode_mask = GCR_MODE_MIXED;
        list = list_le;
        /* Don't map MSI region */
        list[2].map = false;

        /* Only UP supported today */
        if (opp->nb_cpus != 1) {
            return -EINVAL;
        }
        break;
    }

    memory_region_init(&opp->mem, "openpic", 0x40000);

    for (i = 0; i < ARRAY_SIZE(list_le); i++) {
        if (!list[i].map) {
            continue;
        }

        memory_region_init_io(&opp->sub_io_mem[i], list[i].ops, opp,
                              list[i].name, list[i].size);

        memory_region_add_subregion(&opp->mem, list[i].start_addr,
                                    &opp->sub_io_mem[i]);
        map_list(opp, list_le, &list_count);
        break;
    }

    for (i = 0; i < opp->nb_cpus; i++) {
+1 −0
Original line number Diff line number Diff line
@@ -13,5 +13,6 @@ enum {

#define OPENPIC_MODEL_RAVEN       0
#define OPENPIC_MODEL_FSL_MPIC_20 1
#define OPENPIC_MODEL_FSL_MPIC_42 2

#endif /* __OPENPIC_H__ */