// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2015 IBM Corp.
*/
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include "cxl.h"
#include "hcalls.h"
#include "trace.h"
#define CXL_ERROR_DETECTED_EVENT 1
#define CXL_SLOT_RESET_EVENT 2
#define CXL_RESUME_EVENT 3
static void pci_error_handlers(struct cxl_afu *afu,
int bus_error_event,
pci_channel_state_t state)
{
struct pci_dev *afu_dev;
if (afu->phb == NULL)
return;
list_for_each_entry(afu_dev, &afu->phb->bus->devices, bus_list) {
if (!afu_dev->driver)
continue;
switch (bus_error_event) {
case CXL_ERROR_DETECTED_EVENT:
afu_dev->error_state = state;
if (afu_dev->driver->err_handler &&
afu_dev->driver->err_handler->error_detected)
afu_dev->driver->err_handler->error_detected(afu_dev, state);
break;
case CXL_SLOT_RESET_EVENT:
afu_dev->error_state = state;
if (afu_dev->driver->err_handler &&
afu_dev->driver->err_handler->slot_reset)
afu_dev->driver->err_handler->slot_reset(afu_dev);
break;
case CXL_RESUME_EVENT:
if (afu_dev->driver->err_handler &&
afu_dev->driver->err_handler->resume)
afu_dev->driver->err_handler->resume(afu_dev);
break;
}
}
}
static irqreturn_t guest_handle_psl_slice_error(struct cxl_context *ctx, u64 dsisr,
u64 errstat)
{
pr_devel("in %s\n", __func__);
dev_crit(&ctx->afu->dev, "PSL ERROR STATUS: 0x%.16llx\n", errstat);
return cxl_ops->ack_irq(ctx, 0, errstat);
}
static ssize_t guest_collect_vpd(struct cxl *adapter, struct cxl_afu *afu,
void *buf, size_t len)
{
unsigned int entries, mod;
unsigned long **vpd_buf = NULL;
struct sg_list *le;
int rc = 0, i, tocopy;
u64 out = 0;
if (buf == NULL)
return -EINVAL;
/* number of entries in the list */
entries = len / SG_BUFFER_SIZE;
mod = len % SG_BUFFER_SIZE;
if (mod)
entries++;
if (entries > SG_MAX_ENTRIES) {
entries = SG_MAX_ENTRIES;
len = SG_MAX_ENTRIES * SG_BUFFER_SIZE;
mod = 0;
}
vpd_buf = kcalloc(entries, sizeof(unsigned long *), GFP_KERNEL);
if (!vpd_buf)
return -ENOMEM;
le = (struct sg_list *)get_zeroed_page(GFP_KERNEL);
if (!le) {
rc = -ENOMEM;
goto err1;
}
for (i = 0; i < entries; i++) {
vpd_buf[i] = (unsigned long *)get_zeroed_page(GFP_KERNEL);
if (!vpd_buf[i]) {
rc = -ENOMEM;
goto err2;
}
le[i].phys_addr = cpu_to_be64(virt_to_phys(vpd_buf[i]));
le[i].len = cpu_to_be64(SG_BUFFER_SIZE);
if ((i == (entries - 1)) && mod)
le[i].len = cpu_to_be64(mod);
}
if (adapter)
rc = cxl_h_collect_vpd_adapter(adapter->guest->handle,
virt_to_phys(le), entries, &out);
else
rc = cxl_h_collect_vpd(afu->guest->handle, 0,
virt_to_phys<