mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-26 14:17:26 -04:00
[ Upstream commit 4d4d2d4346857bf778fafaa97d6f76bb1663e3c9 ]
The current implementation of the fpga manager assumes that the low-level
module registers a driver for the parent device and uses its owner pointer
to take the module's refcount. This approach is problematic since it can
lead to a null pointer dereference while attempting to get the manager if
the parent device does not have a driver.
To address this problem, add a module owner pointer to the fpga_manager
struct and use it to take the module's refcount. Modify the functions for
registering the manager to take an additional owner module parameter and
rename them to avoid conflicts. Use the old function names for helper
macros that automatically set the module that registers the manager as the
owner. This ensures compatibility with existing low-level control modules
and reduces the chances of registering a manager without setting the owner.
Also, update the documentation to keep it consistent with the new interface
for registering an fpga manager.
Other changes: opportunistically move put_device() from __fpga_mgr_get() to
fpga_mgr_get() and of_fpga_mgr_get() to improve code clarity since the
manager device is taken in these functions.
Fixes: 654ba4cc0f
("fpga manager: ensure lifetime with of_fpga_mgr_get")
Suggested-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Suggested-by: Xu Yilun <yilun.xu@intel.com>
Signed-off-by: Marco Pagani <marpagan@redhat.com>
Acked-by: Xu Yilun <yilun.xu@intel.com>
Link: https://lore.kernel.org/r/20240305192926.84886-1-marpagan@redhat.com
Signed-off-by: Xu Yilun <yilun.xu@linux.intel.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
261 lines
8.9 KiB
C
261 lines
8.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
/*
|
|
* FPGA Framework
|
|
*
|
|
* Copyright (C) 2013-2016 Altera Corporation
|
|
* Copyright (C) 2017 Intel Corporation
|
|
*/
|
|
#ifndef _LINUX_FPGA_MGR_H
|
|
#define _LINUX_FPGA_MGR_H
|
|
|
|
#include <linux/mutex.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
struct fpga_manager;
|
|
struct sg_table;
|
|
|
|
/**
|
|
* enum fpga_mgr_states - fpga framework states
|
|
* @FPGA_MGR_STATE_UNKNOWN: can't determine state
|
|
* @FPGA_MGR_STATE_POWER_OFF: FPGA power is off
|
|
* @FPGA_MGR_STATE_POWER_UP: FPGA reports power is up
|
|
* @FPGA_MGR_STATE_RESET: FPGA in reset state
|
|
* @FPGA_MGR_STATE_FIRMWARE_REQ: firmware request in progress
|
|
* @FPGA_MGR_STATE_FIRMWARE_REQ_ERR: firmware request failed
|
|
* @FPGA_MGR_STATE_PARSE_HEADER: parse FPGA image header
|
|
* @FPGA_MGR_STATE_PARSE_HEADER_ERR: Error during PARSE_HEADER stage
|
|
* @FPGA_MGR_STATE_WRITE_INIT: preparing FPGA for programming
|
|
* @FPGA_MGR_STATE_WRITE_INIT_ERR: Error during WRITE_INIT stage
|
|
* @FPGA_MGR_STATE_WRITE: writing image to FPGA
|
|
* @FPGA_MGR_STATE_WRITE_ERR: Error while writing FPGA
|
|
* @FPGA_MGR_STATE_WRITE_COMPLETE: Doing post programming steps
|
|
* @FPGA_MGR_STATE_WRITE_COMPLETE_ERR: Error during WRITE_COMPLETE
|
|
* @FPGA_MGR_STATE_OPERATING: FPGA is programmed and operating
|
|
*/
|
|
enum fpga_mgr_states {
|
|
/* default FPGA states */
|
|
FPGA_MGR_STATE_UNKNOWN,
|
|
FPGA_MGR_STATE_POWER_OFF,
|
|
FPGA_MGR_STATE_POWER_UP,
|
|
FPGA_MGR_STATE_RESET,
|
|
|
|
/* getting an image for loading */
|
|
FPGA_MGR_STATE_FIRMWARE_REQ,
|
|
FPGA_MGR_STATE_FIRMWARE_REQ_ERR,
|
|
|
|
/* write sequence: parse header, init, write, complete */
|
|
FPGA_MGR_STATE_PARSE_HEADER,
|
|
FPGA_MGR_STATE_PARSE_HEADER_ERR,
|
|
FPGA_MGR_STATE_WRITE_INIT,
|
|
FPGA_MGR_STATE_WRITE_INIT_ERR,
|
|
FPGA_MGR_STATE_WRITE,
|
|
FPGA_MGR_STATE_WRITE_ERR,
|
|
FPGA_MGR_STATE_WRITE_COMPLETE,
|
|
FPGA_MGR_STATE_WRITE_COMPLETE_ERR,
|
|
|
|
/* fpga is programmed and operating */
|
|
FPGA_MGR_STATE_OPERATING,
|
|
};
|
|
|
|
/**
|
|
* DOC: FPGA Manager flags
|
|
*
|
|
* Flags used in the &fpga_image_info->flags field
|
|
*
|
|
* %FPGA_MGR_PARTIAL_RECONFIG: do partial reconfiguration if supported
|
|
*
|
|
* %FPGA_MGR_EXTERNAL_CONFIG: FPGA has been configured prior to Linux booting
|
|
*
|
|
* %FPGA_MGR_ENCRYPTED_BITSTREAM: indicates bitstream is encrypted
|
|
*
|
|
* %FPGA_MGR_BITSTREAM_LSB_FIRST: SPI bitstream bit order is LSB first
|
|
*
|
|
* %FPGA_MGR_COMPRESSED_BITSTREAM: FPGA bitstream is compressed
|
|
*/
|
|
#define FPGA_MGR_PARTIAL_RECONFIG BIT(0)
|
|
#define FPGA_MGR_EXTERNAL_CONFIG BIT(1)
|
|
#define FPGA_MGR_ENCRYPTED_BITSTREAM BIT(2)
|
|
#define FPGA_MGR_BITSTREAM_LSB_FIRST BIT(3)
|
|
#define FPGA_MGR_COMPRESSED_BITSTREAM BIT(4)
|
|
|
|
/**
|
|
* struct fpga_image_info - information specific to an FPGA image
|
|
* @flags: boolean flags as defined above
|
|
* @enable_timeout_us: maximum time to enable traffic through bridge (uSec)
|
|
* @disable_timeout_us: maximum time to disable traffic through bridge (uSec)
|
|
* @config_complete_timeout_us: maximum time for FPGA to switch to operating
|
|
* status in the write_complete op.
|
|
* @firmware_name: name of FPGA image firmware file
|
|
* @sgt: scatter/gather table containing FPGA image
|
|
* @buf: contiguous buffer containing FPGA image
|
|
* @count: size of buf
|
|
* @header_size: size of image header.
|
|
* @data_size: size of image data to be sent to the device. If not specified,
|
|
* whole image will be used. Header may be skipped in either case.
|
|
* @region_id: id of target region
|
|
* @dev: device that owns this
|
|
* @overlay: Device Tree overlay
|
|
*/
|
|
struct fpga_image_info {
|
|
u32 flags;
|
|
u32 enable_timeout_us;
|
|
u32 disable_timeout_us;
|
|
u32 config_complete_timeout_us;
|
|
char *firmware_name;
|
|
struct sg_table *sgt;
|
|
const char *buf;
|
|
size_t count;
|
|
size_t header_size;
|
|
size_t data_size;
|
|
int region_id;
|
|
struct device *dev;
|
|
#ifdef CONFIG_OF
|
|
struct device_node *overlay;
|
|
#endif
|
|
};
|
|
|
|
/**
|
|
* struct fpga_compat_id - id for compatibility check
|
|
*
|
|
* @id_h: high 64bit of the compat_id
|
|
* @id_l: low 64bit of the compat_id
|
|
*/
|
|
struct fpga_compat_id {
|
|
u64 id_h;
|
|
u64 id_l;
|
|
};
|
|
|
|
/**
|
|
* struct fpga_manager_info - collection of parameters for an FPGA Manager
|
|
* @name: fpga manager name
|
|
* @compat_id: FPGA manager id for compatibility check.
|
|
* @mops: pointer to structure of fpga manager ops
|
|
* @priv: fpga manager private data
|
|
*
|
|
* fpga_manager_info contains parameters for the register_full function.
|
|
* These are separated into an info structure because they some are optional
|
|
* others could be added to in the future. The info structure facilitates
|
|
* maintaining a stable API.
|
|
*/
|
|
struct fpga_manager_info {
|
|
const char *name;
|
|
struct fpga_compat_id *compat_id;
|
|
const struct fpga_manager_ops *mops;
|
|
void *priv;
|
|
};
|
|
|
|
/**
|
|
* struct fpga_manager_ops - ops for low level fpga manager drivers
|
|
* @initial_header_size: minimum number of bytes that should be passed into
|
|
* parse_header and write_init.
|
|
* @skip_header: bool flag to tell fpga-mgr core whether it should skip
|
|
* info->header_size part at the beginning of the image when invoking
|
|
* write callback.
|
|
* @state: returns an enum value of the FPGA's state
|
|
* @status: returns status of the FPGA, including reconfiguration error code
|
|
* @parse_header: parse FPGA image header to set info->header_size and
|
|
* info->data_size. In case the input buffer is not large enough, set
|
|
* required size to info->header_size and return -EAGAIN.
|
|
* @write_init: prepare the FPGA to receive configuration data
|
|
* @write: write count bytes of configuration data to the FPGA
|
|
* @write_sg: write the scatter list of configuration data to the FPGA
|
|
* @write_complete: set FPGA to operating state after writing is done
|
|
* @fpga_remove: optional: Set FPGA into a specific state during driver remove
|
|
* @groups: optional attribute groups.
|
|
*
|
|
* fpga_manager_ops are the low level functions implemented by a specific
|
|
* fpga manager driver. The optional ones are tested for NULL before being
|
|
* called, so leaving them out is fine.
|
|
*/
|
|
struct fpga_manager_ops {
|
|
size_t initial_header_size;
|
|
bool skip_header;
|
|
enum fpga_mgr_states (*state)(struct fpga_manager *mgr);
|
|
u64 (*status)(struct fpga_manager *mgr);
|
|
int (*parse_header)(struct fpga_manager *mgr,
|
|
struct fpga_image_info *info,
|
|
const char *buf, size_t count);
|
|
int (*write_init)(struct fpga_manager *mgr,
|
|
struct fpga_image_info *info,
|
|
const char *buf, size_t count);
|
|
int (*write)(struct fpga_manager *mgr, const char *buf, size_t count);
|
|
int (*write_sg)(struct fpga_manager *mgr, struct sg_table *sgt);
|
|
int (*write_complete)(struct fpga_manager *mgr,
|
|
struct fpga_image_info *info);
|
|
void (*fpga_remove)(struct fpga_manager *mgr);
|
|
const struct attribute_group **groups;
|
|
};
|
|
|
|
/* FPGA manager status: Partial/Full Reconfiguration errors */
|
|
#define FPGA_MGR_STATUS_OPERATION_ERR BIT(0)
|
|
#define FPGA_MGR_STATUS_CRC_ERR BIT(1)
|
|
#define FPGA_MGR_STATUS_INCOMPATIBLE_IMAGE_ERR BIT(2)
|
|
#define FPGA_MGR_STATUS_IP_PROTOCOL_ERR BIT(3)
|
|
#define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR BIT(4)
|
|
|
|
/**
|
|
* struct fpga_manager - fpga manager structure
|
|
* @name: name of low level fpga manager
|
|
* @dev: fpga manager device
|
|
* @ref_mutex: only allows one reference to fpga manager
|
|
* @state: state of fpga manager
|
|
* @compat_id: FPGA manager id for compatibility check.
|
|
* @mops: pointer to struct of fpga manager ops
|
|
* @mops_owner: module containing the mops
|
|
* @priv: low level driver private date
|
|
*/
|
|
struct fpga_manager {
|
|
const char *name;
|
|
struct device dev;
|
|
struct mutex ref_mutex;
|
|
enum fpga_mgr_states state;
|
|
struct fpga_compat_id *compat_id;
|
|
const struct fpga_manager_ops *mops;
|
|
struct module *mops_owner;
|
|
void *priv;
|
|
};
|
|
|
|
#define to_fpga_manager(d) container_of(d, struct fpga_manager, dev)
|
|
|
|
struct fpga_image_info *fpga_image_info_alloc(struct device *dev);
|
|
|
|
void fpga_image_info_free(struct fpga_image_info *info);
|
|
|
|
int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info);
|
|
|
|
int fpga_mgr_lock(struct fpga_manager *mgr);
|
|
void fpga_mgr_unlock(struct fpga_manager *mgr);
|
|
|
|
struct fpga_manager *of_fpga_mgr_get(struct device_node *node);
|
|
|
|
struct fpga_manager *fpga_mgr_get(struct device *dev);
|
|
|
|
void fpga_mgr_put(struct fpga_manager *mgr);
|
|
|
|
#define fpga_mgr_register_full(parent, info) \
|
|
__fpga_mgr_register_full(parent, info, THIS_MODULE)
|
|
struct fpga_manager *
|
|
__fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
|
|
struct module *owner);
|
|
|
|
#define fpga_mgr_register(parent, name, mops, priv) \
|
|
__fpga_mgr_register(parent, name, mops, priv, THIS_MODULE)
|
|
struct fpga_manager *
|
|
__fpga_mgr_register(struct device *parent, const char *name,
|
|
const struct fpga_manager_ops *mops, void *priv, struct module *owner);
|
|
|
|
void fpga_mgr_unregister(struct fpga_manager *mgr);
|
|
|
|
#define devm_fpga_mgr_register_full(parent, info) \
|
|
__devm_fpga_mgr_register_full(parent, info, THIS_MODULE)
|
|
struct fpga_manager *
|
|
__devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info,
|
|
struct module *owner);
|
|
#define devm_fpga_mgr_register(parent, name, mops, priv) \
|
|
__devm_fpga_mgr_register(parent, name, mops, priv, THIS_MODULE)
|
|
struct fpga_manager *
|
|
__devm_fpga_mgr_register(struct device *parent, const char *name,
|
|
const struct fpga_manager_ops *mops, void *priv,
|
|
struct module *owner);
|
|
|
|
#endif /*_LINUX_FPGA_MGR_H */
|