/*
* Copyright 2015 IBM Corp.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include "cxl.h"
#include "hcalls.h"
#include "trace.h"
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 = kzalloc(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(le), entries, &out);
pr_devel("length of available (entries: %i), vpd: %#llx\n",
entries, out);
if (!rc) {
/*
* hcall returns in 'out' the size of available VPDs.
* It fills the buffer with as much data as possible.
*/
if (out < len)
len = out;
rc = len;
if (out) {
for (i = 0; i < entries; i++) {
if (len < SG_BUFFER_SIZE)
tocopy = len;
else
tocopy = SG_BUFFER_SIZE;
memcpy(buf, vpd_buf[i], tocopy);
buf += tocopy;
len -= tocopy;
}
}
}
err2:
for (i = 0; i < entries; i++) {
if (vpd_buf[i])
free_page((unsigned long) vpd_buf[i]);
}
free_page((unsigned long) le);
err1:
kfree(vpd_buf);
return rc;
}
static int guest_get_irq_info(struct cxl_context *ctx, struct cxl_irq_info *info)
{
return cxl_h_collect_int_info(ctx->afu->guest->handle, ctx->process_token, info);
}
static irqreturn_t guest_psl_irq(int irq, void *data)
{
struct cxl_context *ctx = data;
struct cxl_irq_info irq_info;
int rc;
pr_devel(