]>
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 | * | |
188 | * Return: struct fpga_region or NULL | |
189 | */ | |
190 | struct fpga_region | |
191 | *fpga_region_create(struct device *dev, | |
192 | struct fpga_manager *mgr, | |
193 | int (*get_bridges)(struct fpga_region *)) | |
0fa20cdf | 194 | { |
9f368977 | 195 | struct fpga_region *region; |
0fa20cdf AT |
196 | int id, ret = 0; |
197 | ||
9f368977 AT |
198 | region = kzalloc(sizeof(*region), GFP_KERNEL); |
199 | if (!region) | |
200 | return NULL; | |
201 | ||
0fa20cdf | 202 | id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL); |
52a3a7cc | 203 | if (id < 0) |
9f368977 | 204 | goto err_free; |
0fa20cdf | 205 | |
9f368977 AT |
206 | region->mgr = mgr; |
207 | region->get_bridges = get_bridges; | |
0fa20cdf AT |
208 | mutex_init(®ion->mutex); |
209 | INIT_LIST_HEAD(®ion->bridge_list); | |
9f368977 | 210 | |
0fa20cdf AT |
211 | device_initialize(®ion->dev); |
212 | region->dev.class = fpga_region_class; | |
213 | region->dev.parent = dev; | |
52a3a7cc | 214 | region->dev.of_node = dev->of_node; |
0fa20cdf | 215 | region->dev.id = id; |
0fa20cdf AT |
216 | |
217 | ret = dev_set_name(®ion->dev, "region%d", id); | |
218 | if (ret) | |
219 | goto err_remove; | |
220 | ||
9f368977 | 221 | return region; |
52a3a7cc AT |
222 | |
223 | err_remove: | |
224 | ida_simple_remove(&fpga_region_ida, id); | |
9f368977 AT |
225 | err_free: |
226 | kfree(region); | |
227 | ||
228 | return NULL; | |
229 | } | |
230 | EXPORT_SYMBOL_GPL(fpga_region_create); | |
231 | ||
232 | /** | |
233 | * fpga_region_free - free a struct fpga_region | |
234 | * @region: FPGA region created by fpga_region_create | |
235 | */ | |
236 | void fpga_region_free(struct fpga_region *region) | |
237 | { | |
238 | ida_simple_remove(&fpga_region_ida, region->dev.id); | |
239 | kfree(region); | |
240 | } | |
241 | EXPORT_SYMBOL_GPL(fpga_region_free); | |
242 | ||
917a4304 | 243 | /** |
9f368977 AT |
244 | * fpga_region_register - register a FPGA region |
245 | * @region: FPGA region created by fpga_region_create | |
246 | * Return: 0 or -errno | |
247 | */ | |
248 | int fpga_region_register(struct fpga_region *region) | |
249 | { | |
250 | return device_add(®ion->dev); | |
251 | ||
52a3a7cc AT |
252 | } |
253 | EXPORT_SYMBOL_GPL(fpga_region_register); | |
254 | ||
917a4304 | 255 | /** |
fdff4053 | 256 | * fpga_region_unregister - unregister and free a FPGA region |
9f368977 AT |
257 | * @region: FPGA region |
258 | */ | |
259 | void fpga_region_unregister(struct fpga_region *region) | |
52a3a7cc AT |
260 | { |
261 | device_unregister(®ion->dev); | |
52a3a7cc AT |
262 | } |
263 | EXPORT_SYMBOL_GPL(fpga_region_unregister); | |
264 | ||
0fa20cdf AT |
265 | static void fpga_region_dev_release(struct device *dev) |
266 | { | |
267 | struct fpga_region *region = to_fpga_region(dev); | |
268 | ||
9f368977 | 269 | fpga_region_free(region); |
0fa20cdf AT |
270 | } |
271 | ||
272 | /** | |
273 | * fpga_region_init - init function for fpga_region class | |
274 | * Creates the fpga_region class and registers a reconfig notifier. | |
275 | */ | |
276 | static int __init fpga_region_init(void) | |
277 | { | |
0fa20cdf AT |
278 | fpga_region_class = class_create(THIS_MODULE, "fpga_region"); |
279 | if (IS_ERR(fpga_region_class)) | |
280 | return PTR_ERR(fpga_region_class); | |
281 | ||
41a8b2c5 | 282 | fpga_region_class->dev_groups = fpga_region_groups; |
0fa20cdf AT |
283 | fpga_region_class->dev_release = fpga_region_dev_release; |
284 | ||
0fa20cdf | 285 | return 0; |
0fa20cdf AT |
286 | } |
287 | ||
288 | static void __exit fpga_region_exit(void) | |
289 | { | |
0fa20cdf AT |
290 | class_destroy(fpga_region_class); |
291 | ida_destroy(&fpga_region_ida); | |
292 | } | |
293 | ||
294 | subsys_initcall(fpga_region_init); | |
295 | module_exit(fpga_region_exit); | |
296 | ||
297 | MODULE_DESCRIPTION("FPGA Region"); | |
59460a93 | 298 | MODULE_AUTHOR("Alan Tull <[email protected]>"); |
0fa20cdf | 299 | MODULE_LICENSE("GPL v2"); |