Commit | Line | Data |
---|---|---|
473f01f7 | 1 | // SPDX-License-Identifier: GPL-2.0 |
0fa20cdf AT |
2 | /* |
3 | * FPGA Region - Device Tree support for FPGA programming under Linux | |
4 | * | |
5 | * Copyright (C) 2013-2016 Altera Corporation | |
ef3acdd8 | 6 | * Copyright (C) 2017 Intel Corporation |
0fa20cdf | 7 | */ |
0fa20cdf AT |
8 | #include <linux/fpga/fpga-bridge.h> |
9 | #include <linux/fpga/fpga-mgr.h> | |
59460a93 | 10 | #include <linux/fpga/fpga-region.h> |
0fa20cdf AT |
11 | #include <linux/idr.h> |
12 | #include <linux/kernel.h> | |
13 | #include <linux/list.h> | |
14 | #include <linux/module.h> | |
0fa20cdf AT |
15 | #include <linux/slab.h> |
16 | #include <linux/spinlock.h> | |
17 | ||
0fa20cdf AT |
18 | static DEFINE_IDA(fpga_region_ida); |
19 | static struct class *fpga_region_class; | |
20 | ||
503d4b7a AT |
21 | struct fpga_region *fpga_region_class_find( |
22 | struct device *start, const void *data, | |
23 | int (*match)(struct device *, const void *)) | |
24 | { | |
25 | struct device *dev; | |
26 | ||
27 | dev = class_find_device(fpga_region_class, start, data, match); | |
28 | if (!dev) | |
29 | return NULL; | |
30 | ||
31 | return to_fpga_region(dev); | |
32 | } | |
33 | EXPORT_SYMBOL_GPL(fpga_region_class_find); | |
34 | ||
0fa20cdf AT |
35 | /** |
36 | * fpga_region_get - get an exclusive reference to a fpga region | |
37 | * @region: FPGA Region struct | |
38 | * | |
39 | * Caller should call fpga_region_put() when done with region. | |
40 | * | |
41 | * Return fpga_region struct if successful. | |
42 | * Return -EBUSY if someone already has a reference to the region. | |
43 | * Return -ENODEV if @np is not a FPGA Region. | |
44 | */ | |
45 | static struct fpga_region *fpga_region_get(struct fpga_region *region) | |
46 | { | |
47 | struct device *dev = ®ion->dev; | |
48 | ||
49 | if (!mutex_trylock(®ion->mutex)) { | |
50 | dev_dbg(dev, "%s: FPGA Region already in use\n", __func__); | |
51 | return ERR_PTR(-EBUSY); | |
52 | } | |
53 | ||
54 | get_device(dev); | |
0fa20cdf | 55 | if (!try_module_get(dev->parent->driver->owner)) { |
0fa20cdf AT |
56 | put_device(dev); |
57 | mutex_unlock(®ion->mutex); | |
58 | return ERR_PTR(-ENODEV); | |
59 | } | |
60 | ||
c3d971ad | 61 | dev_dbg(dev, "get\n"); |
0fa20cdf AT |
62 | |
63 | return region; | |
64 | } | |
65 | ||
66 | /** | |
67 | * fpga_region_put - release a reference to a region | |
68 | * | |
69 | * @region: FPGA region | |
70 | */ | |
71 | static void fpga_region_put(struct fpga_region *region) | |
72 | { | |
73 | struct device *dev = ®ion->dev; | |
74 | ||
c3d971ad | 75 | dev_dbg(dev, "put\n"); |
0fa20cdf AT |
76 | |
77 | module_put(dev->parent->driver->owner); | |
0fa20cdf AT |
78 | put_device(dev); |
79 | mutex_unlock(®ion->mutex); | |
80 | } | |
81 | ||
0fa20cdf AT |
82 | /** |
83 | * fpga_region_program_fpga - program FPGA | |
917a4304 | 84 | * |
0fa20cdf | 85 | * @region: FPGA region |
917a4304 | 86 | * |
61c32102 | 87 | * Program an FPGA using fpga image info (region->info). |
093a89d4 AT |
88 | * If the region has a get_bridges function, the exclusive reference for the |
89 | * bridges will be held if programming succeeds. This is intended to prevent | |
90 | * reprogramming the region until the caller considers it safe to do so. | |
91 | * The caller will need to call fpga_bridges_put() before attempting to | |
92 | * reprogram the region. | |
917a4304 | 93 | * |
0fa20cdf AT |
94 | * Return 0 for success or negative error code. |
95 | */ | |
59460a93 | 96 | int fpga_region_program_fpga(struct fpga_region *region) |
0fa20cdf | 97 | { |
ebf877a5 | 98 | struct device *dev = ®ion->dev; |
61c32102 | 99 | struct fpga_image_info *info = region->info; |
0fa20cdf AT |
100 | int ret; |
101 | ||
102 | region = fpga_region_get(region); | |
103 | if (IS_ERR(region)) { | |
c3d971ad | 104 | dev_err(dev, "failed to get FPGA region\n"); |
0fa20cdf AT |
105 | return PTR_ERR(region); |
106 | } | |
107 | ||
1df2dd7f | 108 | ret = fpga_mgr_lock(region->mgr); |
ebf877a5 AT |
109 | if (ret) { |
110 | dev_err(dev, "FPGA manager is busy\n"); | |
1df2dd7f | 111 | goto err_put_region; |
ebf877a5 AT |
112 | } |
113 | ||
52a3a7cc AT |
114 | /* |
115 | * In some cases, we already have a list of bridges in the | |
116 | * fpga region struct. Or we don't have any bridges. | |
117 | */ | |
118 | if (region->get_bridges) { | |
119 | ret = region->get_bridges(region); | |
120 | if (ret) { | |
121 | dev_err(dev, "failed to get fpga region bridges\n"); | |
122 | goto err_unlock_mgr; | |
123 | } | |
0fa20cdf AT |
124 | } |
125 | ||
126 | ret = fpga_bridges_disable(®ion->bridge_list); | |
127 | if (ret) { | |
c3d971ad | 128 | dev_err(dev, "failed to disable bridges\n"); |
0fa20cdf AT |
129 | goto err_put_br; |
130 | } | |
131 | ||
61c32102 | 132 | ret = fpga_mgr_load(region->mgr, info); |
0fa20cdf | 133 | if (ret) { |
c3d971ad | 134 | dev_err(dev, "failed to load FPGA image\n"); |
0fa20cdf AT |
135 | goto err_put_br; |
136 | } | |
137 | ||
138 | ret = fpga_bridges_enable(®ion->bridge_list); | |
139 | if (ret) { | |
c3d971ad | 140 | dev_err(dev, "failed to enable region bridges\n"); |
0fa20cdf AT |
141 | goto err_put_br; |
142 | } | |
143 | ||
1df2dd7f | 144 | fpga_mgr_unlock(region->mgr); |
0fa20cdf AT |
145 | fpga_region_put(region); |
146 | ||
147 | return 0; | |
148 | ||
149 | err_put_br: | |
52a3a7cc AT |
150 | if (region->get_bridges) |
151 | fpga_bridges_put(®ion->bridge_list); | |
ebf877a5 | 152 | err_unlock_mgr: |
1df2dd7f | 153 | fpga_mgr_unlock(region->mgr); |
e73bbf64 | 154 | err_put_region: |
0fa20cdf AT |
155 | fpga_region_put(region); |
156 | ||
157 | return ret; | |
158 | } | |
59460a93 | 159 | EXPORT_SYMBOL_GPL(fpga_region_program_fpga); |
0fa20cdf | 160 | |
41a8b2c5 WH |
161 | static ssize_t compat_id_show(struct device *dev, |
162 | struct device_attribute *attr, char *buf) | |
163 | { | |
164 | struct fpga_region *region = to_fpga_region(dev); | |
165 | ||
166 | if (!region->compat_id) | |
167 | return -ENOENT; | |
168 | ||
169 | return sprintf(buf, "%016llx%016llx\n", | |
170 | (unsigned long long)region->compat_id->id_h, | |
171 | (unsigned long long)region->compat_id->id_l); | |
172 | } | |
173 | ||
174 | static DEVICE_ATTR_RO(compat_id); | |
175 | ||
176 | static struct attribute *fpga_region_attrs[] = { | |
177 | &dev_attr_compat_id.attr, | |
178 | NULL, | |
179 | }; | |
180 | ATTRIBUTE_GROUPS(fpga_region); | |
181 | ||
9f368977 AT |
182 | /** |
183 | * fpga_region_create - alloc and init a struct fpga_region | |
184 | * @dev: device parent | |
185 | * @mgr: manager that programs this region | |
186 | * @get_bridges: optional function to get bridges to a list | |
187 | * | |
fea82b7f AT |
188 | * The caller of this function is responsible for freeing the resulting region |
189 | * struct with fpga_region_free(). Using devm_fpga_region_create() instead is | |
190 | * recommended. | |
191 | * | |
9f368977 AT |
192 | * Return: struct fpga_region or NULL |
193 | */ | |
194 | struct fpga_region | |
195 | *fpga_region_create(struct device *dev, | |
196 | struct fpga_manager *mgr, | |
197 | int (*get_bridges)(struct fpga_region *)) | |
0fa20cdf | 198 | { |
9f368977 | 199 | struct fpga_region *region; |
0fa20cdf AT |
200 | int id, ret = 0; |
201 | ||
9f368977 AT |
202 | region = kzalloc(sizeof(*region), GFP_KERNEL); |
203 | if (!region) | |
204 | return NULL; | |
205 | ||
0fa20cdf | 206 | id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL); |
52a3a7cc | 207 | if (id < 0) |
9f368977 | 208 | goto err_free; |
0fa20cdf | 209 | |
9f368977 AT |
210 | region->mgr = mgr; |
211 | region->get_bridges = get_bridges; | |
0fa20cdf AT |
212 | mutex_init(®ion->mutex); |
213 | INIT_LIST_HEAD(®ion->bridge_list); | |
9f368977 | 214 | |
0fa20cdf AT |
215 | device_initialize(®ion->dev); |
216 | region->dev.class = fpga_region_class; | |
217 | region->dev.parent = dev; | |
52a3a7cc | 218 | region->dev.of_node = dev->of_node; |
0fa20cdf | 219 | region->dev.id = id; |
0fa20cdf AT |
220 | |
221 | ret = dev_set_name(®ion->dev, "region%d", id); | |
222 | if (ret) | |
223 | goto err_remove; | |
224 | ||
9f368977 | 225 | return region; |
52a3a7cc AT |
226 | |
227 | err_remove: | |
228 | ida_simple_remove(&fpga_region_ida, id); | |
9f368977 AT |
229 | err_free: |
230 | kfree(region); | |
231 | ||
232 | return NULL; | |
233 | } | |
234 | EXPORT_SYMBOL_GPL(fpga_region_create); | |
235 | ||
236 | /** | |
fea82b7f AT |
237 | * fpga_region_free - free a FPGA region created by fpga_region_create() |
238 | * @region: FPGA region | |
9f368977 AT |
239 | */ |
240 | void fpga_region_free(struct fpga_region *region) | |
241 | { | |
242 | ida_simple_remove(&fpga_region_ida, region->dev.id); | |
243 | kfree(region); | |
244 | } | |
245 | EXPORT_SYMBOL_GPL(fpga_region_free); | |
246 | ||
fea82b7f AT |
247 | static void devm_fpga_region_release(struct device *dev, void *res) |
248 | { | |
249 | struct fpga_region *region = *(struct fpga_region **)res; | |
250 | ||
251 | fpga_region_free(region); | |
252 | } | |
253 | ||
254 | /** | |
255 | * devm_fpga_region_create - create and initialize a managed FPGA region struct | |
256 | * @dev: device parent | |
257 | * @mgr: manager that programs this region | |
258 | * @get_bridges: optional function to get bridges to a list | |
259 | * | |
260 | * This function is intended for use in a FPGA region driver's probe function. | |
261 | * After the region driver creates the region struct with | |
262 | * devm_fpga_region_create(), it should register it with fpga_region_register(). | |
263 | * The region driver's remove function should call fpga_region_unregister(). | |
264 | * The region struct allocated with this function will be freed automatically on | |
265 | * driver detach. This includes the case of a probe function returning error | |
266 | * before calling fpga_region_register(), the struct will still get cleaned up. | |
267 | * | |
268 | * Return: struct fpga_region or NULL | |
269 | */ | |
270 | struct fpga_region | |
271 | *devm_fpga_region_create(struct device *dev, | |
272 | struct fpga_manager *mgr, | |
273 | int (*get_bridges)(struct fpga_region *)) | |
274 | { | |
275 | struct fpga_region **ptr, *region; | |
276 | ||
277 | ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL); | |
278 | if (!ptr) | |
279 | return NULL; | |
280 | ||
281 | region = fpga_region_create(dev, mgr, get_bridges); | |
282 | if (!region) { | |
283 | devres_free(ptr); | |
284 | } else { | |
285 | *ptr = region; | |
286 | devres_add(dev, ptr); | |
287 | } | |
288 | ||
289 | return region; | |
290 | } | |
291 | EXPORT_SYMBOL_GPL(devm_fpga_region_create); | |
292 | ||
917a4304 | 293 | /** |
9f368977 | 294 | * fpga_region_register - register a FPGA region |
fea82b7f AT |
295 | * @region: FPGA region |
296 | * | |
9f368977 AT |
297 | * Return: 0 or -errno |
298 | */ | |
299 | int fpga_region_register(struct fpga_region *region) | |
300 | { | |
301 | return device_add(®ion->dev); | |
52a3a7cc AT |
302 | } |
303 | EXPORT_SYMBOL_GPL(fpga_region_register); | |
304 | ||
917a4304 | 305 | /** |
fea82b7f | 306 | * fpga_region_unregister - unregister a FPGA region |
9f368977 | 307 | * @region: FPGA region |
fea82b7f AT |
308 | * |
309 | * This function is intended for use in a FPGA region driver's remove function. | |
9f368977 AT |
310 | */ |
311 | void fpga_region_unregister(struct fpga_region *region) | |
52a3a7cc AT |
312 | { |
313 | device_unregister(®ion->dev); | |
52a3a7cc AT |
314 | } |
315 | EXPORT_SYMBOL_GPL(fpga_region_unregister); | |
316 | ||
0fa20cdf AT |
317 | static void fpga_region_dev_release(struct device *dev) |
318 | { | |
0fa20cdf AT |
319 | } |
320 | ||
321 | /** | |
322 | * fpga_region_init - init function for fpga_region class | |
323 | * Creates the fpga_region class and registers a reconfig notifier. | |
324 | */ | |
325 | static int __init fpga_region_init(void) | |
326 | { | |
0fa20cdf AT |
327 | fpga_region_class = class_create(THIS_MODULE, "fpga_region"); |
328 | if (IS_ERR(fpga_region_class)) | |
329 | return PTR_ERR(fpga_region_class); | |
330 | ||
41a8b2c5 | 331 | fpga_region_class->dev_groups = fpga_region_groups; |
0fa20cdf AT |
332 | fpga_region_class->dev_release = fpga_region_dev_release; |
333 | ||
0fa20cdf | 334 | return 0; |
0fa20cdf AT |
335 | } |
336 | ||
337 | static void __exit fpga_region_exit(void) | |
338 | { | |
0fa20cdf AT |
339 | class_destroy(fpga_region_class); |
340 | ida_destroy(&fpga_region_ida); | |
341 | } | |
342 | ||
343 | subsys_initcall(fpga_region_init); | |
344 | module_exit(fpga_region_exit); | |
345 | ||
346 | MODULE_DESCRIPTION("FPGA Region"); | |
59460a93 | 347 | MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); |
0fa20cdf | 348 | MODULE_LICENSE("GPL v2"); |