/*
 * QEMU model of the RPU_PCIL RPU Power Control Interface Logic
 *
 * Copyright (c) 2022 Xilinx Inc.
 * Copyright (c) 2022-2024, Advanced Micro Devices, Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/* Autogenerated by xregqemu.py 2022-08-02.  */

#include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/register.h"
#include "qemu/bitops.h"
#include "qemu/log.h"
#include "migration/vmstate.h"
#include "hw/irq.h"
#include "hw/fdt_generic_util.h"

#ifndef XILINX_RPU_PCIL_ERR_DEBUG
#define XILINX_RPU_PCIL_ERR_DEBUG 0
#endif

#define TYPE_XILINX_RPU_PCIL "xlnx.rpu_pcil"

#define XILINX_RPU_PCIL(obj) \
     OBJECT_CHECK(RPU_PCIL, (obj), TYPE_XILINX_RPU_PCIL)

REG32(RPU_PCIL_A0_ISR, 0x0)
    FIELD(RPU_PCIL_A0_ISR, PACTIVE1, 0, 1)
REG32(RPU_PCIL_A0_IMR, 0x4)
    FIELD(RPU_PCIL_A0_IMR, PACTIVE1, 0, 1)
REG32(RPU_PCIL_A0_IEN, 0x8)
    FIELD(RPU_PCIL_A0_IEN, PACTIVE1, 0, 1)
REG32(RPU_PCIL_A0_IDS, 0xc)
    FIELD(RPU_PCIL_A0_IDS, PACTIVE1, 0, 1)
REG32(RPU_PCIL_A0_PR, 0x80)
    FIELD(RPU_PCIL_A0_PR, PREQ, 0, 1)
REG32(RPU_PCIL_A0_PS, 0x84)
    FIELD(RPU_PCIL_A0_PS, PSTATE, 0, 1)
REG32(RPU_PCIL_A0_PA, 0x88)
    FIELD(RPU_PCIL_A0_PA, PDENY, 9, 1)
    FIELD(RPU_PCIL_A0_PA, PACCEPT, 8, 1)
    FIELD(RPU_PCIL_A0_PA, PACTIVE, 0, 2)
REG32(RPU_PCIL_A0_PWRDWN, 0xc0)
    FIELD(RPU_PCIL_A0_PWRDWN, EN, 0, 1)
REG32(RPU_PCIL_A1_ISR, 0x100)
    FIELD(RPU_PCIL_A1_ISR, PACTIVE1, 0, 1)
REG32(RPU_PCIL_A1_IMR, 0x104)
    FIELD(RPU_PCIL_A1_IMR, PACTIVE1, 0, 1)
REG32(RPU_PCIL_A1_IEN, 0x108)
    FIELD(RPU_PCIL_A1_IEN, PACTIVE1, 0, 1)
REG32(RPU_PCIL_A1_IDS, 0x10c)
    FIELD(RPU_PCIL_A1_IDS, PACTIVE1, 0, 1)
REG32(RPU_PCIL_A1_PR, 0x180)
    FIELD(RPU_PCIL_A1_PR, PREQ, 0, 1)
REG32(RPU_PCIL_A1_PS, 0x184)
    FIELD(RPU_PCIL_A1_PS, PSTATE, 0, 1)
REG32(RPU_PCIL_A1_PA, 0x188)
    FIELD(RPU_PCIL_A1_PA, PDENY, 9, 1)
    FIELD(RPU_PCIL_A1_PA, PACCEPT, 8, 1)
    FIELD(RPU_PCIL_A1_PA, PACTIVE, 0, 2)
REG32(RPU_PCIL_A1_PWRDWN, 0x1c0)
    FIELD(RPU_PCIL_A1_PWRDWN, EN, 0, 1)
REG32(RPU_PCIL_B0_ISR, 0x1000)
    FIELD(RPU_PCIL_B0_ISR, PACTIVE1, 0, 1)
REG32(RPU_PCIL_B0_IMR, 0x1004)
    FIELD(RPU_PCIL_B0_IMR, PACTIVE1, 0, 1)
REG32(RPU_PCIL_B0_IEN, 0x1008)
    FIELD(RPU_PCIL_B0_IEN, PACTIVE1, 0, 1)
REG32(RPU_PCIL_B0_IDS, 0x100c)
    FIELD(RPU_PCIL_B0_IDS, PACTIVE1, 0, 1)
REG32(RPU_PCIL_B0_PR, 0x1080)
    FIELD(RPU_PCIL_B0_PR, PREQ, 0, 1)
REG32(RPU_PCIL_B0_PS, 0x1084)
    FIELD(RPU_PCIL_B0_PS, PSTATE, 0, 1)
REG32(RPU_PCIL_B0_PA, 0x1088)
    FIELD(RPU_PCIL_B0_PA, PDENY, 9, 1)
    FIELD(RPU_PCIL_B0_PA, PACCEPT, 8, 1)
    FIELD(RPU_PCIL_B0_PA, PACTIVE, 0, 2)
REG32(RPU_PCIL_B0_PWRDWN, 0x10c0)
    FIELD(RPU_PCIL_B0_PWRDWN, EN, 0, 1)
REG32(RPU_PCIL_B1_ISR, 0x1100)
    FIELD(RPU_PCIL_B1_ISR, PACTIVE1, 0, 1)
REG32(RPU_PCIL_B1_IMR, 0x1104)
    FIELD(RPU_PCIL_B1_IMR, PACTIVE1, 0, 1)
REG32(RPU_PCIL_B1_IEN, 0x1108)
    FIELD(RPU_PCIL_B1_IEN, PACTIVE1, 0, 1)
REG32(RPU_PCIL_B1_IDS, 0x110c)
    FIELD(RPU_PCIL_B1_IDS, PACTIVE1, 0, 1)
REG32(RPU_PCIL_B1_PR, 0x1180)
    FIELD(RPU_PCIL_B1_PR, PREQ, 0, 1)
REG32(RPU_PCIL_B1_PS, 0x1184)
    FIELD(RPU_PCIL_B1_PS, PSTATE, 0, 1)
REG32(RPU_PCIL_B1_PA, 0x1188)
    FIELD(RPU_PCIL_B1_PA, PDENY, 9, 1)
    FIELD(RPU_PCIL_B1_PA, PACCEPT, 8, 1)
    FIELD(RPU_PCIL_B1_PA, PACTIVE, 0, 2)
REG32(RPU_PCIL_B1_PWRDWN, 0x11c0)
    FIELD(RPU_PCIL_B1_PWRDWN, EN, 0, 1)
REG32(RPU_PCIL_PSM_STANDBY, 0x8000)
    FIELD(RPU_PCIL_PSM_STANDBY, WFE1_B, 7, 1)
    FIELD(RPU_PCIL_PSM_STANDBY, WFI1_B, 6, 1)
    FIELD(RPU_PCIL_PSM_STANDBY, WFE0_B, 5, 1)
    FIELD(RPU_PCIL_PSM_STANDBY, WFI0_B, 4, 1)
    FIELD(RPU_PCIL_PSM_STANDBY, WFE1_A, 3, 1)
    FIELD(RPU_PCIL_PSM_STANDBY, WFI1_A, 2, 1)
    FIELD(RPU_PCIL_PSM_STANDBY, WFE0_A, 1, 1)
    FIELD(RPU_PCIL_PSM_STANDBY, WFI0_A, 0, 1)
REG32(RPU_PCIL_PSM_IMR, 0x8004)
    FIELD(RPU_PCIL_PSM_IMR, WFE1_B, 7, 1)
    FIELD(RPU_PCIL_PSM_IMR, WFI1_B, 6, 1)
    FIELD(RPU_PCIL_PSM_IMR, WFE0_B, 5, 1)
    FIELD(RPU_PCIL_PSM_IMR, WFI0_B, 4, 1)
    FIELD(RPU_PCIL_PSM_IMR, WFE1_A, 3, 1)
    FIELD(RPU_PCIL_PSM_IMR, WFI1_A, 2, 1)
    FIELD(RPU_PCIL_PSM_IMR, WFE0_A, 1, 1)
    FIELD(RPU_PCIL_PSM_IMR, WFI0_A, 0, 1)
REG32(RPU_PCIL_PSM_IEN, 0x8008)
    FIELD(RPU_PCIL_PSM_IEN, WFE1_B, 7, 1)
    FIELD(RPU_PCIL_PSM_IEN, WFI1_B, 6, 1)
    FIELD(RPU_PCIL_PSM_IEN, WFE0_B, 5, 1)
    FIELD(RPU_PCIL_PSM_IEN, WFI0_B, 4, 1)
    FIELD(RPU_PCIL_PSM_IEN, WFE1_A, 3, 1)
    FIELD(RPU_PCIL_PSM_IEN, WFI1_A, 2, 1)
    FIELD(RPU_PCIL_PSM_IEN, WFE0_A, 1, 1)
    FIELD(RPU_PCIL_PSM_IEN, WFI0_A, 0, 1)
REG32(RPU_PCIL_PSM_IDS, 0x800c)
    FIELD(RPU_PCIL_PSM_IDS, WFE1_B, 7, 1)
    FIELD(RPU_PCIL_PSM_IDS, WFI1_B, 6, 1)
    FIELD(RPU_PCIL_PSM_IDS, WFE0_B, 5, 1)
    FIELD(RPU_PCIL_PSM_IDS, WFI0_B, 4, 1)
    FIELD(RPU_PCIL_PSM_IDS, WFE1_A, 3, 1)
    FIELD(RPU_PCIL_PSM_IDS, WFI1_A, 2, 1)
    FIELD(RPU_PCIL_PSM_IDS, WFE0_A, 1, 1)
    FIELD(RPU_PCIL_PSM_IDS, WFI0_A, 0, 1)

#define RPU_PCIL_R_MAX (R_RPU_PCIL_PSM_IDS + 1)
#define RPU_PCIL_MAX_RPU 4

typedef struct RPU_PCIL {
    SysBusDevice parent_obj;
    MemoryRegion iomem;
    qemu_irq irq_rpu_pcil_a0;
    qemu_irq irq_rpu_pcil_b0;
    qemu_irq irq_rpu_pcil_b1;
    qemu_irq irq_rpu_pcil_a1;
    qemu_irq irq_rpu_pcil_psm;
    qemu_irq rpu_powerdown_request[RPU_PCIL_MAX_RPU];

    uint32_t regs[RPU_PCIL_R_MAX];
    RegisterInfo regs_info[RPU_PCIL_R_MAX];
    uint32_t rpu_standby_state[RPU_PCIL_MAX_RPU];
} RPU_PCIL;

static void rpu_pcil_a0_update_irq(RPU_PCIL *s)
{
    bool pending = s->regs[R_RPU_PCIL_A0_ISR] & ~s->regs[R_RPU_PCIL_A0_IMR];

    qemu_set_irq(s->irq_rpu_pcil_a0, pending);
}

static void rpu_pcil_a0_isr_postw(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);

    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_a0_update_irq(s);
    }
}

static uint64_t rpu_pcil_a0_ien_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_A0_IMR] &= ~val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_a0_update_irq(s);
    }
    return 0;
}

static uint64_t rpu_pcil_a0_ids_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_A0_IMR] |= val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_a0_update_irq(s);
    }
    return 0;
}

static void rpu_pcil_b0_update_irq(RPU_PCIL *s)
{
    bool pending = s->regs[R_RPU_PCIL_B0_ISR] & ~s->regs[R_RPU_PCIL_B0_IMR];

    qemu_set_irq(s->irq_rpu_pcil_b0, pending);
}

static void rpu_pcil_b0_isr_postw(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);

    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_b0_update_irq(s);
    }
}

static uint64_t rpu_pcil_b0_ien_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_B0_IMR] &= ~val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_b0_update_irq(s);
    }
    return 0;
}

static uint64_t rpu_pcil_b0_ids_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_B0_IMR] |= val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_b0_update_irq(s);
    }
    return 0;
}

static void rpu_pcil_b1_update_irq(RPU_PCIL *s)
{
    bool pending = s->regs[R_RPU_PCIL_B1_ISR] & ~s->regs[R_RPU_PCIL_B1_IMR];

    qemu_set_irq(s->irq_rpu_pcil_b1, pending);
}

static void rpu_pcil_b1_isr_postw(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);

    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_b1_update_irq(s);
    }
}

static uint64_t rpu_pcil_b1_ien_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_B1_IMR] &= ~val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_b1_update_irq(s);
    }
    return 0;
}

static uint64_t rpu_pcil_b1_ids_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_B1_IMR] |= val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_b1_update_irq(s);
    }
    return 0;
}

static void rpu_pcil_a1_update_irq(RPU_PCIL *s)
{
    bool pending = s->regs[R_RPU_PCIL_A1_ISR] & ~s->regs[R_RPU_PCIL_A1_IMR];

    qemu_set_irq(s->irq_rpu_pcil_a1, pending);
}

static void rpu_pcil_a1_isr_postw(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);

    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_a1_update_irq(s);
    }
}

static uint64_t rpu_pcil_a1_ien_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_A1_IMR] &= ~val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_a1_update_irq(s);
    }
    return 0;
}

static uint64_t rpu_pcil_a1_ids_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_A1_IMR] |= val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_a1_update_irq(s);
    }
    return 0;
}

static void rpu_pcil_psm_update_irq(RPU_PCIL *s)
{
    bool pending = s->regs[R_RPU_PCIL_PSM_STANDBY]
        & ~s->regs[R_RPU_PCIL_PSM_IMR];

    qemu_set_irq(s->irq_rpu_pcil_psm, pending);
}

static void rpu_pcil_psm_standby_postw(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);

    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_psm_update_irq(s);
    }
}

static uint64_t rpu_pcil_psm_ien_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_PSM_IMR] &= ~val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_psm_update_irq(s);
    }
    return 0;
}

static uint64_t rpu_pcil_psm_ids_prew(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t val = val64;

    s->regs[R_RPU_PCIL_PSM_IMR] |= val;
    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_psm_update_irq(s);
    }
    return 0;
}

static void rpu_pcil_pr_postw(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);
    uint32_t blk_offset = reg->access->addr / 4 - R_RPU_PCIL_A0_PR;

    if (val64) {
        /* Validation not implemented
         * set it true untill we implement all requests
         */
        s->regs[blk_offset + R_RPU_PCIL_A0_PA] |=
                R_RPU_PCIL_A0_PA_PACCEPT_MASK;
    }
}

#define R_RPU_PCIL_PWRDWN(rpu)                                 \
    ((rpu) == 3) ? R_RPU_PCIL_B1_PWRDWN :                      \
    ((rpu) == 2) ? R_RPU_PCIL_B0_PWRDWN :                      \
    ((rpu) == 1) ? R_RPU_PCIL_A1_PWRDWN : R_RPU_PCIL_A0_PWRDWN

static uint32_t rpu_pcil_addr_to_rpu(uint32_t addr)
{
    addr = addr >> 2;

    if (addr >= R_RPU_PCIL_B1_ISR) {
        return 3;
    }

    if (addr >= R_RPU_PCIL_B0_ISR) {
        return 2;
    }

    if (addr >= R_RPU_PCIL_A1_ISR) {
        return 1;
    }

    return 0;
}

static void rpu_pcil_pwrdwn_update(RPU_PCIL *s, uint32_t rpu)
{
    int level;

    assert(rpu < RPU_PCIL_MAX_RPU);
    level = extract32(s->regs[R_RPU_PCIL_PWRDWN(rpu)],
                      R_RPU_PCIL_A0_PWRDWN_EN_SHIFT,
                      1) && s->rpu_standby_state[rpu];
    qemu_set_irq(s->rpu_powerdown_request[rpu], level);
}

static void rpu_pcil_pwrdwn_postw(RegisterInfo *reg, uint64_t val64)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(reg->opaque);

    if (!resettable_is_in_reset(OBJECT(s))) {
        rpu_pcil_pwrdwn_update(s, rpu_pcil_addr_to_rpu(reg->access->addr));
    }
}

static void rpu_standby_wfi(void *opaque, int n, int level)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(opaque);

    /*
     * NOTE that PSM_STANDBY is a sticky version of the rpu-standby-wfi
     * signals, while @rpu_standby_state reflects the current state of them and
     * can be used in order to update the powerdown-request signals.
     */
    s->rpu_standby_state[n] = level;
    s->regs[R_RPU_PCIL_PSM_STANDBY] |= (level ? 1 : 0)
        << (R_RPU_PCIL_PSM_STANDBY_WFI0_A_SHIFT + 2 * n);
    rpu_pcil_psm_update_irq(s);
    rpu_pcil_pwrdwn_update(s, n);
}

static const RegisterAccessInfo rpu_pcil_regs_info[] = {
    {   .name = "RPU_PCIL_A0_ISR",  .addr = A_RPU_PCIL_A0_ISR,
        .rsvd = 0xfffffffe,
        .w1c = 0x1,
        .post_write = rpu_pcil_a0_isr_postw,
    },{ .name = "RPU_PCIL_A0_IMR",  .addr = A_RPU_PCIL_A0_IMR,
        .reset = 0x1,
        .rsvd = 0xfffffffe,
        .ro = 0x1,
    },{ .name = "RPU_PCIL_A0_IEN",  .addr = A_RPU_PCIL_A0_IEN,
        .rsvd = 0xfffffffe,
        .pre_write = rpu_pcil_a0_ien_prew,
    },{ .name = "RPU_PCIL_A0_IDS",  .addr = A_RPU_PCIL_A0_IDS,
        .rsvd = 0xfffffffe,
        .pre_write = rpu_pcil_a0_ids_prew,
    },{ .name = "RPU_PCIL_A0_PR",  .addr = A_RPU_PCIL_A0_PR,
        .rsvd = 0xfffffffe,
        .post_write = rpu_pcil_pr_postw,
    },{ .name = "RPU_PCIL_A0_PS",  .addr = A_RPU_PCIL_A0_PS,
        .rsvd = 0xfffffffe,
    },{ .name = "RPU_PCIL_A0_PA",  .addr = A_RPU_PCIL_A0_PA,
        .rsvd = 0xfffffcfc,
        .ro = 0x303,
    },{ .name = "RPU_PCIL_A0_PWRDWN",  .addr = A_RPU_PCIL_A0_PWRDWN,
        .rsvd = 0xfffffffe,
        .post_write = rpu_pcil_pwrdwn_postw,
    },{ .name = "RPU_PCIL_A1_ISR",  .addr = A_RPU_PCIL_A1_ISR,
        .rsvd = 0xfffffffe,
        .w1c = 0x1,
        .post_write = rpu_pcil_a1_isr_postw,
    },{ .name = "RPU_PCIL_A1_IMR",  .addr = A_RPU_PCIL_A1_IMR,
        .reset = 0x1,
        .rsvd = 0xfffffffe,
        .ro = 0x1,
    },{ .name = "RPU_PCIL_A1_IEN",  .addr = A_RPU_PCIL_A1_IEN,
        .rsvd = 0xfffffffe,
        .pre_write = rpu_pcil_a1_ien_prew,
    },{ .name = "RPU_PCIL_A1_IDS",  .addr = A_RPU_PCIL_A1_IDS,
        .rsvd = 0xfffffffe,
        .pre_write = rpu_pcil_a1_ids_prew,
    },{ .name = "RPU_PCIL_A1_PR",  .addr = A_RPU_PCIL_A1_PR,
        .rsvd = 0xfffffffe,
        .post_write = rpu_pcil_pr_postw,
    },{ .name = "RPU_PCIL_A1_PS",  .addr = A_RPU_PCIL_A1_PS,
        .rsvd = 0xfffffffe,
    },{ .name = "RPU_PCIL_A1_PA",  .addr = A_RPU_PCIL_A1_PA,
        .rsvd = 0xfffffcfc,
        .ro = 0x303,
    },{ .name = "RPU_PCIL_A1_PWRDWN",  .addr = A_RPU_PCIL_A1_PWRDWN,
        .rsvd = 0xfffffffe,
        .post_write = rpu_pcil_pwrdwn_postw,
    },{ .name = "RPU_PCIL_B0_ISR",  .addr = A_RPU_PCIL_B0_ISR,
        .rsvd = 0xfffffffe,
        .w1c = 0x1,
        .post_write = rpu_pcil_b0_isr_postw,
    },{ .name = "RPU_PCIL_B0_IMR",  .addr = A_RPU_PCIL_B0_IMR,
        .reset = 0x1,
        .rsvd = 0xfffffffe,
        .ro = 0x1,
    },{ .name = "RPU_PCIL_B0_IEN",  .addr = A_RPU_PCIL_B0_IEN,
        .rsvd = 0xfffffffe,
        .pre_write = rpu_pcil_b0_ien_prew,
    },{ .name = "RPU_PCIL_B0_IDS",  .addr = A_RPU_PCIL_B0_IDS,
        .rsvd = 0xfffffffe,
        .pre_write = rpu_pcil_b0_ids_prew,
    },{ .name = "RPU_PCIL_B0_PR",  .addr = A_RPU_PCIL_B0_PR,
        .rsvd = 0xfffffffe,
        .post_write = rpu_pcil_pr_postw,
    },{ .name = "RPU_PCIL_B0_PS",  .addr = A_RPU_PCIL_B0_PS,
        .rsvd = 0xfffffffe,
    },{ .name = "RPU_PCIL_B0_PA",  .addr = A_RPU_PCIL_B0_PA,
        .rsvd = 0xfffffcfc,
        .ro = 0x303,
    },{ .name = "RPU_PCIL_B0_PWRDWN",  .addr = A_RPU_PCIL_B0_PWRDWN,
        .rsvd = 0xfffffffe,
        .post_write = rpu_pcil_pwrdwn_postw,
    },{ .name = "RPU_PCIL_B1_ISR",  .addr = A_RPU_PCIL_B1_ISR,
        .rsvd = 0xfffffffe,
        .w1c = 0x1,
        .post_write = rpu_pcil_b1_isr_postw,
    },{ .name = "RPU_PCIL_B1_IMR",  .addr = A_RPU_PCIL_B1_IMR,
        .reset = 0x1,
        .rsvd = 0xfffffffe,
        .ro = 0x1,
    },{ .name = "RPU_PCIL_B1_IEN",  .addr = A_RPU_PCIL_B1_IEN,
        .rsvd = 0xfffffffe,
        .pre_write = rpu_pcil_b1_ien_prew,
    },{ .name = "RPU_PCIL_B1_IDS",  .addr = A_RPU_PCIL_B1_IDS,
        .rsvd = 0xfffffffe,
        .pre_write = rpu_pcil_b1_ids_prew,
    },{ .name = "RPU_PCIL_B1_PR",  .addr = A_RPU_PCIL_B1_PR,
        .rsvd = 0xfffffffe,
        .post_write = rpu_pcil_pr_postw,
    },{ .name = "RPU_PCIL_B1_PS",  .addr = A_RPU_PCIL_B1_PS,
        .rsvd = 0xfffffffe,
    },{ .name = "RPU_PCIL_B1_PA",  .addr = A_RPU_PCIL_B1_PA,
        .rsvd = 0xfffffcfc,
        .ro = 0x303,
    },{ .name = "RPU_PCIL_B1_PWRDWN",  .addr = A_RPU_PCIL_B1_PWRDWN,
        .rsvd = 0xfffffffe,
        .post_write = rpu_pcil_pwrdwn_postw,
    },{ .name = "RPU_PCIL_PSM_STANDBY",  .addr = A_RPU_PCIL_PSM_STANDBY,
        .reset = 0x55,
        .rsvd = 0xffffff00,
        .w1c = 0xff,
        .post_write = rpu_pcil_psm_standby_postw,
    },{ .name = "RPU_PCIL_PSM_IMR",  .addr = A_RPU_PCIL_PSM_IMR,
        .reset = 0xff,
        .rsvd = 0xffffff00,
        .ro = 0xff,
    },{ .name = "RPU_PCIL_PSM_IEN",  .addr = A_RPU_PCIL_PSM_IEN,
        .rsvd = 0xffffff00,
        .pre_write = rpu_pcil_psm_ien_prew,
    },{ .name = "RPU_PCIL_PSM_IDS",  .addr = A_RPU_PCIL_PSM_IDS,
        .rsvd = 0xffffff00,
        .pre_write = rpu_pcil_psm_ids_prew,
    }
};

static void rpu_pcil_reset_enter(Object *obj, ResetType type)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(obj);
    unsigned int i;

    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
        register_reset(&s->regs_info[i]);
    }
}

static void rpu_pcil_reset_hold(Object *obj)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(obj);

    rpu_pcil_a0_update_irq(s);
    rpu_pcil_b0_update_irq(s);
    rpu_pcil_b1_update_irq(s);
    rpu_pcil_a1_update_irq(s);
    rpu_pcil_psm_update_irq(s);
    rpu_pcil_pwrdwn_update(s, 0);
    rpu_pcil_pwrdwn_update(s, 1);
    rpu_pcil_pwrdwn_update(s, 2);
    rpu_pcil_pwrdwn_update(s, 3);
}

static const MemoryRegionOps rpu_pcil_ops = {
    .read = register_read_memory,
    .write = register_write_memory,
    .endianness = DEVICE_LITTLE_ENDIAN,
    .valid = {
        .min_access_size = 4,
        .max_access_size = 4,
    },
};

static void rpu_pcil_init(Object *obj)
{
    RPU_PCIL *s = XILINX_RPU_PCIL(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    RegisterInfoArray *reg_array;

    memory_region_init(&s->iomem, obj, TYPE_XILINX_RPU_PCIL,
                       RPU_PCIL_R_MAX * 4);
    reg_array =
        register_init_block32(DEVICE(obj), rpu_pcil_regs_info,
                              ARRAY_SIZE(rpu_pcil_regs_info),
                              s->regs_info, s->regs,
                              &rpu_pcil_ops,
                              XILINX_RPU_PCIL_ERR_DEBUG,
                              RPU_PCIL_R_MAX * 4);
    memory_region_add_subregion(&s->iomem,
                                0x0,
                                &reg_array->mem);
    sysbus_init_mmio(sbd, &s->iomem);
    sysbus_init_irq(sbd, &s->irq_rpu_pcil_a0);
    sysbus_init_irq(sbd, &s->irq_rpu_pcil_b0);
    sysbus_init_irq(sbd, &s->irq_rpu_pcil_b1);
    sysbus_init_irq(sbd, &s->irq_rpu_pcil_a1);
    sysbus_init_irq(sbd, &s->irq_rpu_pcil_psm);
    qdev_init_gpio_out_named(DEVICE(obj), s->rpu_powerdown_request,
                             "rpu-pwrdwn-req", 4);
    qdev_init_gpio_in_named(DEVICE(obj), rpu_standby_wfi, "rpu-standby-wfi", 4);
}

static const VMStateDescription vmstate_rpu_pcil = {
    .name = TYPE_XILINX_RPU_PCIL,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32_ARRAY(regs, RPU_PCIL, RPU_PCIL_R_MAX),
        VMSTATE_END_OF_LIST(),
    }
};

static const FDTGenericGPIOSet rpu_pcil_controller_gpios[] = {
    {
        .names = &fdt_generic_gpio_name_set_gpio,
        .gpios = (FDTGenericGPIOConnection[]) {
            /* RPU powerdown request outputs to PSMX global.  */
            { .name = "rpu-pwrdwn-req", .fdt_index = 0, .range = 4},
            /* WFI STANDBY inputs from RPU cores.  */
            { .name = "rpu-standby-wfi", .fdt_index = 4, .range = 4},
            { },
        },
    },
    { },
};

static void rpu_pcil_class_init(ObjectClass *klass, void *data)
{
    ResettableClass *rc = RESETTABLE_CLASS(klass);
    DeviceClass *dc = DEVICE_CLASS(klass);
    FDTGenericGPIOClass *fggc = FDT_GENERIC_GPIO_CLASS(klass);

    dc->vmsd = &vmstate_rpu_pcil;
    rc->phases.enter = rpu_pcil_reset_enter;
    rc->phases.hold = rpu_pcil_reset_hold;
    fggc->controller_gpios = rpu_pcil_controller_gpios;
}

static const TypeInfo rpu_pcil_info = {
    .name          = TYPE_XILINX_RPU_PCIL,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(RPU_PCIL),
    .class_init    = rpu_pcil_class_init,
    .instance_init = rpu_pcil_init,
    .interfaces = (InterfaceInfo[]) {
        { TYPE_FDT_GENERIC_GPIO },
        { }
    },
};

static void rpu_pcil_register_types(void)
{
    type_register_static(&rpu_pcil_info);
}

type_init(rpu_pcil_register_types)
