1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Driver for FPGA Accelerated Function Unit (AFU) MMIO Region Management
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2017-2018 Intel Corporation, Inc.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Authors:
8*4882a593Smuzhiyun * Wu Hao <hao.wu@intel.com>
9*4882a593Smuzhiyun * Xiao Guangrong <guangrong.xiao@linux.intel.com>
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun #include "dfl-afu.h"
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun /**
14*4882a593Smuzhiyun * afu_mmio_region_init - init function for afu mmio region support
15*4882a593Smuzhiyun * @pdata: afu platform device's pdata.
16*4882a593Smuzhiyun */
afu_mmio_region_init(struct dfl_feature_platform_data * pdata)17*4882a593Smuzhiyun void afu_mmio_region_init(struct dfl_feature_platform_data *pdata)
18*4882a593Smuzhiyun {
19*4882a593Smuzhiyun struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun INIT_LIST_HEAD(&afu->regions);
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #define for_each_region(region, afu) \
25*4882a593Smuzhiyun list_for_each_entry((region), &(afu)->regions, node)
26*4882a593Smuzhiyun
get_region_by_index(struct dfl_afu * afu,u32 region_index)27*4882a593Smuzhiyun static struct dfl_afu_mmio_region *get_region_by_index(struct dfl_afu *afu,
28*4882a593Smuzhiyun u32 region_index)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun struct dfl_afu_mmio_region *region;
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun for_each_region(region, afu)
33*4882a593Smuzhiyun if (region->index == region_index)
34*4882a593Smuzhiyun return region;
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun return NULL;
37*4882a593Smuzhiyun }
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /**
40*4882a593Smuzhiyun * afu_mmio_region_add - add a mmio region to given feature dev.
41*4882a593Smuzhiyun *
42*4882a593Smuzhiyun * @region_index: region index.
43*4882a593Smuzhiyun * @region_size: region size.
44*4882a593Smuzhiyun * @phys: region's physical address of this region.
45*4882a593Smuzhiyun * @flags: region flags (access permission).
46*4882a593Smuzhiyun *
47*4882a593Smuzhiyun * Return: 0 on success, negative error code otherwise.
48*4882a593Smuzhiyun */
afu_mmio_region_add(struct dfl_feature_platform_data * pdata,u32 region_index,u64 region_size,u64 phys,u32 flags)49*4882a593Smuzhiyun int afu_mmio_region_add(struct dfl_feature_platform_data *pdata,
50*4882a593Smuzhiyun u32 region_index, u64 region_size, u64 phys, u32 flags)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun struct dfl_afu_mmio_region *region;
53*4882a593Smuzhiyun struct dfl_afu *afu;
54*4882a593Smuzhiyun int ret = 0;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun region = devm_kzalloc(&pdata->dev->dev, sizeof(*region), GFP_KERNEL);
57*4882a593Smuzhiyun if (!region)
58*4882a593Smuzhiyun return -ENOMEM;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun region->index = region_index;
61*4882a593Smuzhiyun region->size = region_size;
62*4882a593Smuzhiyun region->phys = phys;
63*4882a593Smuzhiyun region->flags = flags;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun mutex_lock(&pdata->lock);
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun afu = dfl_fpga_pdata_get_private(pdata);
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /* check if @index already exists */
70*4882a593Smuzhiyun if (get_region_by_index(afu, region_index)) {
71*4882a593Smuzhiyun mutex_unlock(&pdata->lock);
72*4882a593Smuzhiyun ret = -EEXIST;
73*4882a593Smuzhiyun goto exit;
74*4882a593Smuzhiyun }
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun region_size = PAGE_ALIGN(region_size);
77*4882a593Smuzhiyun region->offset = afu->region_cur_offset;
78*4882a593Smuzhiyun list_add(®ion->node, &afu->regions);
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun afu->region_cur_offset += region_size;
81*4882a593Smuzhiyun afu->num_regions++;
82*4882a593Smuzhiyun mutex_unlock(&pdata->lock);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun return 0;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun exit:
87*4882a593Smuzhiyun devm_kfree(&pdata->dev->dev, region);
88*4882a593Smuzhiyun return ret;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun /**
92*4882a593Smuzhiyun * afu_mmio_region_destroy - destroy all mmio regions under given feature dev.
93*4882a593Smuzhiyun * @pdata: afu platform device's pdata.
94*4882a593Smuzhiyun */
afu_mmio_region_destroy(struct dfl_feature_platform_data * pdata)95*4882a593Smuzhiyun void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata)
96*4882a593Smuzhiyun {
97*4882a593Smuzhiyun struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata);
98*4882a593Smuzhiyun struct dfl_afu_mmio_region *tmp, *region;
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun list_for_each_entry_safe(region, tmp, &afu->regions, node)
101*4882a593Smuzhiyun devm_kfree(&pdata->dev->dev, region);
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun /**
105*4882a593Smuzhiyun * afu_mmio_region_get_by_index - find an afu region by index.
106*4882a593Smuzhiyun * @pdata: afu platform device's pdata.
107*4882a593Smuzhiyun * @region_index: region index.
108*4882a593Smuzhiyun * @pregion: ptr to region for result.
109*4882a593Smuzhiyun *
110*4882a593Smuzhiyun * Return: 0 on success, negative error code otherwise.
111*4882a593Smuzhiyun */
afu_mmio_region_get_by_index(struct dfl_feature_platform_data * pdata,u32 region_index,struct dfl_afu_mmio_region * pregion)112*4882a593Smuzhiyun int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata,
113*4882a593Smuzhiyun u32 region_index,
114*4882a593Smuzhiyun struct dfl_afu_mmio_region *pregion)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun struct dfl_afu_mmio_region *region;
117*4882a593Smuzhiyun struct dfl_afu *afu;
118*4882a593Smuzhiyun int ret = 0;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun mutex_lock(&pdata->lock);
121*4882a593Smuzhiyun afu = dfl_fpga_pdata_get_private(pdata);
122*4882a593Smuzhiyun region = get_region_by_index(afu, region_index);
123*4882a593Smuzhiyun if (!region) {
124*4882a593Smuzhiyun ret = -EINVAL;
125*4882a593Smuzhiyun goto exit;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun *pregion = *region;
128*4882a593Smuzhiyun exit:
129*4882a593Smuzhiyun mutex_unlock(&pdata->lock);
130*4882a593Smuzhiyun return ret;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun /**
134*4882a593Smuzhiyun * afu_mmio_region_get_by_offset - find an afu mmio region by offset and size
135*4882a593Smuzhiyun *
136*4882a593Smuzhiyun * @pdata: afu platform device's pdata.
137*4882a593Smuzhiyun * @offset: region offset from start of the device fd.
138*4882a593Smuzhiyun * @size: region size.
139*4882a593Smuzhiyun * @pregion: ptr to region for result.
140*4882a593Smuzhiyun *
141*4882a593Smuzhiyun * Find the region which fully contains the region described by input
142*4882a593Smuzhiyun * parameters (offset and size) from the feature dev's region linked list.
143*4882a593Smuzhiyun *
144*4882a593Smuzhiyun * Return: 0 on success, negative error code otherwise.
145*4882a593Smuzhiyun */
afu_mmio_region_get_by_offset(struct dfl_feature_platform_data * pdata,u64 offset,u64 size,struct dfl_afu_mmio_region * pregion)146*4882a593Smuzhiyun int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata,
147*4882a593Smuzhiyun u64 offset, u64 size,
148*4882a593Smuzhiyun struct dfl_afu_mmio_region *pregion)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun struct dfl_afu_mmio_region *region;
151*4882a593Smuzhiyun struct dfl_afu *afu;
152*4882a593Smuzhiyun int ret = 0;
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun mutex_lock(&pdata->lock);
155*4882a593Smuzhiyun afu = dfl_fpga_pdata_get_private(pdata);
156*4882a593Smuzhiyun for_each_region(region, afu)
157*4882a593Smuzhiyun if (region->offset <= offset &&
158*4882a593Smuzhiyun region->offset + region->size >= offset + size) {
159*4882a593Smuzhiyun *pregion = *region;
160*4882a593Smuzhiyun goto exit;
161*4882a593Smuzhiyun }
162*4882a593Smuzhiyun ret = -EINVAL;
163*4882a593Smuzhiyun exit:
164*4882a593Smuzhiyun mutex_unlock(&pdata->lock);
165*4882a593Smuzhiyun return ret;
166*4882a593Smuzhiyun }
167