forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fpga: dfl: introduce interrupt trigger setting API
FPGA user applications may be interested in interrupts generated by DFL features. For example, users can implement their own FPGA logics with interrupts enabled in AFU (Accelerated Function Unit, dynamic region of DFL based FPGA). So user applications need to be notified to handle these interrupts. In order to allow userspace applications to monitor interrupts, driver requires userspace to provide eventfds as interrupt notification channels. Applications then poll/select on the eventfds to get notified. This patch introduces a generic helper functions to do eventfds binding with given interrupts. Sub feature drivers are expected to use XXX_GET_IRQ_NUM to query irq info, and XXX_SET_IRQ to set eventfds for interrupts. This patch also introduces helper functions for these 2 ioctls. Signed-off-by: Luwei Kang <[email protected]> Signed-off-by: Wu Hao <[email protected]> Signed-off-by: Xu Yilun <[email protected]> Signed-off-by: Tom Rix <[email protected]> Reviewed-by: Marcelo Tosatti <[email protected]> Acked-by: Wu Hao <[email protected]> Signed-off-by: Moritz Fischer <[email protected]>
- Loading branch information
1 parent
bfef946
commit 322b598
Showing
3 changed files
with
186 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,7 +10,9 @@ | |
* Wu Hao <[email protected]> | ||
* Xiao Guangrong <[email protected]> | ||
*/ | ||
#include <linux/fpga-dfl.h> | ||
#include <linux/module.h> | ||
#include <linux/uaccess.h> | ||
|
||
#include "dfl.h" | ||
|
||
|
@@ -533,6 +535,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) | |
unsigned int i; | ||
|
||
/* save resource information for each feature */ | ||
feature->dev = fdev; | ||
feature->id = finfo->fid; | ||
feature->resource_index = index; | ||
feature->ioaddr = finfo->ioaddr; | ||
|
@@ -1393,6 +1396,160 @@ int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vfs) | |
} | ||
EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_vf); | ||
|
||
static irqreturn_t dfl_irq_handler(int irq, void *arg) | ||
{ | ||
struct eventfd_ctx *trigger = arg; | ||
|
||
eventfd_signal(trigger, 1); | ||
return IRQ_HANDLED; | ||
} | ||
|
||
static int do_set_irq_trigger(struct dfl_feature *feature, unsigned int idx, | ||
int fd) | ||
{ | ||
struct platform_device *pdev = feature->dev; | ||
struct eventfd_ctx *trigger; | ||
int irq, ret; | ||
|
||
irq = feature->irq_ctx[idx].irq; | ||
|
||
if (feature->irq_ctx[idx].trigger) { | ||
free_irq(irq, feature->irq_ctx[idx].trigger); | ||
kfree(feature->irq_ctx[idx].name); | ||
eventfd_ctx_put(feature->irq_ctx[idx].trigger); | ||
feature->irq_ctx[idx].trigger = NULL; | ||
} | ||
|
||
if (fd < 0) | ||
return 0; | ||
|
||
feature->irq_ctx[idx].name = | ||
kasprintf(GFP_KERNEL, "fpga-irq[%u](%s-%llx)", idx, | ||
dev_name(&pdev->dev), feature->id); | ||
if (!feature->irq_ctx[idx].name) | ||
return -ENOMEM; | ||
|
||
trigger = eventfd_ctx_fdget(fd); | ||
if (IS_ERR(trigger)) { | ||
ret = PTR_ERR(trigger); | ||
goto free_name; | ||
} | ||
|
||
ret = request_irq(irq, dfl_irq_handler, 0, | ||
feature->irq_ctx[idx].name, trigger); | ||
if (!ret) { | ||
feature->irq_ctx[idx].trigger = trigger; | ||
return ret; | ||
} | ||
|
||
eventfd_ctx_put(trigger); | ||
free_name: | ||
kfree(feature->irq_ctx[idx].name); | ||
|
||
return ret; | ||
} | ||
|
||
/** | ||
* dfl_fpga_set_irq_triggers - set eventfd triggers for dfl feature interrupts | ||
* | ||
* @feature: dfl sub feature. | ||
* @start: start of irq index in this dfl sub feature. | ||
* @count: number of irqs. | ||
* @fds: eventfds to bind with irqs. unbind related irq if fds[n] is negative. | ||
* unbind "count" specified number of irqs if fds ptr is NULL. | ||
* | ||
* Bind given eventfds with irqs in this dfl sub feature. Unbind related irq if | ||
* fds[n] is negative. Unbind "count" specified number of irqs if fds ptr is | ||
* NULL. | ||
* | ||
* Return: 0 on success, negative error code otherwise. | ||
*/ | ||
int dfl_fpga_set_irq_triggers(struct dfl_feature *feature, unsigned int start, | ||
unsigned int count, int32_t *fds) | ||
{ | ||
unsigned int i; | ||
int ret = 0; | ||
|
||
/* overflow */ | ||
if (unlikely(start + count < start)) | ||
return -EINVAL; | ||
|
||
/* exceeds nr_irqs */ | ||
if (start + count > feature->nr_irqs) | ||
return -EINVAL; | ||
|
||
for (i = 0; i < count; i++) { | ||
int fd = fds ? fds[i] : -1; | ||
|
||
ret = do_set_irq_trigger(feature, start + i, fd); | ||
if (ret) { | ||
while (i--) | ||
do_set_irq_trigger(feature, start + i, -1); | ||
break; | ||
} | ||
} | ||
|
||
return ret; | ||
} | ||
EXPORT_SYMBOL_GPL(dfl_fpga_set_irq_triggers); | ||
|
||
/** | ||
* dfl_feature_ioctl_get_num_irqs - dfl feature _GET_IRQ_NUM ioctl interface. | ||
* @pdev: the feature device which has the sub feature | ||
* @feature: the dfl sub feature | ||
* @arg: ioctl argument | ||
* | ||
* Return: 0 on success, negative error code otherwise. | ||
*/ | ||
long dfl_feature_ioctl_get_num_irqs(struct platform_device *pdev, | ||
struct dfl_feature *feature, | ||
unsigned long arg) | ||
{ | ||
return put_user(feature->nr_irqs, (__u32 __user *)arg); | ||
} | ||
EXPORT_SYMBOL_GPL(dfl_feature_ioctl_get_num_irqs); | ||
|
||
/** | ||
* dfl_feature_ioctl_set_irq - dfl feature _SET_IRQ ioctl interface. | ||
* @pdev: the feature device which has the sub feature | ||
* @feature: the dfl sub feature | ||
* @arg: ioctl argument | ||
* | ||
* Return: 0 on success, negative error code otherwise. | ||
*/ | ||
long dfl_feature_ioctl_set_irq(struct platform_device *pdev, | ||
struct dfl_feature *feature, | ||
unsigned long arg) | ||
{ | ||
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); | ||
struct dfl_fpga_irq_set hdr; | ||
s32 *fds; | ||
long ret; | ||
|
||
if (!feature->nr_irqs) | ||
return -ENOENT; | ||
|
||
if (copy_from_user(&hdr, (void __user *)arg, sizeof(hdr))) | ||
return -EFAULT; | ||
|
||
if (!hdr.count || (hdr.start + hdr.count > feature->nr_irqs) || | ||
(hdr.start + hdr.count < hdr.start)) | ||
return -EINVAL; | ||
|
||
fds = memdup_user((void __user *)(arg + sizeof(hdr)), | ||
hdr.count * sizeof(s32)); | ||
if (IS_ERR(fds)) | ||
return PTR_ERR(fds); | ||
|
||
mutex_lock(&pdata->lock); | ||
ret = dfl_fpga_set_irq_triggers(feature, hdr.start, hdr.count, fds); | ||
mutex_unlock(&pdata->lock); | ||
|
||
kfree(fds); | ||
return ret; | ||
} | ||
EXPORT_SYMBOL_GPL(dfl_feature_ioctl_set_irq); | ||
|
||
static void __exit dfl_fpga_exit(void) | ||
{ | ||
dfl_chardev_uinit(); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters