Skip to content

Commit 857a262

Browse files
Xiao Guangronggregkh
authored andcommitted
fpga: dfl: afu: add afu sub feature support
User Accelerated Function Unit sub feature exposes the MMIO region of the AFU. After valid PR bitstream is programmed and the port is enabled, then this MMIO region could be accessed. This patch adds support to enumerate the AFU MMIO region and expose it to userspace via mmap file operation. Below interfaces are exposed to user: Sysfs interface: * /sys/class/fpga_region/<regionX>/<dfl-port.x>/afu_id Read-only. Indicate which PR bitstream is programmed to this AFU. Ioctl interfaces: * DFL_FPGA_PORT_GET_INFO Provide info to userspace on the number of supported region. Only UAFU region is supported now. * DFL_FPGA_PORT_GET_REGION_INFO Provide region information, including access permission, region size, offset from the start of device fd. Signed-off-by: Tim Whisonant <[email protected]> Signed-off-by: Enno Luebbers <[email protected]> Signed-off-by: Shiva Rao <[email protected]> Signed-off-by: Christopher Rauer <[email protected]> Signed-off-by: Xiao Guangrong <[email protected]> Signed-off-by: Wu Hao <[email protected]> Acked-by: Alan Tull <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 6fd893c commit 857a262

File tree

6 files changed

+508
-7
lines changed

6 files changed

+508
-7
lines changed

Documentation/ABI/testing/sysfs-platform-dfl-port

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,12 @@ Contact: Wu Hao <[email protected]>
55
Description: Read-only. It returns id of this port. One DFL FPGA device
66
may have more than one port. Userspace could use this id to
77
distinguish different ports under same FPGA device.
8+
9+
What: /sys/bus/platform/devices/dfl-port.0/afu_id
10+
Date: June 2018
11+
KernelVersion: 4.19
12+
Contact: Wu Hao <[email protected]>
13+
Description: Read-only. User can program different PR bitstreams to FPGA
14+
Accelerator Function Unit (AFU) for different functions. It
15+
returns uuid which could be used to identify which PR bitstream
16+
is programmed in this AFU.

drivers/fpga/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ obj-$(CONFIG_FPGA_DFL_FME_REGION) += dfl-fme-region.o
3838
obj-$(CONFIG_FPGA_DFL_AFU) += dfl-afu.o
3939

4040
dfl-fme-objs := dfl-fme-main.o dfl-fme-pr.o
41-
dfl-afu-objs := dfl-afu-main.o
41+
dfl-afu-objs := dfl-afu-main.o dfl-afu-region.o
4242

4343
# Drivers for FPGAs which implement DFL
4444
obj-$(CONFIG_FPGA_DFL_PCI) += dfl-pci.o

drivers/fpga/dfl-afu-main.c

Lines changed: 213 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@
1616

1717
#include <linux/kernel.h>
1818
#include <linux/module.h>
19+
#include <linux/uaccess.h>
1920
#include <linux/fpga-dfl.h>
2021

21-
#include "dfl.h"
22+
#include "dfl-afu.h"
2223

2324
/**
2425
* port_enable - enable a port
2526
* @pdev: port platform device.
2627
*
2728
* Enable Port by clear the port soft reset bit, which is set by default.
28-
* The User AFU is unable to respond to any MMIO access while in reset.
29-
* port_enable function should only be used after port_disable
30-
* function.
29+
* The AFU is unable to respond to any MMIO access while in reset.
30+
* port_enable function should only be used after port_disable function.
3131
*/
3232
static void port_enable(struct platform_device *pdev)
3333
{
@@ -191,11 +191,74 @@ static const struct dfl_feature_ops port_hdr_ops = {
191191
.ioctl = port_hdr_ioctl,
192192
};
193193

194+
static ssize_t
195+
afu_id_show(struct device *dev, struct device_attribute *attr, char *buf)
196+
{
197+
struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
198+
void __iomem *base;
199+
u64 guidl, guidh;
200+
201+
base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_AFU);
202+
203+
mutex_lock(&pdata->lock);
204+
if (pdata->disable_count) {
205+
mutex_unlock(&pdata->lock);
206+
return -EBUSY;
207+
}
208+
209+
guidl = readq(base + GUID_L);
210+
guidh = readq(base + GUID_H);
211+
mutex_unlock(&pdata->lock);
212+
213+
return scnprintf(buf, PAGE_SIZE, "%016llx%016llx\n", guidh, guidl);
214+
}
215+
static DEVICE_ATTR_RO(afu_id);
216+
217+
static const struct attribute *port_afu_attrs[] = {
218+
&dev_attr_afu_id.attr,
219+
NULL
220+
};
221+
222+
static int port_afu_init(struct platform_device *pdev,
223+
struct dfl_feature *feature)
224+
{
225+
struct resource *res = &pdev->resource[feature->resource_index];
226+
int ret;
227+
228+
dev_dbg(&pdev->dev, "PORT AFU Init.\n");
229+
230+
ret = afu_mmio_region_add(dev_get_platdata(&pdev->dev),
231+
DFL_PORT_REGION_INDEX_AFU, resource_size(res),
232+
res->start, DFL_PORT_REGION_READ |
233+
DFL_PORT_REGION_WRITE | DFL_PORT_REGION_MMAP);
234+
if (ret)
235+
return ret;
236+
237+
return sysfs_create_files(&pdev->dev.kobj, port_afu_attrs);
238+
}
239+
240+
static void port_afu_uinit(struct platform_device *pdev,
241+
struct dfl_feature *feature)
242+
{
243+
dev_dbg(&pdev->dev, "PORT AFU UInit.\n");
244+
245+
sysfs_remove_files(&pdev->dev.kobj, port_afu_attrs);
246+
}
247+
248+
static const struct dfl_feature_ops port_afu_ops = {
249+
.init = port_afu_init,
250+
.uinit = port_afu_uinit,
251+
};
252+
194253
static struct dfl_feature_driver port_feature_drvs[] = {
195254
{
196255
.id = PORT_FEATURE_ID_HEADER,
197256
.ops = &port_hdr_ops,
198257
},
258+
{
259+
.id = PORT_FEATURE_ID_AFU,
260+
.ops = &port_afu_ops,
261+
},
199262
{
200263
.ops = NULL,
201264
}
@@ -243,6 +306,64 @@ static long afu_ioctl_check_extension(struct dfl_feature_platform_data *pdata,
243306
return 0;
244307
}
245308

309+
static long
310+
afu_ioctl_get_info(struct dfl_feature_platform_data *pdata, void __user *arg)
311+
{
312+
struct dfl_fpga_port_info info;
313+
struct dfl_afu *afu;
314+
unsigned long minsz;
315+
316+
minsz = offsetofend(struct dfl_fpga_port_info, num_umsgs);
317+
318+
if (copy_from_user(&info, arg, minsz))
319+
return -EFAULT;
320+
321+
if (info.argsz < minsz)
322+
return -EINVAL;
323+
324+
mutex_lock(&pdata->lock);
325+
afu = dfl_fpga_pdata_get_private(pdata);
326+
info.flags = 0;
327+
info.num_regions = afu->num_regions;
328+
info.num_umsgs = afu->num_umsgs;
329+
mutex_unlock(&pdata->lock);
330+
331+
if (copy_to_user(arg, &info, sizeof(info)))
332+
return -EFAULT;
333+
334+
return 0;
335+
}
336+
337+
static long afu_ioctl_get_region_info(struct dfl_feature_platform_data *pdata,
338+
void __user *arg)
339+
{
340+
struct dfl_fpga_port_region_info rinfo;
341+
struct dfl_afu_mmio_region region;
342+
unsigned long minsz;
343+
long ret;
344+
345+
minsz = offsetofend(struct dfl_fpga_port_region_info, offset);
346+
347+
if (copy_from_user(&rinfo, arg, minsz))
348+
return -EFAULT;
349+
350+
if (rinfo.argsz < minsz || rinfo.padding)
351+
return -EINVAL;
352+
353+
ret = afu_mmio_region_get_by_index(pdata, rinfo.index, &region);
354+
if (ret)
355+
return ret;
356+
357+
rinfo.flags = region.flags;
358+
rinfo.size = region.size;
359+
rinfo.offset = region.offset;
360+
361+
if (copy_to_user(arg, &rinfo, sizeof(rinfo)))
362+
return -EFAULT;
363+
364+
return 0;
365+
}
366+
246367
static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
247368
{
248369
struct platform_device *pdev = filp->private_data;
@@ -259,6 +380,10 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
259380
return DFL_FPGA_API_VERSION;
260381
case DFL_FPGA_CHECK_EXTENSION:
261382
return afu_ioctl_check_extension(pdata, arg);
383+
case DFL_FPGA_PORT_GET_INFO:
384+
return afu_ioctl_get_info(pdata, (void __user *)arg);
385+
case DFL_FPGA_PORT_GET_REGION_INFO:
386+
return afu_ioctl_get_region_info(pdata, (void __user *)arg);
262387
default:
263388
/*
264389
* Let sub-feature's ioctl function to handle the cmd
@@ -277,13 +402,83 @@ static long afu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
277402
return -EINVAL;
278403
}
279404

405+
static int afu_mmap(struct file *filp, struct vm_area_struct *vma)
406+
{
407+
struct platform_device *pdev = filp->private_data;
408+
struct dfl_feature_platform_data *pdata;
409+
u64 size = vma->vm_end - vma->vm_start;
410+
struct dfl_afu_mmio_region region;
411+
u64 offset;
412+
int ret;
413+
414+
if (!(vma->vm_flags & VM_SHARED))
415+
return -EINVAL;
416+
417+
pdata = dev_get_platdata(&pdev->dev);
418+
419+
offset = vma->vm_pgoff << PAGE_SHIFT;
420+
ret = afu_mmio_region_get_by_offset(pdata, offset, size, &region);
421+
if (ret)
422+
return ret;
423+
424+
if (!(region.flags & DFL_PORT_REGION_MMAP))
425+
return -EINVAL;
426+
427+
if ((vma->vm_flags & VM_READ) && !(region.flags & DFL_PORT_REGION_READ))
428+
return -EPERM;
429+
430+
if ((vma->vm_flags & VM_WRITE) &&
431+
!(region.flags & DFL_PORT_REGION_WRITE))
432+
return -EPERM;
433+
434+
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
435+
436+
return remap_pfn_range(vma, vma->vm_start,
437+
(region.phys + (offset - region.offset)) >> PAGE_SHIFT,
438+
size, vma->vm_page_prot);
439+
}
440+
280441
static const struct file_operations afu_fops = {
281442
.owner = THIS_MODULE,
282443
.open = afu_open,
283444
.release = afu_release,
284445
.unlocked_ioctl = afu_ioctl,
446+
.mmap = afu_mmap,
285447
};
286448

449+
static int afu_dev_init(struct platform_device *pdev)
450+
{
451+
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
452+
struct dfl_afu *afu;
453+
454+
afu = devm_kzalloc(&pdev->dev, sizeof(*afu), GFP_KERNEL);
455+
if (!afu)
456+
return -ENOMEM;
457+
458+
afu->pdata = pdata;
459+
460+
mutex_lock(&pdata->lock);
461+
dfl_fpga_pdata_set_private(pdata, afu);
462+
afu_mmio_region_init(pdata);
463+
mutex_unlock(&pdata->lock);
464+
465+
return 0;
466+
}
467+
468+
static int afu_dev_destroy(struct platform_device *pdev)
469+
{
470+
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
471+
struct dfl_afu *afu;
472+
473+
mutex_lock(&pdata->lock);
474+
afu = dfl_fpga_pdata_get_private(pdata);
475+
afu_mmio_region_destroy(pdata);
476+
dfl_fpga_pdata_set_private(pdata, NULL);
477+
mutex_unlock(&pdata->lock);
478+
479+
return 0;
480+
}
481+
287482
static int port_enable_set(struct platform_device *pdev, bool enable)
288483
{
289484
struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
@@ -312,14 +507,25 @@ static int afu_probe(struct platform_device *pdev)
312507

313508
dev_dbg(&pdev->dev, "%s\n", __func__);
314509

510+
ret = afu_dev_init(pdev);
511+
if (ret)
512+
goto exit;
513+
315514
ret = dfl_fpga_dev_feature_init(pdev, port_feature_drvs);
316515
if (ret)
317-
return ret;
516+
goto dev_destroy;
318517

319518
ret = dfl_fpga_dev_ops_register(pdev, &afu_fops, THIS_MODULE);
320-
if (ret)
519+
if (ret) {
321520
dfl_fpga_dev_feature_uinit(pdev);
521+
goto dev_destroy;
522+
}
523+
524+
return 0;
322525

526+
dev_destroy:
527+
afu_dev_destroy(pdev);
528+
exit:
323529
return ret;
324530
}
325531

@@ -329,6 +535,7 @@ static int afu_remove(struct platform_device *pdev)
329535

330536
dfl_fpga_dev_ops_unregister(pdev);
331537
dfl_fpga_dev_feature_uinit(pdev);
538+
afu_dev_destroy(pdev);
332539

333540
return 0;
334541
}

0 commit comments

Comments
 (0)