]>
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 | 18 | static DEFINE_IDA(fpga_region_ida); |
1a22ec09 | 19 | static const struct class fpga_region_class; |
0fa20cdf | 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 | ||
1a22ec09 | 27 | dev = class_find_device(&fpga_region_class, start, data, match); |
503d4b7a AT |
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 | * | |
baa57b33 MP |
41 | * Return: |
42 | * * fpga_region struct if successful. | |
43 | * * -EBUSY if someone already has a reference to the region. | |
44 | * * -ENODEV if can't take parent driver module refcount. | |
0fa20cdf AT |
45 | */ |
46 | static struct fpga_region *fpga_region_get(struct fpga_region *region) | |
47 | { | |
48 | struct device *dev = ®ion->dev; | |
49 | ||
50 | if (!mutex_trylock(®ion->mutex)) { | |
51 | dev_dbg(dev, "%s: FPGA Region already in use\n", __func__); | |
52 | return ERR_PTR(-EBUSY); | |
53 | } | |
54 | ||
55 | get_device(dev); | |
b7c0e1ec | 56 | if (!try_module_get(region->ops_owner)) { |
0fa20cdf AT |
57 | put_device(dev); |
58 | mutex_unlock(®ion->mutex); | |
59 | return ERR_PTR(-ENODEV); | |
60 | } | |
61 | ||
c3d971ad | 62 | dev_dbg(dev, "get\n"); |
0fa20cdf AT |
63 | |
64 | return region; | |
65 | } | |
66 | ||
67 | /** | |
68 | * fpga_region_put - release a reference to a region | |
69 | * | |
70 | * @region: FPGA region | |
71 | */ | |
72 | static void fpga_region_put(struct fpga_region *region) | |
73 | { | |
74 | struct device *dev = ®ion->dev; | |
75 | ||
c3d971ad | 76 | dev_dbg(dev, "put\n"); |
0fa20cdf | 77 | |
b7c0e1ec | 78 | module_put(region->ops_owner); |
0fa20cdf AT |
79 | put_device(dev); |
80 | mutex_unlock(®ion->mutex); | |
81 | } | |
82 | ||
0fa20cdf AT |
83 | /** |
84 | * fpga_region_program_fpga - program FPGA | |
917a4304 | 85 | * |
0fa20cdf | 86 | * @region: FPGA region |
917a4304 | 87 | * |
61c32102 | 88 | * Program an FPGA using fpga image info (region->info). |
093a89d4 AT |
89 | * If the region has a get_bridges function, the exclusive reference for the |
90 | * bridges will be held if programming succeeds. This is intended to prevent | |
91 | * reprogramming the region until the caller considers it safe to do so. | |
92 | * The caller will need to call fpga_bridges_put() before attempting to | |
93 | * reprogram the region. | |
917a4304 | 94 | * |
baa57b33 | 95 | * Return: 0 for success or negative error code. |
0fa20cdf | 96 | */ |
59460a93 | 97 | int fpga_region_program_fpga(struct fpga_region *region) |
0fa20cdf | 98 | { |
ebf877a5 | 99 | struct device *dev = ®ion->dev; |
61c32102 | 100 | struct fpga_image_info *info = region->info; |
0fa20cdf AT |
101 | int ret; |
102 | ||
103 | region = fpga_region_get(region); | |
104 | if (IS_ERR(region)) { | |
c3d971ad | 105 | dev_err(dev, "failed to get FPGA region\n"); |
0fa20cdf AT |
106 | return PTR_ERR(region); |
107 | } | |
108 | ||
1df2dd7f | 109 | ret = fpga_mgr_lock(region->mgr); |
ebf877a5 AT |
110 | if (ret) { |
111 | dev_err(dev, "FPGA manager is busy\n"); | |
1df2dd7f | 112 | goto err_put_region; |
ebf877a5 AT |
113 | } |
114 | ||
52a3a7cc AT |
115 | /* |
116 | * In some cases, we already have a list of bridges in the | |
117 | * fpga region struct. Or we don't have any bridges. | |
118 | */ | |
119 | if (region->get_bridges) { | |
120 | ret = region->get_bridges(region); | |
121 | if (ret) { | |
122 | dev_err(dev, "failed to get fpga region bridges\n"); | |
123 | goto err_unlock_mgr; | |
124 | } | |
0fa20cdf AT |
125 | } |
126 | ||
127 | ret = fpga_bridges_disable(®ion->bridge_list); | |
128 | if (ret) { | |
c3d971ad | 129 | dev_err(dev, "failed to disable bridges\n"); |
0fa20cdf AT |
130 | goto err_put_br; |
131 | } | |
132 | ||
61c32102 | 133 | ret = fpga_mgr_load(region->mgr, info); |
0fa20cdf | 134 | if (ret) { |
c3d971ad | 135 | dev_err(dev, "failed to load FPGA image\n"); |
0fa20cdf AT |
136 | goto err_put_br; |
137 | } | |
138 | ||
139 | ret = fpga_bridges_enable(®ion->bridge_list); | |
140 | if (ret) { | |
c3d971ad | 141 | dev_err(dev, "failed to enable region bridges\n"); |
0fa20cdf AT |
142 | goto err_put_br; |
143 | } | |
144 | ||
1df2dd7f | 145 | fpga_mgr_unlock(region->mgr); |
0fa20cdf AT |
146 | fpga_region_put(region); |
147 | ||
148 | return 0; | |
149 | ||
150 | err_put_br: | |
52a3a7cc AT |
151 | if (region->get_bridges) |
152 | fpga_bridges_put(®ion->bridge_list); | |
ebf877a5 | 153 | err_unlock_mgr: |
1df2dd7f | 154 | fpga_mgr_unlock(region->mgr); |
e73bbf64 | 155 | err_put_region: |
0fa20cdf AT |
156 | fpga_region_put(region); |
157 | ||
158 | return ret; | |
159 | } | |
59460a93 | 160 | EXPORT_SYMBOL_GPL(fpga_region_program_fpga); |
0fa20cdf | 161 | |
41a8b2c5 WH |
162 | static ssize_t compat_id_show(struct device *dev, |
163 | struct device_attribute *attr, char *buf) | |
164 | { | |
165 | struct fpga_region *region = to_fpga_region(dev); | |
166 | ||
167 | if (!region->compat_id) | |
168 | return -ENOENT; | |
169 | ||
170 | return sprintf(buf, "%016llx%016llx\n", | |
171 | (unsigned long long)region->compat_id->id_h, | |
172 | (unsigned long long)region->compat_id->id_l); | |
173 | } | |
174 | ||
175 | static DEVICE_ATTR_RO(compat_id); | |
176 | ||
177 | static struct attribute *fpga_region_attrs[] = { | |
178 | &dev_attr_compat_id.attr, | |
179 | NULL, | |
180 | }; | |
181 | ATTRIBUTE_GROUPS(fpga_region); | |
182 | ||
9f368977 | 183 | /** |
b7c0e1ec | 184 | * __fpga_region_register_full - create and register an FPGA Region device |
5e77886d | 185 | * @parent: device parent |
8886a579 | 186 | * @info: parameters for FPGA Region |
b7c0e1ec | 187 | * @owner: module containing the get_bridges function |
fea82b7f | 188 | * |
8886a579 | 189 | * Return: struct fpga_region or ERR_PTR() |
9f368977 | 190 | */ |
8886a579 | 191 | struct fpga_region * |
b7c0e1ec MP |
192 | __fpga_region_register_full(struct device *parent, const struct fpga_region_info *info, |
193 | struct module *owner) | |
0fa20cdf | 194 | { |
9f368977 | 195 | struct fpga_region *region; |
0fa20cdf AT |
196 | int id, ret = 0; |
197 | ||
8886a579 RW |
198 | if (!info) { |
199 | dev_err(parent, | |
200 | "Attempt to register without required info structure\n"); | |
201 | return ERR_PTR(-EINVAL); | |
202 | } | |
203 | ||
9f368977 AT |
204 | region = kzalloc(sizeof(*region), GFP_KERNEL); |
205 | if (!region) | |
8886a579 | 206 | return ERR_PTR(-ENOMEM); |
9f368977 | 207 | |
a5e3d775 | 208 | id = ida_alloc(&fpga_region_ida, GFP_KERNEL); |
8886a579 RW |
209 | if (id < 0) { |
210 | ret = id; | |
9f368977 | 211 | goto err_free; |
8886a579 RW |
212 | } |
213 | ||
214 | region->mgr = info->mgr; | |
215 | region->compat_id = info->compat_id; | |
216 | region->priv = info->priv; | |
217 | region->get_bridges = info->get_bridges; | |
b7c0e1ec | 218 | region->ops_owner = owner; |
0fa20cdf AT |
219 | |
220 | mutex_init(®ion->mutex); | |
221 | INIT_LIST_HEAD(®ion->bridge_list); | |
9f368977 | 222 | |
1a22ec09 | 223 | region->dev.class = &fpga_region_class; |
5e77886d RW |
224 | region->dev.parent = parent; |
225 | region->dev.of_node = parent->of_node; | |
0fa20cdf | 226 | region->dev.id = id; |
0fa20cdf AT |
227 | |
228 | ret = dev_set_name(®ion->dev, "region%d", id); | |
229 | if (ret) | |
230 | goto err_remove; | |
231 | ||
8886a579 RW |
232 | ret = device_register(®ion->dev); |
233 | if (ret) { | |
234 | put_device(®ion->dev); | |
235 | return ERR_PTR(ret); | |
236 | } | |
237 | ||
9f368977 | 238 | return region; |
52a3a7cc AT |
239 | |
240 | err_remove: | |
a5e3d775 | 241 | ida_free(&fpga_region_ida, id); |
9f368977 AT |
242 | err_free: |
243 | kfree(region); | |
244 | ||
8886a579 | 245 | return ERR_PTR(ret); |
fea82b7f | 246 | } |
b7c0e1ec | 247 | EXPORT_SYMBOL_GPL(__fpga_region_register_full); |
fea82b7f AT |
248 | |
249 | /** | |
b7c0e1ec | 250 | * __fpga_region_register - create and register an FPGA Region device |
5e77886d | 251 | * @parent: device parent |
fea82b7f AT |
252 | * @mgr: manager that programs this region |
253 | * @get_bridges: optional function to get bridges to a list | |
b7c0e1ec | 254 | * @owner: module containing the get_bridges function |
fea82b7f | 255 | * |
8886a579 RW |
256 | * This simple version of the register function should be sufficient for most users. |
257 | * The fpga_region_register_full() function is available for users that need to | |
258 | * pass additional, optional parameters. | |
fea82b7f | 259 | * |
8886a579 | 260 | * Return: struct fpga_region or ERR_PTR() |
fea82b7f | 261 | */ |
8886a579 | 262 | struct fpga_region * |
b7c0e1ec MP |
263 | __fpga_region_register(struct device *parent, struct fpga_manager *mgr, |
264 | int (*get_bridges)(struct fpga_region *), struct module *owner) | |
fea82b7f | 265 | { |
8886a579 | 266 | struct fpga_region_info info = { 0 }; |
fea82b7f | 267 | |
8886a579 RW |
268 | info.mgr = mgr; |
269 | info.get_bridges = get_bridges; | |
fea82b7f | 270 | |
b7c0e1ec | 271 | return __fpga_region_register_full(parent, &info, owner); |
52a3a7cc | 272 | } |
b7c0e1ec | 273 | EXPORT_SYMBOL_GPL(__fpga_region_register); |
52a3a7cc | 274 | |
917a4304 | 275 | /** |
011c49e3 | 276 | * fpga_region_unregister - unregister an FPGA region |
9f368977 | 277 | * @region: FPGA region |
fea82b7f | 278 | * |
011c49e3 | 279 | * This function is intended for use in an FPGA region driver's remove function. |
9f368977 AT |
280 | */ |
281 | void fpga_region_unregister(struct fpga_region *region) | |
52a3a7cc AT |
282 | { |
283 | device_unregister(®ion->dev); | |
52a3a7cc AT |
284 | } |
285 | EXPORT_SYMBOL_GPL(fpga_region_unregister); | |
286 | ||
0fa20cdf AT |
287 | static void fpga_region_dev_release(struct device *dev) |
288 | { | |
8886a579 RW |
289 | struct fpga_region *region = to_fpga_region(dev); |
290 | ||
a5e3d775 | 291 | ida_free(&fpga_region_ida, region->dev.id); |
8886a579 | 292 | kfree(region); |
0fa20cdf AT |
293 | } |
294 | ||
1a22ec09 IO |
295 | static const struct class fpga_region_class = { |
296 | .name = "fpga_region", | |
297 | .dev_groups = fpga_region_groups, | |
298 | .dev_release = fpga_region_dev_release, | |
299 | }; | |
300 | ||
0fa20cdf | 301 | /** |
baa57b33 MP |
302 | * fpga_region_init - creates the fpga_region class. |
303 | * | |
304 | * Return: 0 on success or ERR_PTR() on error. | |
0fa20cdf AT |
305 | */ |
306 | static int __init fpga_region_init(void) | |
307 | { | |
1a22ec09 | 308 | return class_register(&fpga_region_class); |
0fa20cdf AT |
309 | } |
310 | ||
311 | static void __exit fpga_region_exit(void) | |
312 | { | |
1a22ec09 | 313 | class_unregister(&fpga_region_class); |
0fa20cdf AT |
314 | ida_destroy(&fpga_region_ida); |
315 | } | |
316 | ||
317 | subsys_initcall(fpga_region_init); | |
318 | module_exit(fpga_region_exit); | |
319 | ||
320 | MODULE_DESCRIPTION("FPGA Region"); | |
59460a93 | 321 | MODULE_AUTHOR("Alan Tull <[email protected]>"); |
0fa20cdf | 322 | MODULE_LICENSE("GPL v2"); |