/*
* Copyright 2014 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/sched.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <asm/synch.h>
#include <misc/cxl-base.h>
#include "cxl.h"
#include "trace.h"
static int afu_control(struct cxl_afu *afu, u64 command,
u64 result, u64 mask, bool enabled)
{
u64 AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT);
int rc = 0;
spin_lock(&afu->afu_cntl_lock);
pr_devel("AFU command starting: %llx\n", command);
trace_cxl_afu_ctrl(afu, command);
cxl_p2n_write(afu, CXL_AFU_Cntl_An, AFU_Cntl | command);
AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
while ((AFU_Cntl & mask) != result) {
if (time_after_eq(jiffies, timeout)) {
dev_warn(&afu->dev, "WARNING: AFU control timed out!\n");
rc = -EBUSY;
goto out;
}
if (!cxl_ops->link_ok(afu->adapter)) {
afu->enabled = enabled;
rc = -EIO;
goto out;
}
pr_devel_ratelimited("AFU control... (0x%016llx)\n",
AFU_Cntl | command);
cpu_relax();
AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
};
pr_devel("AFU command complete: %llx\n", command);
afu->enabled = enabled;
out:
trace_cxl_afu_ctrl_done(afu, command, rc);
spin_unlock(&afu->afu_cntl_lock);
return rc;
}
static int afu_enable(struct cxl_afu *afu)
{
pr_devel("AFU enable request\n");
return afu_control(afu, CXL_AFU_Cntl_An_E,
CXL_AFU_Cntl_An_ES_Enabled,
CXL_AFU_Cntl_An_ES_MASK, true);
}
int cxl_afu_disable(struct cxl_afu *afu)
{
pr_devel("AFU disable request\n");
return afu_control(afu, 0, CXL_AFU_Cntl_An_ES_Disabled,
CXL_AFU_Cntl_An_ES_MASK, false);
}
/* This will disable as well as reset */
static int native_afu_reset(struct cxl_afu *afu)
{
pr_devel("AFU reset request\n");
return afu_control(afu, CXL_AFU_Cntl_An_RA,
CXL_AFU_Cntl_An_RS_Complete | CXL_AFU_Cntl_An_ES_Disabled,
CXL_AFU_Cntl_An_RS_MASK | CXL_AFU_Cntl_An_ES_MASK,
false);
}
static int native_afu_check_and_enable(struct cxl_afu *afu)
{
if (!cxl_ops->link_ok(afu->adapter)) {
WARN(1, "Refusing to enable afu while link down!\n");
return -EIO;
}
if (afu->enabled)
return 0;
return afu_enable(afu);
}
int cxl_psl_purge(struct cxl_afu *afu)
{
u64 PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An);
u64 AFU_Cntl = cxl_p2n_read(afu, CXL_AFU_Cntl_An);
u64 dsisr, dar;
u64 start, end;
unsigned long timeout = jiffies + (HZ * CXL_TIMEOUT);
int rc = 0;
trace_cxl_psl_ctrl(afu, CXL_PSL_SCNTL_An_Pc);
pr_devel("PSL purge request\n");
if (!cxl_ops->link_ok(afu->adapter)) {
dev_warn(&afu->dev, "PSL Purge called with link down, ignoring\n");
rc = -EIO;
goto out;
}
if ((AFU_Cntl & CXL_AFU_Cntl_An_ES_MASK) != CXL_AFU_Cntl_An_ES_Disabled) {
WARN(1, "psl_purge request while AFU not disabled!\n");
cxl_afu_disable(afu);
}
cxl_p1n_write(afu, CXL_PSL_SCNTL_An,
PSL_CNTL | CXL_PSL_SCNTL_An_Pc);
start = local_clock();
PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An);
while ((PSL_CNTL & CXL_PSL_SCNTL_An_Ps_MASK)
== CXL_PSL_SCNTL_An_Ps_Pending) {
if (time_after_eq(jiffies, timeout)) {
dev_warn(&afu->dev, "WARNING: PSL Purge timed out!\n");
rc = -EBUSY;
goto out;
}
if (!cxl_ops->link_ok(afu->adapter)) {
rc = -EIO;
goto out;
}
dsisr = cxl_p2n_read(afu, CXL_PSL_DSISR_An);
pr_devel_ratelimited("PSL purging... PSL_CNTL: 0x%016llx PSL_DSISR: 0x%016llx\n", PSL_CNTL, dsisr);
if (dsisr & CXL_PSL_DSISR_TRANS) {
dar = cxl_p2n_read(afu, CXL_PSL_DAR_An);
dev_notice(&afu->dev, "PSL purge terminating pending translation, DSISR: 0x%016llx, DAR: 0x%016llx\n", dsisr, dar);
cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_AE);
} else if (dsisr) {
dev_notice(&afu->dev, "PSL purge acknowledging pending non-translation fault, DSISR: 0x%016llx\n", dsisr);
cxl_p2n_write(afu, CXL_PSL_TFC_An, CXL_PSL_TFC_An_A);
} else {
cpu_relax();
}
PSL_CNTL = cxl_p1n_read(afu, CXL_PSL_SCNTL_An);
};
end = local_clock();
pr_devel("PSL purged in %lld ns\n", end - start);
cxl_p1n_write(afu, CXL_PSL_SCNTL_An,
PSL_CNTL &<