// SPDX-License-Identifier: GPL-2.0
/*
* FPGA Manager Core
*
* Copyright (C) 2013-2015 Altera Corporation
* Copyright (C) 2017 Intel Corporation
*
* With code from the mailing list:
* Copyright (C) 2013 Xilinx, Inc.
*/
#include <linux/firmware.h>
#include <linux/fpga/fpga-mgr.h>
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/scatterlist.h>
#include <linux/highmem.h>
static DEFINE_IDA(fpga_mgr_ida);
static const struct class fpga_mgr_class;
struct fpga_mgr_devres {
struct fpga_manager *mgr;
};
static inline void fpga_mgr_fpga_remove(struct fpga_manager *mgr)
{
if (mgr->mops->fpga_remove)
mgr->mops->fpga_remove(mgr);
}
static inline enum fpga_mgr_states fpga_mgr_state(struct fpga_manager *mgr)
{
if (mgr->mops->state)
return mgr->mops->state(mgr);
return FPGA_MGR_STATE_UNKNOWN;
}
static inline u64 fpga_mgr_status(struct fpga_manager *mgr)
{
if (mgr->mops->status)
return mgr->mops->status(mgr);
return 0;
}
static inline int fpga_mgr_write(struct fpga_manager *mgr, const char *buf, size_t count)
{
if (mgr->mops->write)
return mgr->mops->write(mgr, buf, count);
return -EOPNOTSUPP;
}
/*
* After all the FPGA image has been written, do the device specific steps to
* finish and set the FPGA into operating mode.
*/
static inline int fpga_mgr_write_complete(struct fpga_manager *mgr,
struct fpga_image_info *info)
{
int ret = 0;
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE;
if (mgr->mops->write_complete)
ret = mgr->mops->write_complete(mgr, info);
if (ret) {
dev_err(&mgr->dev, "Error after writing image data to FPGA\n");
mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR;
return ret;
}
mgr->state = FPGA_MGR_STATE_OPERATING;
return 0;
}
static inline int fpga_mgr_parse_header(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
if (mgr->mops->parse_header)
return mgr->mops->parse_header(mgr, info, buf, count);
return 0;
}
static inline int fpga_mgr_write_init(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
if (mgr->mops->write_init)
return mgr->mops->write_init(mgr, info, buf, count);
return 0;
}
static inline int fpga_mgr_write_sg(struct fpga_manager *mgr,
struct sg_table *sgt)
{
if (mgr->mops->write_sg)
return mgr->mops->write_sg(mgr, sgt);
return -EOPNOTSUPP;
}
/**
* fpga_image_info_alloc - Allocate an FPGA image info struct
* @dev: owning device
*
* Return: struct fpga_image_info or NULL
*/
struct fpga_image_info *fpga_image_info_alloc(struct device *dev)
{
struct fpga_image_info *info;
get_device(dev);
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info) {
put_device(dev);
return NULL;
}
info->dev = dev;
return info;
}
EXPORT_SYMBOL_GPL(fpga_image_info_alloc);
/**
* fpga_image_info_free - Free an FPGA image info struct
* @info: FPGA image info struct to free
*/
void fpga_image_info_free(struct fpga_image_info *info)
{
struct device *dev;
if (!info)
return;
dev = info->dev;
if (info->firmware_name)
devm_kfree(dev, info->firmware_name);
devm_kfree(dev, info);
put_device(dev);
}
EXPORT_SYMBOL_GPL(fpga_image_info_free);
/*
* Call the low level driver's parse_header function with entire FPGA image
* buffer on the input. This will set info->header_size and info->data_size.
*/
static int fpga_mgr_parse_header_mapped(struct fpga_manager *mgr,
struct fpga_image_info *info,
const char *buf, size_t count)
{
int ret;
mgr->state = FPGA_MGR_STATE_PARSE_HEADER;
ret = fpga_mgr_parse_header(mgr, info, buf, count);
if (info->header_size + info->data_size > count) {
dev_err(&mgr->dev, "Bitstream data outruns FPGA image\n");
ret = -EINVAL;
}
if (ret) {
dev_err(&mgr->dev, "Error while parsing FPGA image header\n");
mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
}
return ret;
}
/*
* Call the low level driver's parse_header function with first fragment of
* scattered FPGA image on the input. If header fits first fragment,
* parse_header will set info->header_size and info->data_size. If it is not,
* parse_header will set desired size to info->header_size and -EAGAIN will be
* returned.
*/
static int fpga_mgr_parse_header_sg_first(struct fpga_manager *mgr,
struct fpga_image_info *info,
struct sg_table *sgt)
{
struct sg_mapping_iter miter;
int ret;
mgr->state = FPGA_MGR_STATE_PARSE_HEADER;
sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
if (sg_miter_next(&miter) &&
miter.length >= info->header_size)
ret = fpga_mgr_parse_header(mgr, info, miter.addr, miter.length);
else
ret = -EAGAIN;
sg_miter_stop(&miter);
if (ret && ret != -EAGAIN) {
dev_err(&mgr<