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