// SPDX-License-Identifier: GPL-2.0
/*
* Driver for FPGA Accelerated Function Unit (AFU)
*
* Copyright (C) 2017-2018 Intel Corporation, Inc.
*
* Authors:
* Wu Hao <hao.wu@intel.com>
* Xiao Guangrong <guangrong.xiao@linux.intel.com>
* Joseph Grecco <joe.grecco@intel.com>
* Enno Luebbers <enno.luebbers@intel.com>
* Tim Whisonant <tim.whisonant@intel.com>
* Ananda Ravuri <ananda.ravuri@intel.com>
* Henry Mitchel <henry.mitchel@intel.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/uaccess.h>
#include <linux/fpga-dfl.h>
#include "dfl-afu.h"
/**
* __afu_port_enable - enable a port by clear reset
* @pdev: port platform device.
*
* Enable Port by clear the port soft reset bit, which is set by default.
* The AFU is unable to respond to any MMIO access while in reset.
* __afu_port_enable function should only be used after __afu_port_disable
* function.
*
* The caller needs to hold lock for protection.
*/
void __afu_port_enable(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
u64 v;
WARN_ON(!pdata->disable_count);
if (--pdata->disable_count != 0)
return;
base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
/* Clear port soft reset */
v = readq(base + PORT_HDR_CTRL);
v &= ~PORT_CTRL_SFTRST;
writeq(v, base + PORT_HDR_CTRL);
}
#define RST_POLL_INVL 10 /* us */
#define RST_POLL_TIMEOUT 1000 /* us */
/**
* __afu_port_disable - disable a port by hold reset
* @pdev: port platform device.
*
* Disable Port by setting the port soft reset bit, it puts the port into reset.
*
* The caller needs to hold lock for protection.
*/
int __afu_port_disable(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
void __iomem *base;
u64 v;
if (pdata->disable_count++ != 0)
return 0;
base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
/* Set port soft reset */
v = readq(base + PORT_HDR_CTRL);
v |= PORT_CTRL_SFTRST;
writeq(v, base + PORT_HDR_CTRL);
/*
* HW sets ack bit to 1 when all outstanding requests have been drained
* on this port and minimum soft reset pulse width has elapsed.
* Driver polls port_soft_reset_ack to determine if reset done by HW.
*/
if (readq_poll_timeout(base + PORT_HDR_CTRL, v,
v & PORT_CTRL_SFTRST_ACK,
RST_POLL_INVL, RST_POLL_TIMEOUT)) {
dev_err(&pdev->dev, "timeout, fail to reset device\n");
return -ETIMEDOUT;
}
return 0;
}
/*
* This function resets the FPGA Port and its accelerator (AFU) by function
* __port_disable and __port_enable (set port soft reset bit and then clear
* it). Userspace can do Port reset at any time, e.g. during DMA or Partial
* Reconfiguration. But it should never cause any system level issue, only
* functional failure (e.g. DMA or PR operation failure) and be recoverable
* from the failure.
*
* Note: the accelerator (AFU) is not accessible when its port is in reset
* (disabled). Any attempts on MMIO access to AFU while in reset, will
* result errors reported via port error reporting sub feature (if present).
*/
static int __port_reset(struct platform_device *pdev)
{
int ret;
ret = __afu_port_disable(pdev);
if (!ret)
__afu_port_enable(pdev);
return ret;
}
static int port_reset(struct platform_device *pdev)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
int ret;
mutex_lock(&pdata->lock);
ret = __port_reset(pdev);
mutex_unlock(&pdata->lock);
return ret;
}
static int port_get_id(struct platform_device *pdev)
{
void __iomem *base;
base = dfl_get_feature_ioaddr_by_id(&pdev->dev, PORT_FEATURE_ID_HEADER);
return FIELD_GET(PORT_CAP_PORT_NUM, readq(base + PORT_HDR_CAP));
}
static ssize_t
id_show(struct device *dev, struct device_attribute *attr, char *buf)
{
int id = port_get_id(to_platform_device(dev));
return scnprintf(buf, PAGE_SIZE, "%d\n", id);
}
static DEVICE_ATTR_RO(id);
static ssize_t
ltr_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
void __iomem *base;
u64 v;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
v = readq(base + PORT_HDR_CTRL);
mutex_unlock(&pdata->lock);
return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v));
}
static ssize_t
ltr_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
void __iomem *base;
bool ltr;
u64 v;
if (kstrtobool(buf, <r))
return -EINVAL;
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
mutex_lock(&pdata->lock);
v = r