// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/node.h>
#include <asm/div64.h>
#include "cxlpci.h"
#include "cxl.h"
static const guid_t acpi_cxl_qtg_id_guid =
GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071,
0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52);
#define HBIW_TO_NR_MAPS_SIZE (CXL_DECODER_MAX_INTERLEAVE + 1)
static const int hbiw_to_nr_maps[HBIW_TO_NR_MAPS_SIZE] = {
[1] = 0, [2] = 1, [3] = 0, [4] = 2, [6] = 1, [8] = 3, [12] = 2, [16] = 4
};
static const int valid_hbiw[] = { 1, 2, 3, 4, 6, 8, 12, 16 };
u64 cxl_do_xormap_calc(struct cxl_cxims_data *cximsd, u64 addr, int hbiw)
{
int nr_maps_to_apply = -1;
u64 val;
int pos;
/*
* Strictly validate hbiw since this function is used for testing and
* that nullifies any expectation of trusted parameters from the CXL
* Region Driver.
*/
for (int i = 0; i < ARRAY_SIZE(valid_hbiw); i++) {
if (valid_hbiw[i] == hbiw) {
nr_maps_to_apply = hbiw_to_nr_maps[hbiw];
break;
}
}
if (nr_maps_to_apply == -1 || nr_maps_to_apply > cximsd->nr_maps)
return ULLONG_MAX;
/*
* In regions using XOR interleave arithmetic the CXL HPA may not
* be the same as the SPA. This helper performs the SPA->CXL HPA
* or the CXL HPA->SPA translation. Since XOR is self-inverting,
* so is this function.
*
* For root decoders using xormaps (hbiw: 2,4,6,8,12,16) applying the
* xormaps will toggle a position bit.
*
* pos is the lowest set bit in an XORMAP
* val is the XORALLBITS(addr & XORMAP)
*
* XORALLBITS: The CXL spec (3.1 Table 9-22) defines XORALLBITS
* as an operation that outputs a single bit by XORing all the
* bits in the input (addr & xormap). Implement XORALLBITS using
* hweight64(). If the hamming weight is even the XOR of those
* bits results in val==0, if odd the XOR result is val==1.
*/
for (int i = 0; i < cximsd->nr_maps; i++) {
if (!cximsd->xormaps[i])
continue;
pos = __ffs(cximsd->xormaps[i]);
val = (hweight64(addr & cximsd->xormaps[i]) & 1);
addr = (addr & ~(1ULL << pos)) | (val << pos);
}
return addr;
}
EXPORT_SYMBOL_FOR_MODULES(cxl_do_xormap_calc, "cxl_translate");
static u64 cxl_apply_xor_maps(struct cxl_root_decoder *cxlrd, u64 addr)
{
int hbiw = cxlrd->cxlsd.nr_targets;
struct cxl_cxims_data *cximsd;
/* No xormaps for host bridge interleave ways of 1 or 3 */
if (hbiw == 1 || hbiw == 3)
return addr;
cximsd = cxlrd->platform_data;
return cxl_do_xormap_calc(cximsd, addr, hbiw);
}
struct cxl_cxims_context {
struct device *dev;
struct cxl_root_decoder *cxlrd;
};
static int cxl_parse_cxims(union acpi_subtable_headers *header, void *arg,
const unsigned long end)
{
struct acpi_cedt_cxims *cxims = (struct acpi_cedt_cxims *)header;
struct cxl_cxims_context *ctx = arg;
struct cxl_root_decoder *cxlrd = ctx->cxlrd;
struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
struct device *dev = ctx->dev;
struct cxl_cxims_data *cximsd;
unsigned int hbig, nr_maps;
int rc;
rc = eig_to_granularity(cxims->hbig, &hbig);
if (rc)
return rc;
/* Does this CXIMS entry apply to the given CXL Window? */
if (hbig != cxld->interleave_granularity)
return 0;
/* IW 1,3 do not use xormaps and skip this parsing entirely */
if (is_power_of_2(cxld->interleave_ways))