Commit | Line | Data |
---|---|---|
473f01f7 | 1 | // SPDX-License-Identifier: GPL-2.0 |
0fa20cdf | 2 | /* |
c71e8050 | 3 | * FPGA Region - Support for FPGA programming under Linux |
0fa20cdf AT |
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 | ||
57ce2e40 NM |
21 | struct fpga_region * |
22 | fpga_region_class_find(struct device *start, const void *data, | |
23 | int (*match)(struct device *, const void *)) | |
503d4b7a AT |
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 | 35 | /** |
011c49e3 | 36 | * fpga_region_get - get an exclusive reference to an fpga region |
0fa20cdf AT |
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. | |
011c49e3 | 43 | * Return -ENODEV if @np is not an FPGA Region. |
0fa20cdf AT |
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 | 182 | /** |
8886a579 | 183 | * fpga_region_register_full - create and register an FPGA Region device |
5e77886d | 184 | * @parent: device parent |
8886a579 | 185 | * @info: parameters for FPGA Region |
fea82b7f | 186 | * |
8886a579 | 187 | * Return: struct fpga_region or ERR_PTR() |
9f368977 | 188 | */ |
8886a579 RW |
189 | struct fpga_region * |
190 | fpga_region_register_full(struct device *parent, const struct fpga_region_info *info) | |
0fa20cdf | 191 | { |
9f368977 | 192 | struct fpga_region *region; |
0fa20cdf AT |
193 | int id, ret = 0; |
194 | ||
8886a579 RW |
195 | if (!info) { |
196 | dev_err(parent, | |
197 | "Attempt to register without required info structure\n"); | |
198 | return ERR_PTR(-EINVAL); | |
199 | } | |
200 | ||
9f368977 AT |
201 | region = kzalloc(sizeof(*region), GFP_KERNEL); |
202 | if (!region) | |
8886a579 | 203 | return ERR_PTR(-ENOMEM); |
9f368977 | 204 | |
a5e3d775 | 205 | id = ida_alloc(&fpga_region_ida, GFP_KERNEL); |
8886a579 RW |
206 | if (id < 0) { |
207 | ret = id; | |
9f368977 | 208 | goto err_free; |
8886a579 RW |
209 | } |
210 | ||
211 | region->mgr = info->mgr; | |
212 | region->compat_id = info->compat_id; | |
213 | region->priv = info->priv; | |
214 | region->get_bridges = info->get_bridges; | |
0fa20cdf AT |
215 | |
216 | mutex_init(®ion->mutex); | |
217 | INIT_LIST_HEAD(®ion->bridge_list); | |
9f368977 | 218 | |
0fa20cdf | 219 | region->dev.class = fpga_region_class; |
5e77886d RW |
220 | region->dev.parent = parent; |
221 | region->dev.of_node = parent->of_node; | |
0fa20cdf | 222 | region->dev.id = id; |
0fa20cdf AT |
223 | |
224 | ret = dev_set_name(®ion->dev, "region%d", id); | |
225 | if (ret) | |
226 | goto err_remove; | |
227 | ||
8886a579 RW |
228 | ret = device_register(®ion->dev); |
229 | if (ret) { | |
230 | put_device(®ion->dev); | |
231 | return ERR_PTR(ret); | |
232 | } | |
233 | ||
9f368977 | 234 | return region; |
52a3a7cc AT |
235 | |
236 | err_remove: | |
a5e3d775 | 237 | ida_free(&fpga_region_ida, id); |
9f368977 AT |
238 | err_free: |
239 | kfree(region); | |
240 | ||
8886a579 | 241 | return ERR_PTR(ret); |
fea82b7f | 242 | } |
8886a579 | 243 | EXPORT_SYMBOL_GPL(fpga_region_register_full); |
fea82b7f AT |
244 | |
245 | /** | |
8886a579 | 246 | * fpga_region_register - create and register an FPGA Region device |
5e77886d | 247 | * @parent: device parent |
fea82b7f AT |
248 | * @mgr: manager that programs this region |
249 | * @get_bridges: optional function to get bridges to a list | |
250 | * | |
8886a579 RW |
251 | * This simple version of the register function should be sufficient for most users. |
252 | * The fpga_region_register_full() function is available for users that need to | |
253 | * pass additional, optional parameters. | |
fea82b7f | 254 | * |
8886a579 | 255 | * Return: struct fpga_region or ERR_PTR() |
fea82b7f | 256 | */ |
8886a579 RW |
257 | struct fpga_region * |
258 | fpga_region_register(struct device *parent, struct fpga_manager *mgr, | |
259 | int (*get_bridges)(struct fpga_region *)) | |
fea82b7f | 260 | { |
8886a579 | 261 | struct fpga_region_info info = { 0 }; |
fea82b7f | 262 | |
8886a579 RW |
263 | info.mgr = mgr; |
264 | info.get_bridges = get_bridges; | |
fea82b7f | 265 | |
8886a579 | 266 | return fpga_region_register_full(parent, &info); |
52a3a7cc AT |
267 | } |
268 | EXPORT_SYMBOL_GPL(fpga_region_register); | |
269 | ||
917a4304 | 270 | /** |
011c49e3 | 271 | * fpga_region_unregister - unregister an FPGA region |
9f368977 | 272 | * @region: FPGA region |
fea82b7f | 273 | * |
011c49e3 | 274 | * This function is intended for use in an FPGA region driver's remove function. |
9f368977 AT |
275 | */ |
276 | void fpga_region_unregister(struct fpga_region *region) | |
52a3a7cc AT |
277 | { |
278 | device_unregister(®ion->dev); | |
52a3a7cc AT |
279 | } |
280 | EXPORT_SYMBOL_GPL(fpga_region_unregister); | |
281 | ||
0fa20cdf AT |
282 | static void fpga_region_dev_release(struct device *dev) |
283 | { | |
8886a579 RW |
284 | struct fpga_region *region = to_fpga_region(dev); |
285 | ||
a5e3d775 | 286 | ida_free(&fpga_region_ida, region->dev.id); |
8886a579 | 287 | kfree(region); |
0fa20cdf AT |
288 | } |
289 | ||
290 | /** | |
291 | * fpga_region_init - init function for fpga_region class | |
292 | * Creates the fpga_region class and registers a reconfig notifier. | |
293 | */ | |
294 | static int __init fpga_region_init(void) | |
295 | { | |
0fa20cdf AT |
296 | fpga_region_class = class_create(THIS_MODULE, "fpga_region"); |
297 | if (IS_ERR(fpga_region_class)) | |
298 | return PTR_ERR(fpga_region_class); | |
299 | ||
41a8b2c5 | 300 | fpga_region_class->dev_groups = fpga_region_groups; |
0fa20cdf AT |
301 | fpga_region_class->dev_release = fpga_region_dev_release; |
302 | ||
0fa20cdf | 303 | return 0; |
0fa20cdf AT |
304 | } |
305 | ||
306 | static void __exit fpga_region_exit(void) | |
307 | { | |
0fa20cdf AT |
308 | class_destroy(fpga_region_class); |
309 | ida_destroy(&fpga_region_ida); | |
310 | } | |
311 | ||
312 | subsys_initcall(fpga_region_init); | |
313 | module_exit(fpga_region_exit); | |
314 | ||
315 | MODULE_DESCRIPTION("FPGA Region"); | |
59460a93 | 316 | MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); |
0fa20cdf | 317 | MODULE_LICENSE("GPL v2"); |