diff options
-rw-r--r-- | drivers/fpga/dfl.c | 79 | ||||
-rw-r--r-- | drivers/fpga/dfl.h | 21 |
2 files changed, 100 insertions, 0 deletions
diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index e2c72c5dd9e6..421668ab613e 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -136,6 +136,85 @@ static enum dfl_id_type dfh_id_to_type(u32 id) return DFL_ID_MAX; } +/* + * introduce a global port_ops list, it allows port drivers to register ops + * in such list, then other feature devices (e.g. FME), could use the port + * functions even related port platform device is hidden. Below is one example, + * in virtualization case of PCIe-based FPGA DFL device, when SRIOV is + * enabled, port (and it's AFU) is turned into VF and port platform device + * is hidden from system but it's still required to access port to finish FPGA + * reconfiguration function in FME. + */ + +static DEFINE_MUTEX(dfl_port_ops_mutex); +static LIST_HEAD(dfl_port_ops_list); + +/** + * dfl_fpga_port_ops_get - get matched port ops from the global list + * @pdev: platform device to match with associated port ops. + * Return: matched port ops on success, NULL otherwise. + * + * Please note that must dfl_fpga_port_ops_put after use the port_ops. + */ +struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct platform_device *pdev) +{ + struct dfl_fpga_port_ops *ops = NULL; + + mutex_lock(&dfl_port_ops_mutex); + if (list_empty(&dfl_port_ops_list)) + goto done; + + list_for_each_entry(ops, &dfl_port_ops_list, node) { + /* match port_ops using the name of platform device */ + if (!strcmp(pdev->name, ops->name)) { + if (!try_module_get(ops->owner)) + ops = NULL; + goto done; + } + } + + ops = NULL; +done: + mutex_unlock(&dfl_port_ops_mutex); + return ops; +} +EXPORT_SYMBOL_GPL(dfl_fpga_port_ops_get); + +/** + * dfl_fpga_port_ops_put - put port ops + * @ops: port ops. + */ +void dfl_fpga_port_ops_put(struct dfl_fpga_port_ops *ops) +{ + if (ops && ops->owner) + module_put(ops->owner); +} +EXPORT_SYMBOL_GPL(dfl_fpga_port_ops_put); + +/** + * dfl_fpga_port_ops_add - add port_ops to global list + * @ops: port ops to add. + */ +void dfl_fpga_port_ops_add(struct dfl_fpga_port_ops *ops) +{ + mutex_lock(&dfl_port_ops_mutex); + list_add_tail(&ops->node, &dfl_port_ops_list); + mutex_unlock(&dfl_port_ops_mutex); +} +EXPORT_SYMBOL_GPL(dfl_fpga_port_ops_add); + +/** + * dfl_fpga_port_ops_del - remove port_ops from global list + * @ops: port ops to del. + */ +void dfl_fpga_port_ops_del(struct dfl_fpga_port_ops *ops) +{ + mutex_lock(&dfl_port_ops_mutex); + list_del(&ops->node); + mutex_unlock(&dfl_port_ops_mutex); +} +EXPORT_SYMBOL_GPL(dfl_fpga_port_ops_del); + /** * dfl_fpga_dev_feature_uinit - uinit for sub features of dfl feature device * @pdev: feature device. diff --git a/drivers/fpga/dfl.h b/drivers/fpga/dfl.h index 14d4731c13b0..654e0f694a5f 100644 --- a/drivers/fpga/dfl.h +++ b/drivers/fpga/dfl.h @@ -130,6 +130,27 @@ /* Latency tolerance reporting. '1' >= 40us, '0' < 40us.*/ #define PORT_CTRL_LATENCY BIT_ULL(2) #define PORT_CTRL_SFTRST_ACK BIT_ULL(4) /* HW ack for reset */ +/** + * struct dfl_fpga_port_ops - port ops + * + * @name: name of this port ops, to match with port platform device. + * @owner: pointer to the module which owns this port ops. + * @node: node to link port ops to global list. + * @get_id: get port id from hardware. + * @enable_set: enable/disable the port. + */ +struct dfl_fpga_port_ops { + const char *name; + struct module *owner; + struct list_head node; + int (*get_id)(struct platform_device *pdev); + int (*enable_set)(struct platform_device *pdev, bool enable); +}; + +void dfl_fpga_port_ops_add(struct dfl_fpga_port_ops *ops); +void dfl_fpga_port_ops_del(struct dfl_fpga_port_ops *ops); +struct dfl_fpga_port_ops *dfl_fpga_port_ops_get(struct platform_device *pdev); +void dfl_fpga_port_ops_put(struct dfl_fpga_port_ops *ops); /** * struct dfl_feature_driver - sub feature's driver |