mirror of https://github.com/F-Stack/f-stack.git
353 lines
8.4 KiB
C
353 lines
8.4 KiB
C
|
/* SPDX-License-Identifier: BSD-3-Clause
|
||
|
* Copyright(c) 2010-2018 Intel Corporation
|
||
|
*/
|
||
|
|
||
|
#include "ifpga_feature_dev.h"
|
||
|
|
||
|
static u64
|
||
|
pr_err_handle(struct feature_fme_pr *fme_pr)
|
||
|
{
|
||
|
struct feature_fme_pr_status fme_pr_status;
|
||
|
unsigned long err_code;
|
||
|
u64 fme_pr_error;
|
||
|
int i;
|
||
|
|
||
|
fme_pr_status.csr = readq(&fme_pr->ccip_fme_pr_status);
|
||
|
if (!fme_pr_status.pr_status)
|
||
|
return 0;
|
||
|
|
||
|
err_code = readq(&fme_pr->ccip_fme_pr_err);
|
||
|
fme_pr_error = err_code;
|
||
|
|
||
|
for (i = 0; i < PR_MAX_ERR_NUM; i++) {
|
||
|
if (err_code & (1 << i))
|
||
|
dev_info(NULL, "%s\n", pr_err_msg[i]);
|
||
|
}
|
||
|
|
||
|
writeq(fme_pr_error, &fme_pr->ccip_fme_pr_err);
|
||
|
return fme_pr_error;
|
||
|
}
|
||
|
|
||
|
static int fme_pr_write_init(struct ifpga_fme_hw *fme_dev,
|
||
|
struct fpga_pr_info *info)
|
||
|
{
|
||
|
struct feature_fme_pr *fme_pr;
|
||
|
struct feature_fme_pr_ctl fme_pr_ctl;
|
||
|
struct feature_fme_pr_status fme_pr_status;
|
||
|
|
||
|
fme_pr = get_fme_feature_ioaddr_by_index(fme_dev,
|
||
|
FME_FEATURE_ID_PR_MGMT);
|
||
|
if (!fme_pr)
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (info->flags != FPGA_MGR_PARTIAL_RECONFIG)
|
||
|
return -EINVAL;
|
||
|
|
||
|
dev_info(fme_dev, "resetting PR before initiated PR\n");
|
||
|
|
||
|
fme_pr_ctl.csr = readq(&fme_pr->ccip_fme_pr_control);
|
||
|
fme_pr_ctl.pr_reset = 1;
|
||
|
writeq(fme_pr_ctl.csr, &fme_pr->ccip_fme_pr_control);
|
||
|
|
||
|
fme_pr_ctl.pr_reset_ack = 1;
|
||
|
|
||
|
if (fpga_wait_register_field(pr_reset_ack, fme_pr_ctl,
|
||
|
&fme_pr->ccip_fme_pr_control,
|
||
|
PR_WAIT_TIMEOUT, 1)) {
|
||
|
dev_err(fme_dev, "maximum PR timeout\n");
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
|
||
|
fme_pr_ctl.csr = readq(&fme_pr->ccip_fme_pr_control);
|
||
|
fme_pr_ctl.pr_reset = 0;
|
||
|
writeq(fme_pr_ctl.csr, &fme_pr->ccip_fme_pr_control);
|
||
|
|
||
|
dev_info(fme_dev, "waiting for PR resource in HW to be initialized and ready\n");
|
||
|
|
||
|
fme_pr_status.pr_host_status = PR_HOST_STATUS_IDLE;
|
||
|
|
||
|
if (fpga_wait_register_field(pr_host_status, fme_pr_status,
|
||
|
&fme_pr->ccip_fme_pr_status,
|
||
|
PR_WAIT_TIMEOUT, 1)) {
|
||
|
dev_err(fme_dev, "maximum PR timeout\n");
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
|
||
|
dev_info(fme_dev, "check if have any previous PR error\n");
|
||
|
pr_err_handle(fme_pr);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int fme_pr_write(struct ifpga_fme_hw *fme_dev,
|
||
|
int port_id, const char *buf, size_t count,
|
||
|
struct fpga_pr_info *info)
|
||
|
{
|
||
|
struct feature_fme_pr *fme_pr;
|
||
|
struct feature_fme_pr_ctl fme_pr_ctl;
|
||
|
struct feature_fme_pr_status fme_pr_status;
|
||
|
struct feature_fme_pr_data fme_pr_data;
|
||
|
int delay, pr_credit;
|
||
|
int ret = 0;
|
||
|
|
||
|
fme_pr = get_fme_feature_ioaddr_by_index(fme_dev,
|
||
|
FME_FEATURE_ID_PR_MGMT);
|
||
|
if (!fme_pr)
|
||
|
return -EINVAL;
|
||
|
|
||
|
dev_info(fme_dev, "set PR port ID and start request\n");
|
||
|
|
||
|
fme_pr_ctl.csr = readq(&fme_pr->ccip_fme_pr_control);
|
||
|
fme_pr_ctl.pr_regionid = port_id;
|
||
|
fme_pr_ctl.pr_start_req = 1;
|
||
|
writeq(fme_pr_ctl.csr, &fme_pr->ccip_fme_pr_control);
|
||
|
|
||
|
dev_info(fme_dev, "pushing data from bitstream to HW\n");
|
||
|
|
||
|
fme_pr_status.csr = readq(&fme_pr->ccip_fme_pr_status);
|
||
|
pr_credit = fme_pr_status.pr_credit;
|
||
|
|
||
|
while (count > 0) {
|
||
|
delay = 0;
|
||
|
while (pr_credit <= 1) {
|
||
|
if (delay++ > PR_WAIT_TIMEOUT) {
|
||
|
dev_err(fme_dev, "maximum try\n");
|
||
|
|
||
|
info->pr_err = pr_err_handle(fme_pr);
|
||
|
return info->pr_err ? -EIO : -ETIMEDOUT;
|
||
|
}
|
||
|
udelay(1);
|
||
|
|
||
|
fme_pr_status.csr = readq(&fme_pr->ccip_fme_pr_status);
|
||
|
pr_credit = fme_pr_status.pr_credit;
|
||
|
};
|
||
|
|
||
|
if (count >= fme_dev->pr_bandwidth) {
|
||
|
switch (fme_dev->pr_bandwidth) {
|
||
|
case 4:
|
||
|
fme_pr_data.rsvd = 0;
|
||
|
fme_pr_data.pr_data_raw = *((const u32 *)buf);
|
||
|
writeq(fme_pr_data.csr,
|
||
|
&fme_pr->ccip_fme_pr_data);
|
||
|
break;
|
||
|
default:
|
||
|
ret = -EFAULT;
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
buf += fme_dev->pr_bandwidth;
|
||
|
count -= fme_dev->pr_bandwidth;
|
||
|
pr_credit--;
|
||
|
} else {
|
||
|
WARN_ON(1);
|
||
|
ret = -EINVAL;
|
||
|
goto done;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int fme_pr_write_complete(struct ifpga_fme_hw *fme_dev,
|
||
|
struct fpga_pr_info *info)
|
||
|
{
|
||
|
struct feature_fme_pr *fme_pr;
|
||
|
struct feature_fme_pr_ctl fme_pr_ctl;
|
||
|
|
||
|
fme_pr = get_fme_feature_ioaddr_by_index(fme_dev,
|
||
|
FME_FEATURE_ID_PR_MGMT);
|
||
|
|
||
|
fme_pr_ctl.csr = readq(&fme_pr->ccip_fme_pr_control);
|
||
|
fme_pr_ctl.pr_push_complete = 1;
|
||
|
writeq(fme_pr_ctl.csr, &fme_pr->ccip_fme_pr_control);
|
||
|
|
||
|
dev_info(fme_dev, "green bitstream push complete\n");
|
||
|
dev_info(fme_dev, "waiting for HW to release PR resource\n");
|
||
|
|
||
|
fme_pr_ctl.pr_start_req = 0;
|
||
|
|
||
|
if (fpga_wait_register_field(pr_start_req, fme_pr_ctl,
|
||
|
&fme_pr->ccip_fme_pr_control,
|
||
|
PR_WAIT_TIMEOUT, 1)) {
|
||
|
printf("maximum try.\n");
|
||
|
return -ETIMEDOUT;
|
||
|
}
|
||
|
|
||
|
dev_info(fme_dev, "PR operation complete, checking status\n");
|
||
|
info->pr_err = pr_err_handle(fme_pr);
|
||
|
if (info->pr_err)
|
||
|
return -EIO;
|
||
|
|
||
|
dev_info(fme_dev, "PR done successfully\n");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int fpga_pr_buf_load(struct ifpga_fme_hw *fme_dev,
|
||
|
struct fpga_pr_info *info, const char *buf,
|
||
|
size_t count)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
info->state = FPGA_PR_STATE_WRITE_INIT;
|
||
|
ret = fme_pr_write_init(fme_dev, info);
|
||
|
if (ret) {
|
||
|
dev_err(fme_dev, "Error preparing FPGA for writing\n");
|
||
|
info->state = FPGA_PR_STATE_WRITE_INIT_ERR;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Write the FPGA image to the FPGA.
|
||
|
*/
|
||
|
info->state = FPGA_PR_STATE_WRITE;
|
||
|
ret = fme_pr_write(fme_dev, info->port_id, buf, count, info);
|
||
|
if (ret) {
|
||
|
dev_err(fme_dev, "Error while writing image data to FPGA\n");
|
||
|
info->state = FPGA_PR_STATE_WRITE_ERR;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* After all the FPGA image has been written, do the device specific
|
||
|
* steps to finish and set the FPGA into operating mode.
|
||
|
*/
|
||
|
info->state = FPGA_PR_STATE_WRITE_COMPLETE;
|
||
|
ret = fme_pr_write_complete(fme_dev, info);
|
||
|
if (ret) {
|
||
|
dev_err(fme_dev, "Error after writing image data to FPGA\n");
|
||
|
info->state = FPGA_PR_STATE_WRITE_COMPLETE_ERR;
|
||
|
return ret;
|
||
|
}
|
||
|
info->state = FPGA_PR_STATE_DONE;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int fme_pr(struct ifpga_hw *hw, u32 port_id, void *buffer, u32 size,
|
||
|
u64 *status)
|
||
|
{
|
||
|
struct feature_fme_header *fme_hdr;
|
||
|
struct feature_fme_capability fme_capability;
|
||
|
struct ifpga_fme_hw *fme = &hw->fme;
|
||
|
struct fpga_pr_info info;
|
||
|
struct ifpga_port_hw *port;
|
||
|
int ret = 0;
|
||
|
|
||
|
if (!buffer || size == 0)
|
||
|
return -EINVAL;
|
||
|
if (fme->state != IFPGA_FME_IMPLEMENTED)
|
||
|
return -EINVAL;
|
||
|
|
||
|
/*
|
||
|
* Padding extra zeros to align PR buffer with PR bandwidth, HW will
|
||
|
* ignore these zeros automatically.
|
||
|
*/
|
||
|
size = IFPGA_ALIGN(size, fme->pr_bandwidth);
|
||
|
|
||
|
/* get fme header region */
|
||
|
fme_hdr = get_fme_feature_ioaddr_by_index(fme,
|
||
|
FME_FEATURE_ID_HEADER);
|
||
|
if (!fme_hdr)
|
||
|
return -EINVAL;
|
||
|
|
||
|
/* check port id */
|
||
|
fme_capability.csr = readq(&fme_hdr->capability);
|
||
|
if (port_id >= fme_capability.num_ports) {
|
||
|
dev_err(fme, "port number more than maximum\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
memset(&info, 0, sizeof(struct fpga_pr_info));
|
||
|
info.flags = FPGA_MGR_PARTIAL_RECONFIG;
|
||
|
info.port_id = port_id;
|
||
|
|
||
|
spinlock_lock(&fme->lock);
|
||
|
|
||
|
/* get port device by port_id */
|
||
|
port = &hw->port[port_id];
|
||
|
|
||
|
/* Disable Port before PR */
|
||
|
fpga_port_disable(port);
|
||
|
|
||
|
ret = fpga_pr_buf_load(fme, &info, (void *)buffer, size);
|
||
|
|
||
|
*status = info.pr_err;
|
||
|
|
||
|
/* Re-enable Port after PR finished */
|
||
|
fpga_port_enable(port);
|
||
|
spinlock_unlock(&fme->lock);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int do_pr(struct ifpga_hw *hw, u32 port_id, void *buffer, u32 size, u64 *status)
|
||
|
{
|
||
|
struct bts_header *bts_hdr;
|
||
|
void *buf;
|
||
|
struct ifpga_port_hw *port;
|
||
|
int ret;
|
||
|
|
||
|
if (!buffer || size == 0) {
|
||
|
dev_err(hw, "invalid parameter\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
bts_hdr = (struct bts_header *)buffer;
|
||
|
|
||
|
if (is_valid_bts(bts_hdr)) {
|
||
|
dev_info(hw, "this is a valid bitsteam..\n");
|
||
|
size -= (sizeof(struct bts_header) +
|
||
|
bts_hdr->metadata_len);
|
||
|
buf = (u8 *)buffer + sizeof(struct bts_header) +
|
||
|
bts_hdr->metadata_len;
|
||
|
} else {
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
/* clean port error before do PR */
|
||
|
port = &hw->port[port_id];
|
||
|
ret = port_clear_error(port);
|
||
|
if (ret) {
|
||
|
dev_err(hw, "port cannot clear error\n");
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return fme_pr(hw, port_id, buf, size, status);
|
||
|
}
|
||
|
|
||
|
static int fme_pr_mgmt_init(struct feature *feature)
|
||
|
{
|
||
|
struct feature_fme_pr *fme_pr;
|
||
|
struct feature_header fme_pr_header;
|
||
|
struct ifpga_fme_hw *fme;
|
||
|
|
||
|
dev_info(NULL, "FME PR MGMT Init.\n");
|
||
|
|
||
|
fme = (struct ifpga_fme_hw *)feature->parent;
|
||
|
|
||
|
fme_pr = (struct feature_fme_pr *)feature->addr;
|
||
|
|
||
|
fme_pr_header.csr = readq(&fme_pr->header);
|
||
|
if (fme_pr_header.revision == 2) {
|
||
|
dev_info(NULL, "using 512-bit PR\n");
|
||
|
fme->pr_bandwidth = 64;
|
||
|
} else {
|
||
|
dev_info(NULL, "using 32-bit PR\n");
|
||
|
fme->pr_bandwidth = 4;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void fme_pr_mgmt_uinit(struct feature *feature)
|
||
|
{
|
||
|
UNUSED(feature);
|
||
|
|
||
|
dev_info(NULL, "FME PR MGMT UInit.\n");
|
||
|
}
|
||
|
|
||
|
struct feature_ops fme_pr_mgmt_ops = {
|
||
|
.init = fme_pr_mgmt_init,
|
||
|
.uinit = fme_pr_mgmt_uinit,
|
||
|
};
|