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 * @pdata: afu platform device's pdata. 43 * @region_index: region index. 44 * @region_size: region size. 45 * @phys: region's physical address of this region. 46 * @flags: region flags (access permission). 47 * 48 * Return: 0 on success, negative error code otherwise. 49 */ 50 int afu_mmio_region_add(struct dfl_feature_platform_data *pdata, 51 u32 region_index, u64 region_size, u64 phys, u32 flags) 52 { 53 struct dfl_afu_mmio_region *region; 54 struct dfl_afu *afu; 55 int ret = 0; 56 57 region = devm_kzalloc(&pdata->dev->dev, sizeof(*region), GFP_KERNEL); 58 if (!region) 59 return -ENOMEM; 60 61 region->index = region_index; 62 region->size = region_size; 63 region->phys = phys; 64 region->flags = flags; 65 66 mutex_lock(&pdata->lock); 67 68 afu = dfl_fpga_pdata_get_private(pdata); 69 70 /* check if @index already exists */ 71 if (get_region_by_index(afu, region_index)) { 72 mutex_unlock(&pdata->lock); 73 ret = -EEXIST; 74 goto exit; 75 } 76 77 region_size = PAGE_ALIGN(region_size); 78 region->offset = afu->region_cur_offset; 79 list_add(®ion->node, &afu->regions); 80 81 afu->region_cur_offset += region_size; 82 afu->num_regions++; 83 mutex_unlock(&pdata->lock); 84 85 return 0; 86 87 exit: 88 devm_kfree(&pdata->dev->dev, region); 89 return ret; 90 } 91 92 /** 93 * afu_mmio_region_destroy - destroy all mmio regions under given feature dev. 94 * @pdata: afu platform device's pdata. 95 */ 96 void afu_mmio_region_destroy(struct dfl_feature_platform_data *pdata) 97 { 98 struct dfl_afu *afu = dfl_fpga_pdata_get_private(pdata); 99 struct dfl_afu_mmio_region *tmp, *region; 100 101 list_for_each_entry_safe(region, tmp, &afu->regions, node) 102 devm_kfree(&pdata->dev->dev, region); 103 } 104 105 /** 106 * afu_mmio_region_get_by_index - find an afu region by index. 107 * @pdata: afu platform device's pdata. 108 * @region_index: region index. 109 * @pregion: ptr to region for result. 110 * 111 * Return: 0 on success, negative error code otherwise. 112 */ 113 int afu_mmio_region_get_by_index(struct dfl_feature_platform_data *pdata, 114 u32 region_index, 115 struct dfl_afu_mmio_region *pregion) 116 { 117 struct dfl_afu_mmio_region *region; 118 struct dfl_afu *afu; 119 int ret = 0; 120 121 mutex_lock(&pdata->lock); 122 afu = dfl_fpga_pdata_get_private(pdata); 123 region = get_region_by_index(afu, region_index); 124 if (!region) { 125 ret = -EINVAL; 126 goto exit; 127 } 128 *pregion = *region; 129 exit: 130 mutex_unlock(&pdata->lock); 131 return ret; 132 } 133 134 /** 135 * afu_mmio_region_get_by_offset - find an afu mmio region by offset and size 136 * 137 * @pdata: afu platform device's pdata. 138 * @offset: region offset from start of the device fd. 139 * @size: region size. 140 * @pregion: ptr to region for result. 141 * 142 * Find the region which fully contains the region described by input 143 * parameters (offset and size) from the feature dev's region linked list. 144 * 145 * Return: 0 on success, negative error code otherwise. 146 */ 147 int afu_mmio_region_get_by_offset(struct dfl_feature_platform_data *pdata, 148 u64 offset, u64 size, 149 struct dfl_afu_mmio_region *pregion) 150 { 151 struct dfl_afu_mmio_region *region; 152 struct dfl_afu *afu; 153 int ret = 0; 154 155 mutex_lock(&pdata->lock); 156 afu = dfl_fpga_pdata_get_private(pdata); 157 for_each_region(region, afu) 158 if (region->offset <= offset && 159 region->offset + region->size >= offset + size) { 160 *pregion = *region; 161 goto exit; 162 } 163 ret = -EINVAL; 164 exit: 165 mutex_unlock(&pdata->lock); 166 return ret; 167 } 168