/*
 * QEMU model of the NPI_NIR NPI Root
 *
 * Copyright (c) 2021 Xilinx Inc.
 *
 * Autogenerated by xregqemu.py 2021-03-03.
 *
 * 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.
 */

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

#ifndef XILINX_NPI_NIR_ERR_DEBUG
#define XILINX_NPI_NIR_ERR_DEBUG 0
#endif

#define TYPE_XILINX_NPI_NIR "xlnx.npi-nir"

#define XILINX_NPI_NIR(obj) \
     OBJECT_CHECK(NPI_NIR, (obj), TYPE_XILINX_NPI_NIR)

REG32(REG_PCSR_MASK, 0x0)
    FIELD(REG_PCSR_MASK, TEST_SAFE, 20, 1)
    FIELD(REG_PCSR_MASK, SLVERREN, 19, 1)
    FIELD(REG_PCSR_MASK, GATEREG, 1, 1)
    FIELD(REG_PCSR_MASK, PCOMPLETE, 0, 1)
REG32(REG_PCSR_CONTROL, 0x4)
    FIELD(REG_PCSR_CONTROL, TEST_SAFE, 20, 1)
    FIELD(REG_PCSR_CONTROL, SLVERREN, 19, 1)
    FIELD(REG_PCSR_CONTROL, GATEREG, 1, 1)
    FIELD(REG_PCSR_CONTROL, PCOMPLETE, 0, 1)
REG32(REG_PCSR_STATUS, 0x8)
    FIELD(REG_PCSR_STATUS, HARD_FAIL_OR, 11, 3)
    FIELD(REG_PCSR_STATUS, HARD_FAIL_AND, 8, 3)
    FIELD(REG_PCSR_STATUS, MEM_CLEAR_PASS, 7, 1)
    FIELD(REG_PCSR_STATUS, MEM_CLEAR_DONE, 6, 1)
    FIELD(REG_PCSR_STATUS, CALERROR, 5, 1)
    FIELD(REG_PCSR_STATUS, CALDONE, 4, 1)
    FIELD(REG_PCSR_STATUS, INCAL, 3, 1)
    FIELD(REG_PCSR_STATUS, SCAN_CLEAR_PASS, 2, 1)
    FIELD(REG_PCSR_STATUS, SCAN_CLEAR_DONE, 1, 1)
    FIELD(REG_PCSR_STATUS, PCSRLOCK, 0, 1)
REG32(REG_PCSR_LOCK, 0xc)
    FIELD(REG_PCSR_LOCK, STATE, 0, 1)
REG32(REG_ITR, 0x40)
    FIELD(REG_ITR, AXI_WRSTRB_ERR, 6, 1)
    FIELD(REG_ITR, NIP_PORT_NOT_RDY, 5, 1)
    FIELD(REG_ITR, SLV_ERR, 4, 1)
    FIELD(REG_ITR, NIS_PORT_NOT_RDY, 3, 1)
    FIELD(REG_ITR, NIS_INVLD_ADDR, 2, 1)
    FIELD(REG_ITR, AXI_REQ_ERR, 1, 1)
    FIELD(REG_ITR, RESP_TIMEOUT_ERR, 0, 1)
REG32(REG_ISR, 0x44)
    FIELD(REG_ISR, AXI_WRSTRB_ERR, 6, 1)
    FIELD(REG_ISR, NIP_PORT_NOT_RDY, 5, 1)
    FIELD(REG_ISR, SLV_ERR, 4, 1)
    FIELD(REG_ISR, NIS_PORT_NOT_RDY, 3, 1)
    FIELD(REG_ISR, NIS_INVLD_ADDR, 2, 1)
    FIELD(REG_ISR, AXI_REQ_ERR, 1, 1)
    FIELD(REG_ISR, RESP_TIMEOUT_ERR, 0, 1)
REG32(REG_IMR0, 0x48)
    FIELD(REG_IMR0, AXI_WRSTRB_ERR, 6, 1)
    FIELD(REG_IMR0, NIP_PORT_NOT_RDY, 5, 1)
    FIELD(REG_IMR0, SLV_ERR, 4, 1)
    FIELD(REG_IMR0, NIS_PORT_NOT_RDY, 3, 1)
    FIELD(REG_IMR0, NIS_INVLD_ADDR, 2, 1)
    FIELD(REG_IMR0, AXI_REQ_ERR, 1, 1)
    FIELD(REG_IMR0, RESP_TIMEOUT_ERR, 0, 1)
REG32(REG_IER0, 0x4c)
    FIELD(REG_IER0, AXI_WRSTRB_ERR, 6, 1)
    FIELD(REG_IER0, NIP_PORT_NOT_RDY, 5, 1)
    FIELD(REG_IER0, SLV_ERR, 4, 1)
    FIELD(REG_IER0, NIS_PORT_NOT_RDY, 3, 1)
    FIELD(REG_IER0, NIS_INVLD_ADDR, 2, 1)
    FIELD(REG_IER0, AXI_REQ_ERR, 1, 1)
    FIELD(REG_IER0, RESP_TIMEOUT_ERR, 0, 1)
REG32(REG_IDR0, 0x50)
    FIELD(REG_IDR0, AXI_WRSTRB_ERR, 6, 1)
    FIELD(REG_IDR0, NIP_PORT_NOT_RDY, 5, 1)
    FIELD(REG_IDR0, SLV_ERR, 4, 1)
    FIELD(REG_IDR0, NIS_PORT_NOT_RDY, 3, 1)
    FIELD(REG_IDR0, NIS_INVLD_ADDR, 2, 1)
    FIELD(REG_IDR0, AXI_REQ_ERR, 1, 1)
    FIELD(REG_IDR0, RESP_TIMEOUT_ERR, 0, 1)
REG32(REG_IOR, 0x54)
    FIELD(REG_IOR, OFFSET, 0, 5)
REG32(REG_TIMEBASE_SEL, 0x100)
    FIELD(REG_TIMEBASE_SEL, TB5, 25, 5)
    FIELD(REG_TIMEBASE_SEL, TB4, 20, 5)
    FIELD(REG_TIMEBASE_SEL, TB3, 15, 5)
    FIELD(REG_TIMEBASE_SEL, TB2, 10, 5)
    FIELD(REG_TIMEBASE_SEL, TB1, 5, 5)
    FIELD(REG_TIMEBASE_SEL, TB0, 0, 5)
REG32(REG_SRVC, 0x104)
    FIELD(REG_SRVC, CMD_TYPE, 1, 2)
    FIELD(REG_SRVC, CMD_MODE, 0, 1)
REG32(REG_CNTL, 0x108)
    FIELD(REG_CNTL, PROG_TRACK_SIZE, 16, 7)
    FIELD(REG_CNTL, START_INDEX, 1, 9)
    FIELD(REG_CNTL, DIS_EARLY_W_RESP, 0, 1)
REG32(REG_STATUS, 0x200)
    FIELD(REG_STATUS, END_INDEX, 0, 9)
REG32(ERR_TYPE, 0x204)
    FIELD(ERR_TYPE, RESP_ERROR_TYPE, 0, 3)
REG32(ERR_LOG_P0_INFO_0, 0x208)
    FIELD(ERR_LOG_P0_INFO_0, ID, 16, 16)
    FIELD(ERR_LOG_P0_INFO_0, LOCK, 14, 1)
    FIELD(ERR_LOG_P0_INFO_0, BURST, 12, 2)
    FIELD(ERR_LOG_P0_INFO_0, LEN, 4, 8)
    FIELD(ERR_LOG_P0_INFO_0, AXSIZE, 1, 3)
    FIELD(ERR_LOG_P0_INFO_0, COMMAND_TYPE, 0, 1)
REG32(ERR_LOG_P0_INFO_1, 0x20c)
    FIELD(ERR_LOG_P0_INFO_1, ADDR, 0, 25)

#define NPI_NIR_R_MAX (R_ERR_LOG_P0_INFO_1 + 1)

#define LOCK_VAL 0xF9E8D7C6

typedef struct NPI_NIR {
    SysBusDevice parent_obj;
    MemoryRegion iomem;

    uint32_t regs[NPI_NIR_R_MAX];
    RegisterInfo regs_info[NPI_NIR_R_MAX];
} NPI_NIR;

static void noc_nir_lock_postw(RegisterInfo *reg, uint64_t val64)
{
    NPI_NIR *s = XILINX_NPI_NIR(reg->opaque);
    bool locked = val64 != LOCK_VAL;

    ARRAY_FIELD_DP32(s->regs, REG_PCSR_STATUS, PCSRLOCK, locked);
}

static const RegisterAccessInfo npi_nir_regs_info[] = {
    {   .name = "REG_PCSR_MASK",  .addr = A_REG_PCSR_MASK,
        .rsvd = 0xffe7fffc,
        .ro = 0x7fffc,
    },{ .name = "REG_PCSR_CONTROL",  .addr = A_REG_PCSR_CONTROL,
        .rsvd = 0xffe7fffc,
        .ro = 0x7fffc,
    },{ .name = "REG_PCSR_STATUS",  .addr = A_REG_PCSR_STATUS,
        .reset = R_REG_PCSR_STATUS_PCSRLOCK_MASK \
                 | R_REG_PCSR_STATUS_MEM_CLEAR_PASS_MASK \
                 | R_REG_PCSR_STATUS_MEM_CLEAR_DONE_MASK \
                 | R_REG_PCSR_STATUS_CALDONE_MASK \
                 | R_REG_PCSR_STATUS_SCAN_CLEAR_PASS_MASK \
                 | R_REG_PCSR_STATUS_SCAN_CLEAR_DONE_MASK,
        .rsvd = 0xffffc000,
        .ro = 0x3fff,
    },{ .name = "REG_PCSR_LOCK",  .addr = A_REG_PCSR_LOCK,
        .reset = 0x1,
        .rsvd = 0xfffffffe,
        .post_write = noc_nir_lock_postw
    },{ .name = "REG_ITR",  .addr = A_REG_ITR,
        .rsvd = 0xffffff80,
    },{ .name = "REG_ISR",  .addr = A_REG_ISR,
        .rsvd = 0xffffff80,
        .w1c = 0x7f,
    },{ .name = "REG_IMR0",  .addr = A_REG_IMR0,
        .reset = 0x7f,
        .rsvd = 0xffffff80,
        .ro = 0x7f,
    },{ .name = "REG_IER0",  .addr = A_REG_IER0,
        .rsvd = 0xffffff80,
    },{ .name = "REG_IDR0",  .addr = A_REG_IDR0,
        .rsvd = 0xffffff80,
    },{ .name = "REG_IOR",  .addr = A_REG_IOR,
        .reset = 0xe,
        .rsvd = 0xffffffe0,
    },{ .name = "REG_TIMEBASE_SEL",  .addr = A_REG_TIMEBASE_SEL,
        .reset = 0x398b2310,
        .rsvd = 0xc0000000,
    },{ .name = "REG_SRVC",  .addr = A_REG_SRVC,
        .rsvd = 0xfffffff8,
    },{ .name = "REG_CNTL",  .addr = A_REG_CNTL,
        .reset = 0x400002,
        .rsvd = 0xff80fc00,
        .ro = 0xfc00,
    },{ .name = "REG_STATUS",  .addr = A_REG_STATUS,
        .rsvd = 0xfffffe00,
        .ro = 0x1ff,
    },{ .name = "ERR_TYPE",  .addr = A_ERR_TYPE,
        .rsvd = 0xfffffff8,
    },{ .name = "ERR_LOG_P0_INFO_0",  .addr = A_ERR_LOG_P0_INFO_0,
        .rsvd = 0x8000,
        .ro = 0x8000,
    },{ .name = "ERR_LOG_P0_INFO_1",  .addr = A_ERR_LOG_P0_INFO_1,
        .rsvd = 0xfe000000,
    }
};

static void npi_nir_reset(DeviceState *dev)
{
    NPI_NIR *s = XILINX_NPI_NIR(dev);
    unsigned int i;

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

}

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

static void npi_nir_realize(DeviceState *dev, Error **errp)
{
    /* Delete this if you don't need it */
}

static void npi_nir_init(Object *obj)
{
    NPI_NIR *s = XILINX_NPI_NIR(obj);
    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
    RegisterInfoArray *reg_array;

    memory_region_init(&s->iomem, obj, TYPE_XILINX_NPI_NIR, NPI_NIR_R_MAX * 4);
    reg_array =
        register_init_block32(DEVICE(obj), npi_nir_regs_info,
                              ARRAY_SIZE(npi_nir_regs_info),
                              s->regs_info, s->regs,
                              &npi_nir_ops,
                              XILINX_NPI_NIR_ERR_DEBUG,
                              NPI_NIR_R_MAX * 4);
    memory_region_add_subregion(&s->iomem,
                                0x0,
                                &reg_array->mem);
    sysbus_init_mmio(sbd, &s->iomem);
}

static const VMStateDescription vmstate_npi_nir = {
    .name = TYPE_XILINX_NPI_NIR,
    .version_id = 1,
    .minimum_version_id = 1,
    .fields = (VMStateField[]) {
        VMSTATE_UINT32_ARRAY(regs, NPI_NIR, NPI_NIR_R_MAX),
        VMSTATE_END_OF_LIST(),
    }
};

static void npi_nir_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

    dc->reset = npi_nir_reset;
    dc->realize = npi_nir_realize;
    dc->vmsd = &vmstate_npi_nir;
}

static const TypeInfo npi_nir_info = {
    .name          = TYPE_XILINX_NPI_NIR,
    .parent        = TYPE_SYS_BUS_DEVICE,
    .instance_size = sizeof(NPI_NIR),
    .class_init    = npi_nir_class_init,
    .instance_init = npi_nir_init,
};

static void npi_nir_register_types(void)
{
    type_register_static(&npi_nir_info);
}

type_init(npi_nir_register_types)
