110 lines
3.6 KiB
C
110 lines
3.6 KiB
C
|
/*
|
||
|
* CXL Utility library for devices
|
||
|
*
|
||
|
* Copyright(C) 2020 Intel Corporation.
|
||
|
*
|
||
|
* This work is licensed under the terms of the GNU GPL, version 2. See the
|
||
|
* COPYING file in the top-level directory.
|
||
|
*/
|
||
|
|
||
|
#include "qemu/osdep.h"
|
||
|
#include "qemu/log.h"
|
||
|
#include "hw/cxl/cxl.h"
|
||
|
|
||
|
/*
|
||
|
* Device registers have no restrictions per the spec, and so fall back to the
|
||
|
* default memory mapped register rules in 8.2:
|
||
|
* Software shall use CXL.io Memory Read and Write to access memory mapped
|
||
|
* register defined in this section. Unless otherwise specified, software
|
||
|
* shall restrict the accesses width based on the following:
|
||
|
* • A 32 bit register shall be accessed as a 1 Byte, 2 Bytes or 4 Bytes
|
||
|
* quantity.
|
||
|
* • A 64 bit register shall be accessed as a 1 Byte, 2 Bytes, 4 Bytes or 8
|
||
|
* Bytes
|
||
|
* • The address shall be a multiple of the access width, e.g. when
|
||
|
* accessing a register as a 4 Byte quantity, the address shall be
|
||
|
* multiple of 4.
|
||
|
* • The accesses shall map to contiguous bytes.If these rules are not
|
||
|
* followed, the behavior is undefined
|
||
|
*/
|
||
|
|
||
|
static uint64_t caps_reg_read(void *opaque, hwaddr offset, unsigned size)
|
||
|
{
|
||
|
CXLDeviceState *cxl_dstate = opaque;
|
||
|
|
||
|
if (size == 4) {
|
||
|
return cxl_dstate->caps_reg_state32[offset / sizeof(*cxl_dstate->caps_reg_state32)];
|
||
|
} else {
|
||
|
return cxl_dstate->caps_reg_state64[offset / sizeof(*cxl_dstate->caps_reg_state64)];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static uint64_t dev_reg_read(void *opaque, hwaddr offset, unsigned size)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const MemoryRegionOps dev_ops = {
|
||
|
.read = dev_reg_read,
|
||
|
.write = NULL, /* status register is read only */
|
||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||
|
.valid = {
|
||
|
.min_access_size = 1,
|
||
|
.max_access_size = 8,
|
||
|
.unaligned = false,
|
||
|
},
|
||
|
.impl = {
|
||
|
.min_access_size = 1,
|
||
|
.max_access_size = 8,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
static const MemoryRegionOps caps_ops = {
|
||
|
.read = caps_reg_read,
|
||
|
.write = NULL, /* caps registers are read only */
|
||
|
.endianness = DEVICE_LITTLE_ENDIAN,
|
||
|
.valid = {
|
||
|
.min_access_size = 1,
|
||
|
.max_access_size = 8,
|
||
|
.unaligned = false,
|
||
|
},
|
||
|
.impl = {
|
||
|
.min_access_size = 4,
|
||
|
.max_access_size = 8,
|
||
|
},
|
||
|
};
|
||
|
|
||
|
void cxl_device_register_block_init(Object *obj, CXLDeviceState *cxl_dstate)
|
||
|
{
|
||
|
/* This will be a BAR, so needs to be rounded up to pow2 for PCI spec */
|
||
|
memory_region_init(&cxl_dstate->device_registers, obj, "device-registers",
|
||
|
pow2ceil(CXL_MMIO_SIZE));
|
||
|
|
||
|
memory_region_init_io(&cxl_dstate->caps, obj, &caps_ops, cxl_dstate,
|
||
|
"cap-array", CXL_CAPS_SIZE);
|
||
|
memory_region_init_io(&cxl_dstate->device, obj, &dev_ops, cxl_dstate,
|
||
|
"device-status", CXL_DEVICE_STATUS_REGISTERS_LENGTH);
|
||
|
|
||
|
memory_region_add_subregion(&cxl_dstate->device_registers, 0,
|
||
|
&cxl_dstate->caps);
|
||
|
memory_region_add_subregion(&cxl_dstate->device_registers,
|
||
|
CXL_DEVICE_STATUS_REGISTERS_OFFSET,
|
||
|
&cxl_dstate->device);
|
||
|
}
|
||
|
|
||
|
static void device_reg_init_common(CXLDeviceState *cxl_dstate) { }
|
||
|
|
||
|
void cxl_device_register_init_common(CXLDeviceState *cxl_dstate)
|
||
|
{
|
||
|
uint64_t *cap_hdrs = cxl_dstate->caps_reg_state64;
|
||
|
const int cap_count = 1;
|
||
|
|
||
|
/* CXL Device Capabilities Array Register */
|
||
|
ARRAY_FIELD_DP64(cap_hdrs, CXL_DEV_CAP_ARRAY, CAP_ID, 0);
|
||
|
ARRAY_FIELD_DP64(cap_hdrs, CXL_DEV_CAP_ARRAY, CAP_VERSION, 1);
|
||
|
ARRAY_FIELD_DP64(cap_hdrs, CXL_DEV_CAP_ARRAY, CAP_COUNT, cap_count);
|
||
|
|
||
|
cxl_device_cap_init(cxl_dstate, DEVICE_STATUS, 1);
|
||
|
device_reg_init_common(cxl_dstate);
|
||
|
}
|