root/drivers/fpga/fpga-mgr.c
// 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->dev, "Error while parsing FPGA image header\n");
                mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
        }

        return ret;
}

/*
 * Copy scattered FPGA image fragments to temporary buffer and call the
 * low level driver's parse_header function. This should be called after
 * fpga_mgr_parse_header_sg_first() returned -EAGAIN. In case of success,
 * pointer to the newly allocated image header copy will be returned and
 * its size will be set into *ret_size. Returned buffer needs to be freed.
 */
static void *fpga_mgr_parse_header_sg(struct fpga_manager *mgr,
                                      struct fpga_image_info *info,
                                      struct sg_table *sgt, size_t *ret_size)
{
        size_t len, new_header_size, header_size = 0;
        char *new_buf, *buf = NULL;
        int ret;

        do {
                new_header_size = info->header_size;
                if (new_header_size <= header_size) {
                        dev_err(&mgr->dev, "Requested invalid header size\n");
                        ret = -EFAULT;
                        break;
                }

                new_buf = krealloc(buf, new_header_size, GFP_KERNEL);
                if (!new_buf) {
                        ret = -ENOMEM;
                        break;
                }

                buf = new_buf;

                len = sg_pcopy_to_buffer(sgt->sgl, sgt->nents,
                                         buf + header_size,
                                         new_header_size - header_size,
                                         header_size);
                if (len != new_header_size - header_size) {
                        ret = -EFAULT;
                        break;
                }

                header_size = new_header_size;
                ret = fpga_mgr_parse_header(mgr, info, buf, header_size);
        } while (ret == -EAGAIN);

        if (ret) {
                dev_err(&mgr->dev, "Error while parsing FPGA image header\n");
                mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
                kfree(buf);
                buf = ERR_PTR(ret);
        }

        *ret_size = header_size;

        return buf;
}

/*
 * Call the low level driver's write_init function. This will do the
 * device-specific things to get the FPGA into the state where it is ready to
 * receive an FPGA image. The low level driver gets to see at least first
 * info->header_size bytes in the buffer. If info->header_size is 0,
 * write_init will not get any bytes of image buffer.
 */
static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,
                                   struct fpga_image_info *info,
                                   const char *buf, size_t count)
{
        size_t header_size = info->header_size;
        int ret;

        mgr->state = FPGA_MGR_STATE_WRITE_INIT;

        if (header_size > count)
                ret = -EINVAL;
        else if (!header_size)
                ret = fpga_mgr_write_init(mgr, info, NULL, 0);
        else
                ret = fpga_mgr_write_init(mgr, info, buf, count);

        if (ret) {
                dev_err(&mgr->dev, "Error preparing FPGA for writing\n");
                mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR;
                return ret;
        }

        return 0;
}

static int fpga_mgr_prepare_sg(struct fpga_manager *mgr,
                               struct fpga_image_info *info,
                               struct sg_table *sgt)
{
        struct sg_mapping_iter miter;
        size_t len;
        char *buf;
        int ret;

        /* Short path. Low level driver don't care about image header. */
        if (!mgr->mops->initial_header_size && !mgr->mops->parse_header)
                return fpga_mgr_write_init_buf(mgr, info, NULL, 0);

        /*
         * First try to use miter to map the first fragment to access the
         * header, this is the typical path.
         */
        ret = fpga_mgr_parse_header_sg_first(mgr, info, sgt);
        /* If 0, header fits first fragment, call write_init on it */
        if (!ret) {
                sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
                if (sg_miter_next(&miter)) {
                        ret = fpga_mgr_write_init_buf(mgr, info, miter.addr,
                                                      miter.length);
                        sg_miter_stop(&miter);
                        return ret;
                }
                sg_miter_stop(&miter);
        /*
         * If -EAGAIN, more sg buffer is needed,
         * otherwise an error has occurred.
         */
        } else if (ret != -EAGAIN) {
                return ret;
        }

        /*
         * Copy the fragments into temporary memory.
         * Copying is done inside fpga_mgr_parse_header_sg().
         */
        buf = fpga_mgr_parse_header_sg(mgr, info, sgt, &len);
        if (IS_ERR(buf))
                return PTR_ERR(buf);

        ret = fpga_mgr_write_init_buf(mgr, info, buf, len);

        kfree(buf);

        return ret;
}

/**
 * fpga_mgr_buf_load_sg - load fpga from image in buffer from a scatter list
 * @mgr:        fpga manager
 * @info:       fpga image specific information
 * @sgt:        scatterlist table
 *
 * Step the low level fpga manager through the device-specific steps of getting
 * an FPGA ready to be configured, writing the image to it, then doing whatever
 * post-configuration steps necessary.  This code assumes the caller got the
 * mgr pointer from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is
 * not an error code.
 *
 * This is the preferred entry point for FPGA programming, it does not require
 * any contiguous kernel memory.
 *
 * Return: 0 on success, negative error code otherwise.
 */
static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr,
                                struct fpga_image_info *info,
                                struct sg_table *sgt)
{
        int ret;

        ret = fpga_mgr_prepare_sg(mgr, info, sgt);
        if (ret)
                return ret;

        /* Write the FPGA image to the FPGA. */
        mgr->state = FPGA_MGR_STATE_WRITE;
        if (mgr->mops->write_sg) {
                ret = fpga_mgr_write_sg(mgr, sgt);
        } else {
                size_t length, count = 0, data_size = info->data_size;
                struct sg_mapping_iter miter;

                sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);

                if (mgr->mops->skip_header &&
                    !sg_miter_skip(&miter, info->header_size)) {
                        ret = -EINVAL;
                        goto out;
                }

                while (sg_miter_next(&miter)) {
                        if (data_size)
                                length = min(miter.length, data_size - count);
                        else
                                length = miter.length;

                        ret = fpga_mgr_write(mgr, miter.addr, length);
                        if (ret)
                                break;

                        count += length;
                        if (data_size && count >= data_size)
                                break;
                }
                sg_miter_stop(&miter);
        }

out:
        if (ret) {
                dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
                mgr->state = FPGA_MGR_STATE_WRITE_ERR;
                return ret;
        }

        return fpga_mgr_write_complete(mgr, info);
}

static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr,
                                    struct fpga_image_info *info,
                                    const char *buf, size_t count)
{
        int ret;

        ret = fpga_mgr_parse_header_mapped(mgr, info, buf, count);
        if (ret)
                return ret;

        ret = fpga_mgr_write_init_buf(mgr, info, buf, count);
        if (ret)
                return ret;

        if (mgr->mops->skip_header) {
                buf += info->header_size;
                count -= info->header_size;
        }

        if (info->data_size)
                count = info->data_size;

        /*
         * Write the FPGA image to the FPGA.
         */
        mgr->state = FPGA_MGR_STATE_WRITE;
        ret = fpga_mgr_write(mgr, buf, count);
        if (ret) {
                dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
                mgr->state = FPGA_MGR_STATE_WRITE_ERR;
                return ret;
        }

        return fpga_mgr_write_complete(mgr, info);
}

/**
 * fpga_mgr_buf_load - load fpga from image in buffer
 * @mgr:        fpga manager
 * @info:       fpga image info
 * @buf:        buffer contain fpga image
 * @count:      byte count of buf
 *
 * Step the low level fpga manager through the device-specific steps of getting
 * an FPGA ready to be configured, writing the image to it, then doing whatever
 * post-configuration steps necessary.  This code assumes the caller got the
 * mgr pointer from of_fpga_mgr_get() and checked that it is not an error code.
 *
 * Return: 0 on success, negative error code otherwise.
 */
static int fpga_mgr_buf_load(struct fpga_manager *mgr,
                             struct fpga_image_info *info,
                             const char *buf, size_t count)
{
        struct page **pages;
        struct sg_table sgt;
        const void *p;
        int nr_pages;
        int index;
        int rc;

        /*
         * This is just a fast path if the caller has already created a
         * contiguous kernel buffer and the driver doesn't require SG, non-SG
         * drivers will still work on the slow path.
         */
        if (mgr->mops->write)
                return fpga_mgr_buf_load_mapped(mgr, info, buf, count);

        /*
         * Convert the linear kernel pointer into a sg_table of pages for use
         * by the driver.
         */
        nr_pages = DIV_ROUND_UP((unsigned long)buf + count, PAGE_SIZE) -
                   (unsigned long)buf / PAGE_SIZE;
        pages = kmalloc_objs(struct page *, nr_pages);
        if (!pages)
                return -ENOMEM;

        p = buf - offset_in_page(buf);
        for (index = 0; index < nr_pages; index++) {
                if (is_vmalloc_addr(p))
                        pages[index] = vmalloc_to_page(p);
                else
                        pages[index] = kmap_to_page((void *)p);
                if (!pages[index]) {
                        kfree(pages);
                        return -EFAULT;
                }
                p += PAGE_SIZE;
        }

        /*
         * The temporary pages list is used to code share the merging algorithm
         * in sg_alloc_table_from_pages
         */
        rc = sg_alloc_table_from_pages(&sgt, pages, index, offset_in_page(buf),
                                       count, GFP_KERNEL);
        kfree(pages);
        if (rc)
                return rc;

        rc = fpga_mgr_buf_load_sg(mgr, info, &sgt);
        sg_free_table(&sgt);

        return rc;
}

/**
 * fpga_mgr_firmware_load - request firmware and load to fpga
 * @mgr:        fpga manager
 * @info:       fpga image specific information
 * @image_name: name of image file on the firmware search path
 *
 * Request an FPGA image using the firmware class, then write out to the FPGA.
 * Update the state before each step to provide info on what step failed if
 * there is a failure.  This code assumes the caller got the mgr pointer
 * from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is not an error
 * code.
 *
 * Return: 0 on success, negative error code otherwise.
 */
static int fpga_mgr_firmware_load(struct fpga_manager *mgr,
                                  struct fpga_image_info *info,
                                  const char *image_name)
{
        struct device *dev = &mgr->dev;
        const struct firmware *fw;
        int ret;

        dev_info(dev, "writing %s to %s\n", image_name, mgr->name);

        mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ;

        ret = request_firmware(&fw, image_name, dev);
        if (ret) {
                mgr->state = FPGA_MGR_STATE_FIRMWARE_REQ_ERR;
                dev_err(dev, "Error requesting firmware %s\n", image_name);
                return ret;
        }

        ret = fpga_mgr_buf_load(mgr, info, fw->data, fw->size);

        release_firmware(fw);

        return ret;
}

/**
 * fpga_mgr_load - load FPGA from scatter/gather table, buffer, or firmware
 * @mgr:        fpga manager
 * @info:       fpga image information.
 *
 * Load the FPGA from an image which is indicated in @info.  If successful, the
 * FPGA ends up in operating mode.
 *
 * Return: 0 on success, negative error code otherwise.
 */
int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info)
{
        info->header_size = mgr->mops->initial_header_size;

        if (info->sgt)
                return fpga_mgr_buf_load_sg(mgr, info, info->sgt);
        if (info->buf && info->count)
                return fpga_mgr_buf_load(mgr, info, info->buf, info->count);
        if (info->firmware_name)
                return fpga_mgr_firmware_load(mgr, info, info->firmware_name);
        return -EINVAL;
}
EXPORT_SYMBOL_GPL(fpga_mgr_load);

static const char * const state_str[] = {
        [FPGA_MGR_STATE_UNKNOWN] =              "unknown",
        [FPGA_MGR_STATE_POWER_OFF] =            "power off",
        [FPGA_MGR_STATE_POWER_UP] =             "power up",
        [FPGA_MGR_STATE_RESET] =                "reset",

        /* requesting FPGA image from firmware */
        [FPGA_MGR_STATE_FIRMWARE_REQ] =         "firmware request",
        [FPGA_MGR_STATE_FIRMWARE_REQ_ERR] =     "firmware request error",

        /* Parse FPGA image header */
        [FPGA_MGR_STATE_PARSE_HEADER] =         "parse header",
        [FPGA_MGR_STATE_PARSE_HEADER_ERR] =     "parse header error",

        /* Preparing FPGA to receive image */
        [FPGA_MGR_STATE_WRITE_INIT] =           "write init",
        [FPGA_MGR_STATE_WRITE_INIT_ERR] =       "write init error",

        /* Writing image to FPGA */
        [FPGA_MGR_STATE_WRITE] =                "write",
        [FPGA_MGR_STATE_WRITE_ERR] =            "write error",

        /* Finishing configuration after image has been written */
        [FPGA_MGR_STATE_WRITE_COMPLETE] =       "write complete",
        [FPGA_MGR_STATE_WRITE_COMPLETE_ERR] =   "write complete error",

        /* FPGA reports to be in normal operating mode */
        [FPGA_MGR_STATE_OPERATING] =            "operating",
};

static ssize_t name_show(struct device *dev,
                         struct device_attribute *attr, char *buf)
{
        struct fpga_manager *mgr = to_fpga_manager(dev);

        return sprintf(buf, "%s\n", mgr->name);
}

static ssize_t state_show(struct device *dev,
                          struct device_attribute *attr, char *buf)
{
        struct fpga_manager *mgr = to_fpga_manager(dev);

        return sprintf(buf, "%s\n", state_str[mgr->state]);
}

static ssize_t status_show(struct device *dev,
                           struct device_attribute *attr, char *buf)
{
        struct fpga_manager *mgr = to_fpga_manager(dev);
        u64 status;
        int len = 0;

        status = fpga_mgr_status(mgr);

        if (status & FPGA_MGR_STATUS_OPERATION_ERR)
                len += sprintf(buf + len, "reconfig operation error\n");
        if (status & FPGA_MGR_STATUS_CRC_ERR)
                len += sprintf(buf + len, "reconfig CRC error\n");
        if (status & FPGA_MGR_STATUS_INCOMPATIBLE_IMAGE_ERR)
                len += sprintf(buf + len, "reconfig incompatible image\n");
        if (status & FPGA_MGR_STATUS_IP_PROTOCOL_ERR)
                len += sprintf(buf + len, "reconfig IP protocol error\n");
        if (status & FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR)
                len += sprintf(buf + len, "reconfig fifo overflow error\n");

        return len;
}

static DEVICE_ATTR_RO(name);
static DEVICE_ATTR_RO(state);
static DEVICE_ATTR_RO(status);

static struct attribute *fpga_mgr_attrs[] = {
        &dev_attr_name.attr,
        &dev_attr_state.attr,
        &dev_attr_status.attr,
        NULL,
};
ATTRIBUTE_GROUPS(fpga_mgr);

static struct fpga_manager *__fpga_mgr_get(struct device *mgr_dev)
{
        struct fpga_manager *mgr;

        mgr = to_fpga_manager(mgr_dev);

        if (!try_module_get(mgr->mops_owner))
                mgr = ERR_PTR(-ENODEV);

        return mgr;
}

static int fpga_mgr_dev_match(struct device *dev, const void *data)
{
        return dev->parent == data;
}

/**
 * fpga_mgr_get - Given a device, get a reference to an fpga mgr.
 * @dev:        parent device that fpga mgr was registered with
 *
 * Return: fpga manager struct or IS_ERR() condition containing error code.
 */
struct fpga_manager *fpga_mgr_get(struct device *dev)
{
        struct fpga_manager *mgr;
        struct device *mgr_dev;

        mgr_dev = class_find_device(&fpga_mgr_class, NULL, dev, fpga_mgr_dev_match);
        if (!mgr_dev)
                return ERR_PTR(-ENODEV);

        mgr = __fpga_mgr_get(mgr_dev);
        if (IS_ERR(mgr))
                put_device(mgr_dev);

        return mgr;
}
EXPORT_SYMBOL_GPL(fpga_mgr_get);

/**
 * of_fpga_mgr_get - Given a device node, get a reference to an fpga mgr.
 *
 * @node:       device node
 *
 * Return: fpga manager struct or IS_ERR() condition containing error code.
 */
struct fpga_manager *of_fpga_mgr_get(struct device_node *node)
{
        struct fpga_manager *mgr;
        struct device *mgr_dev;

        mgr_dev = class_find_device_by_of_node(&fpga_mgr_class, node);
        if (!mgr_dev)
                return ERR_PTR(-ENODEV);

        mgr = __fpga_mgr_get(mgr_dev);
        if (IS_ERR(mgr))
                put_device(mgr_dev);

        return mgr;
}
EXPORT_SYMBOL_GPL(of_fpga_mgr_get);

/**
 * fpga_mgr_put - release a reference to an fpga manager
 * @mgr:        fpga manager structure
 */
void fpga_mgr_put(struct fpga_manager *mgr)
{
        module_put(mgr->mops_owner);
        put_device(&mgr->dev);
}
EXPORT_SYMBOL_GPL(fpga_mgr_put);

/**
 * fpga_mgr_lock - Lock FPGA manager for exclusive use
 * @mgr:        fpga manager
 *
 * Given a pointer to FPGA Manager (from fpga_mgr_get() or
 * of_fpga_mgr_put()) attempt to get the mutex. The user should call
 * fpga_mgr_lock() and verify that it returns 0 before attempting to
 * program the FPGA.  Likewise, the user should call fpga_mgr_unlock
 * when done programming the FPGA.
 *
 * Return: 0 for success or -EBUSY
 */
int fpga_mgr_lock(struct fpga_manager *mgr)
{
        if (!mutex_trylock(&mgr->ref_mutex)) {
                dev_err(&mgr->dev, "FPGA manager is in use.\n");
                return -EBUSY;
        }

        return 0;
}
EXPORT_SYMBOL_GPL(fpga_mgr_lock);

/**
 * fpga_mgr_unlock - Unlock FPGA manager after done programming
 * @mgr:        fpga manager
 */
void fpga_mgr_unlock(struct fpga_manager *mgr)
{
        mutex_unlock(&mgr->ref_mutex);
}
EXPORT_SYMBOL_GPL(fpga_mgr_unlock);

/**
 * __fpga_mgr_register_full - create and register an FPGA Manager device
 * @parent:     fpga manager device from pdev
 * @info:       parameters for fpga manager
 * @owner:      owner module containing the ops
 *
 * The caller of this function is responsible for calling fpga_mgr_unregister().
 * Using devm_fpga_mgr_register_full() instead is recommended.
 *
 * Return: pointer to struct fpga_manager pointer or ERR_PTR()
 */
struct fpga_manager *
__fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
                         struct module *owner)
{
        const struct fpga_manager_ops *mops = info->mops;
        struct fpga_manager *mgr;
        int id, ret;

        if (!mops) {
                dev_err(parent, "Attempt to register without fpga_manager_ops\n");
                return ERR_PTR(-EINVAL);
        }

        if (!info->name || !strlen(info->name)) {
                dev_err(parent, "Attempt to register with no name!\n");
                return ERR_PTR(-EINVAL);
        }

        mgr = kzalloc_obj(*mgr);
        if (!mgr)
                return ERR_PTR(-ENOMEM);

        id = ida_alloc(&fpga_mgr_ida, GFP_KERNEL);
        if (id < 0) {
                ret = id;
                goto error_kfree;
        }

        mutex_init(&mgr->ref_mutex);

        mgr->mops_owner = owner;

        mgr->name = info->name;
        mgr->mops = info->mops;
        mgr->priv = info->priv;
        mgr->compat_id = info->compat_id;

        mgr->dev.class = &fpga_mgr_class;
        mgr->dev.groups = mops->groups;
        mgr->dev.parent = parent;
        mgr->dev.of_node = parent->of_node;
        mgr->dev.id = id;

        ret = dev_set_name(&mgr->dev, "fpga%d", id);
        if (ret)
                goto error_device;

        /*
         * Initialize framework state by requesting low level driver read state
         * from device.  FPGA may be in reset mode or may have been programmed
         * by bootloader or EEPROM.
         */
        mgr->state = fpga_mgr_state(mgr);

        ret = device_register(&mgr->dev);
        if (ret) {
                put_device(&mgr->dev);
                return ERR_PTR(ret);
        }

        return mgr;

error_device:
        ida_free(&fpga_mgr_ida, id);
error_kfree:
        kfree(mgr);

        return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(__fpga_mgr_register_full);

/**
 * __fpga_mgr_register - create and register an FPGA Manager device
 * @parent:     fpga manager device from pdev
 * @name:       fpga manager name
 * @mops:       pointer to structure of fpga manager ops
 * @priv:       fpga manager private data
 * @owner:      owner module containing the ops
 *
 * The caller of this function is responsible for calling fpga_mgr_unregister().
 * Using devm_fpga_mgr_register() instead is recommended. This simple
 * version of the register function should be sufficient for most users. The
 * fpga_mgr_register_full() function is available for users that need to pass
 * additional, optional parameters.
 *
 * Return: pointer to struct fpga_manager pointer or ERR_PTR()
 */
struct fpga_manager *
__fpga_mgr_register(struct device *parent, const char *name,
                    const struct fpga_manager_ops *mops, void *priv, struct module *owner)
{
        struct fpga_manager_info info = { 0 };

        info.name = name;
        info.mops = mops;
        info.priv = priv;

        return __fpga_mgr_register_full(parent, &info, owner);
}
EXPORT_SYMBOL_GPL(__fpga_mgr_register);

/**
 * fpga_mgr_unregister - unregister an FPGA manager
 * @mgr: fpga manager struct
 *
 * This function is intended for use in an FPGA manager driver's remove function.
 */
void fpga_mgr_unregister(struct fpga_manager *mgr)
{
        dev_info(&mgr->dev, "%s %s\n", __func__, mgr->name);

        /*
         * If the low level driver provides a method for putting fpga into
         * a desired state upon unregister, do it.
         */
        fpga_mgr_fpga_remove(mgr);

        device_unregister(&mgr->dev);
}
EXPORT_SYMBOL_GPL(fpga_mgr_unregister);

static void devm_fpga_mgr_unregister(struct device *dev, void *res)
{
        struct fpga_mgr_devres *dr = res;

        fpga_mgr_unregister(dr->mgr);
}

/**
 * __devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register()
 * @parent:     fpga manager device from pdev
 * @info:       parameters for fpga manager
 * @owner:      owner module containing the ops
 *
 * Return:  fpga manager pointer on success, negative error code otherwise.
 *
 * This is the devres variant of fpga_mgr_register_full() for which the unregister
 * function will be called automatically when the managing device is detached.
 */
struct fpga_manager *
__devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
                              struct module *owner)
{
        struct fpga_mgr_devres *dr;
        struct fpga_manager *mgr;

        dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL);
        if (!dr)
                return ERR_PTR(-ENOMEM);

        mgr = __fpga_mgr_register_full(parent, info, owner);
        if (IS_ERR(mgr)) {
                devres_free(dr);
                return mgr;
        }

        dr->mgr = mgr;
        devres_add(parent, dr);

        return mgr;
}
EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register_full);

/**
 * __devm_fpga_mgr_register - resource managed variant of fpga_mgr_register()
 * @parent:     fpga manager device from pdev
 * @name:       fpga manager name
 * @mops:       pointer to structure of fpga manager ops
 * @priv:       fpga manager private data
 * @owner:      owner module containing the ops
 *
 * Return:  fpga manager pointer on success, negative error code otherwise.
 *
 * This is the devres variant of fpga_mgr_register() for which the
 * unregister function will be called automatically when the managing
 * device is detached.
 */
struct fpga_manager *
__devm_fpga_mgr_register(struct device *parent, const char *name,
                         const struct fpga_manager_ops *mops, void *priv,
                         struct module *owner)
{
        struct fpga_manager_info info = { 0 };

        info.name = name;
        info.mops = mops;
        info.priv = priv;

        return __devm_fpga_mgr_register_full(parent, &info, owner);
}
EXPORT_SYMBOL_GPL(__devm_fpga_mgr_register);

static void fpga_mgr_dev_release(struct device *dev)
{
        struct fpga_manager *mgr = to_fpga_manager(dev);

        ida_free(&fpga_mgr_ida, mgr->dev.id);
        kfree(mgr);
}

static const struct class fpga_mgr_class = {
        .name = "fpga_manager",
        .dev_groups = fpga_mgr_groups,
        .dev_release = fpga_mgr_dev_release,
};

static int __init fpga_mgr_class_init(void)
{
        pr_info("FPGA manager framework\n");

        return class_register(&fpga_mgr_class);
}

static void __exit fpga_mgr_class_exit(void)
{
        class_unregister(&fpga_mgr_class);
        ida_destroy(&fpga_mgr_ida);
}

MODULE_AUTHOR("Alan Tull <atull@kernel.org>");
MODULE_DESCRIPTION("FPGA manager framework");
MODULE_LICENSE("GPL v2");

subsys_initcall(fpga_mgr_class_init);
module_exit(fpga_mgr_class_exit);