Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fpga: fpga-region: device tree control for FPGA

FPGA Regions support programming FPGA under control of the Device
Tree.

Signed-off-by: Alan Tull <atull@opensource.altera.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alan Tull and committed by
Greg Kroah-Hartman
0fa20cdf 21aeda95

+615
+7
drivers/fpga/Kconfig
··· 13 13 14 14 if FPGA 15 15 16 + config FPGA_REGION 17 + tristate "FPGA Region" 18 + depends on OF && FPGA_BRIDGE 19 + help 20 + FPGA Regions allow loading FPGA images under control of 21 + the Device Tree. 22 + 16 23 config FPGA_MGR_SOCFPGA 17 24 tristate "Altera SOCFPGA FPGA Manager" 18 25 depends on ARCH_SOCFPGA
+3
drivers/fpga/Makefile
··· 11 11 12 12 # FPGA Bridge Drivers 13 13 obj-$(CONFIG_FPGA_BRIDGE) += fpga-bridge.o 14 + 15 + # High Level Interfaces 16 + obj-$(CONFIG_FPGA_REGION) += fpga-region.o
+603
drivers/fpga/fpga-region.c
··· 1 + /* 2 + * FPGA Region - Device Tree support for FPGA programming under Linux 3 + * 4 + * Copyright (C) 2013-2016 Altera Corporation 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms and conditions of the GNU General Public License, 8 + * version 2, as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope it will be useful, but WITHOUT 11 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 + * more details. 14 + * 15 + * You should have received a copy of the GNU General Public License along with 16 + * this program. If not, see <http://www.gnu.org/licenses/>. 17 + */ 18 + 19 + #include <linux/fpga/fpga-bridge.h> 20 + #include <linux/fpga/fpga-mgr.h> 21 + #include <linux/idr.h> 22 + #include <linux/kernel.h> 23 + #include <linux/list.h> 24 + #include <linux/module.h> 25 + #include <linux/of_platform.h> 26 + #include <linux/slab.h> 27 + #include <linux/spinlock.h> 28 + 29 + /** 30 + * struct fpga_region - FPGA Region structure 31 + * @dev: FPGA Region device 32 + * @mutex: enforces exclusive reference to region 33 + * @bridge_list: list of FPGA bridges specified in region 34 + * @info: fpga image specific information 35 + */ 36 + struct fpga_region { 37 + struct device dev; 38 + struct mutex mutex; /* for exclusive reference to region */ 39 + struct list_head bridge_list; 40 + struct fpga_image_info *info; 41 + }; 42 + 43 + #define to_fpga_region(d) container_of(d, struct fpga_region, dev) 44 + 45 + static DEFINE_IDA(fpga_region_ida); 46 + static struct class *fpga_region_class; 47 + 48 + static const struct of_device_id fpga_region_of_match[] = { 49 + { .compatible = "fpga-region", }, 50 + {}, 51 + }; 52 + MODULE_DEVICE_TABLE(of, fpga_region_of_match); 53 + 54 + static int fpga_region_of_node_match(struct device *dev, const void *data) 55 + { 56 + return dev->of_node == data; 57 + } 58 + 59 + /** 60 + * fpga_region_find - find FPGA region 61 + * @np: device node of FPGA Region 62 + * Caller will need to put_device(&region->dev) when done. 63 + * Returns FPGA Region struct or NULL 64 + */ 65 + static struct fpga_region *fpga_region_find(struct device_node *np) 66 + { 67 + struct device *dev; 68 + 69 + dev = class_find_device(fpga_region_class, NULL, np, 70 + fpga_region_of_node_match); 71 + if (!dev) 72 + return NULL; 73 + 74 + return to_fpga_region(dev); 75 + } 76 + 77 + /** 78 + * fpga_region_get - get an exclusive reference to a fpga region 79 + * @region: FPGA Region struct 80 + * 81 + * Caller should call fpga_region_put() when done with region. 82 + * 83 + * Return fpga_region struct if successful. 84 + * Return -EBUSY if someone already has a reference to the region. 85 + * Return -ENODEV if @np is not a FPGA Region. 86 + */ 87 + static struct fpga_region *fpga_region_get(struct fpga_region *region) 88 + { 89 + struct device *dev = &region->dev; 90 + 91 + if (!mutex_trylock(&region->mutex)) { 92 + dev_dbg(dev, "%s: FPGA Region already in use\n", __func__); 93 + return ERR_PTR(-EBUSY); 94 + } 95 + 96 + get_device(dev); 97 + of_node_get(dev->of_node); 98 + if (!try_module_get(dev->parent->driver->owner)) { 99 + of_node_put(dev->of_node); 100 + put_device(dev); 101 + mutex_unlock(&region->mutex); 102 + return ERR_PTR(-ENODEV); 103 + } 104 + 105 + dev_dbg(&region->dev, "get\n"); 106 + 107 + return region; 108 + } 109 + 110 + /** 111 + * fpga_region_put - release a reference to a region 112 + * 113 + * @region: FPGA region 114 + */ 115 + static void fpga_region_put(struct fpga_region *region) 116 + { 117 + struct device *dev = &region->dev; 118 + 119 + dev_dbg(&region->dev, "put\n"); 120 + 121 + module_put(dev->parent->driver->owner); 122 + of_node_put(dev->of_node); 123 + put_device(dev); 124 + mutex_unlock(&region->mutex); 125 + } 126 + 127 + /** 128 + * fpga_region_get_manager - get exclusive reference for FPGA manager 129 + * @region: FPGA region 130 + * 131 + * Get FPGA Manager from "fpga-mgr" property or from ancestor region. 132 + * 133 + * Caller should call fpga_mgr_put() when done with manager. 134 + * 135 + * Return: fpga manager struct or IS_ERR() condition containing error code. 136 + */ 137 + static struct fpga_manager *fpga_region_get_manager(struct fpga_region *region) 138 + { 139 + struct device *dev = &region->dev; 140 + struct device_node *np = dev->of_node; 141 + struct device_node *mgr_node; 142 + struct fpga_manager *mgr; 143 + 144 + of_node_get(np); 145 + while (np) { 146 + if (of_device_is_compatible(np, "fpga-region")) { 147 + mgr_node = of_parse_phandle(np, "fpga-mgr", 0); 148 + if (mgr_node) { 149 + mgr = of_fpga_mgr_get(mgr_node); 150 + of_node_put(np); 151 + return mgr; 152 + } 153 + } 154 + np = of_get_next_parent(np); 155 + } 156 + of_node_put(np); 157 + 158 + return ERR_PTR(-EINVAL); 159 + } 160 + 161 + /** 162 + * fpga_region_get_bridges - create a list of bridges 163 + * @region: FPGA region 164 + * @overlay: device node of the overlay 165 + * 166 + * Create a list of bridges including the parent bridge and the bridges 167 + * specified by "fpga-bridges" property. Note that the 168 + * fpga_bridges_enable/disable/put functions are all fine with an empty list 169 + * if that happens. 170 + * 171 + * Caller should call fpga_bridges_put(&region->bridge_list) when 172 + * done with the bridges. 173 + * 174 + * Return 0 for success (even if there are no bridges specified) 175 + * or -EBUSY if any of the bridges are in use. 176 + */ 177 + static int fpga_region_get_bridges(struct fpga_region *region, 178 + struct device_node *overlay) 179 + { 180 + struct device *dev = &region->dev; 181 + struct device_node *region_np = dev->of_node; 182 + struct device_node *br, *np, *parent_br = NULL; 183 + int i, ret; 184 + 185 + /* If parent is a bridge, add to list */ 186 + ret = fpga_bridge_get_to_list(region_np->parent, region->info, 187 + &region->bridge_list); 188 + if (ret == -EBUSY) 189 + return ret; 190 + 191 + if (!ret) 192 + parent_br = region_np->parent; 193 + 194 + /* If overlay has a list of bridges, use it. */ 195 + if (of_parse_phandle(overlay, "fpga-bridges", 0)) 196 + np = overlay; 197 + else 198 + np = region_np; 199 + 200 + for (i = 0; ; i++) { 201 + br = of_parse_phandle(np, "fpga-bridges", i); 202 + if (!br) 203 + break; 204 + 205 + /* If parent bridge is in list, skip it. */ 206 + if (br == parent_br) 207 + continue; 208 + 209 + /* If node is a bridge, get it and add to list */ 210 + ret = fpga_bridge_get_to_list(br, region->info, 211 + &region->bridge_list); 212 + 213 + /* If any of the bridges are in use, give up */ 214 + if (ret == -EBUSY) { 215 + fpga_bridges_put(&region->bridge_list); 216 + return -EBUSY; 217 + } 218 + } 219 + 220 + return 0; 221 + } 222 + 223 + /** 224 + * fpga_region_program_fpga - program FPGA 225 + * @region: FPGA region 226 + * @firmware_name: name of FPGA image firmware file 227 + * @overlay: device node of the overlay 228 + * Program an FPGA using information in the device tree. 229 + * Function assumes that there is a firmware-name property. 230 + * Return 0 for success or negative error code. 231 + */ 232 + static int fpga_region_program_fpga(struct fpga_region *region, 233 + const char *firmware_name, 234 + struct device_node *overlay) 235 + { 236 + struct fpga_manager *mgr; 237 + int ret; 238 + 239 + region = fpga_region_get(region); 240 + if (IS_ERR(region)) { 241 + pr_err("failed to get fpga region\n"); 242 + return PTR_ERR(region); 243 + } 244 + 245 + mgr = fpga_region_get_manager(region); 246 + if (IS_ERR(mgr)) { 247 + pr_err("failed to get fpga region manager\n"); 248 + return PTR_ERR(mgr); 249 + } 250 + 251 + ret = fpga_region_get_bridges(region, overlay); 252 + if (ret) { 253 + pr_err("failed to get fpga region bridges\n"); 254 + goto err_put_mgr; 255 + } 256 + 257 + ret = fpga_bridges_disable(&region->bridge_list); 258 + if (ret) { 259 + pr_err("failed to disable region bridges\n"); 260 + goto err_put_br; 261 + } 262 + 263 + ret = fpga_mgr_firmware_load(mgr, region->info, firmware_name); 264 + if (ret) { 265 + pr_err("failed to load fpga image\n"); 266 + goto err_put_br; 267 + } 268 + 269 + ret = fpga_bridges_enable(&region->bridge_list); 270 + if (ret) { 271 + pr_err("failed to enable region bridges\n"); 272 + goto err_put_br; 273 + } 274 + 275 + fpga_mgr_put(mgr); 276 + fpga_region_put(region); 277 + 278 + return 0; 279 + 280 + err_put_br: 281 + fpga_bridges_put(&region->bridge_list); 282 + err_put_mgr: 283 + fpga_mgr_put(mgr); 284 + fpga_region_put(region); 285 + 286 + return ret; 287 + } 288 + 289 + /** 290 + * child_regions_with_firmware 291 + * @overlay: device node of the overlay 292 + * 293 + * If the overlay adds child FPGA regions, they are not allowed to have 294 + * firmware-name property. 295 + * 296 + * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name. 297 + */ 298 + static int child_regions_with_firmware(struct device_node *overlay) 299 + { 300 + struct device_node *child_region; 301 + const char *child_firmware_name; 302 + int ret = 0; 303 + 304 + of_node_get(overlay); 305 + 306 + child_region = of_find_matching_node(overlay, fpga_region_of_match); 307 + while (child_region) { 308 + if (!of_property_read_string(child_region, "firmware-name", 309 + &child_firmware_name)) { 310 + ret = -EINVAL; 311 + break; 312 + } 313 + child_region = of_find_matching_node(child_region, 314 + fpga_region_of_match); 315 + } 316 + 317 + of_node_put(child_region); 318 + 319 + if (ret) 320 + pr_err("firmware-name not allowed in child FPGA region: %s", 321 + child_region->full_name); 322 + 323 + return ret; 324 + } 325 + 326 + /** 327 + * fpga_region_notify_pre_apply - pre-apply overlay notification 328 + * 329 + * @region: FPGA region that the overlay was applied to 330 + * @nd: overlay notification data 331 + * 332 + * Called after when an overlay targeted to a FPGA Region is about to be 333 + * applied. Function will check the properties that will be added to the FPGA 334 + * region. If the checks pass, it will program the FPGA. 335 + * 336 + * The checks are: 337 + * The overlay must add either firmware-name or external-fpga-config property 338 + * to the FPGA Region. 339 + * 340 + * firmware-name : program the FPGA 341 + * external-fpga-config : FPGA is already programmed 342 + * 343 + * The overlay can add other FPGA regions, but child FPGA regions cannot have a 344 + * firmware-name property since those regions don't exist yet. 345 + * 346 + * If the overlay that breaks the rules, notifier returns an error and the 347 + * overlay is rejected before it goes into the main tree. 348 + * 349 + * Returns 0 for success or negative error code for failure. 350 + */ 351 + static int fpga_region_notify_pre_apply(struct fpga_region *region, 352 + struct of_overlay_notify_data *nd) 353 + { 354 + const char *firmware_name = NULL; 355 + struct fpga_image_info *info; 356 + int ret; 357 + 358 + info = devm_kzalloc(&region->dev, sizeof(*info), GFP_KERNEL); 359 + if (!info) 360 + return -ENOMEM; 361 + 362 + region->info = info; 363 + 364 + /* Reject overlay if child FPGA Regions have firmware-name property */ 365 + ret = child_regions_with_firmware(nd->overlay); 366 + if (ret) 367 + return ret; 368 + 369 + /* Read FPGA region properties from the overlay */ 370 + if (of_property_read_bool(nd->overlay, "partial-fpga-config")) 371 + info->flags |= FPGA_MGR_PARTIAL_RECONFIG; 372 + 373 + if (of_property_read_bool(nd->overlay, "external-fpga-config")) 374 + info->flags |= FPGA_MGR_EXTERNAL_CONFIG; 375 + 376 + of_property_read_string(nd->overlay, "firmware-name", &firmware_name); 377 + 378 + of_property_read_u32(nd->overlay, "region-unfreeze-timeout-us", 379 + &info->enable_timeout_us); 380 + 381 + of_property_read_u32(nd->overlay, "region-freeze-timeout-us", 382 + &info->disable_timeout_us); 383 + 384 + /* If FPGA was externally programmed, don't specify firmware */ 385 + if ((info->flags & FPGA_MGR_EXTERNAL_CONFIG) && firmware_name) { 386 + pr_err("error: specified firmware and external-fpga-config"); 387 + return -EINVAL; 388 + } 389 + 390 + /* FPGA is already configured externally. We're done. */ 391 + if (info->flags & FPGA_MGR_EXTERNAL_CONFIG) 392 + return 0; 393 + 394 + /* If we got this far, we should be programming the FPGA */ 395 + if (!firmware_name) { 396 + pr_err("should specify firmware-name or external-fpga-config\n"); 397 + return -EINVAL; 398 + } 399 + 400 + return fpga_region_program_fpga(region, firmware_name, nd->overlay); 401 + } 402 + 403 + /** 404 + * fpga_region_notify_post_remove - post-remove overlay notification 405 + * 406 + * @region: FPGA region that was targeted by the overlay that was removed 407 + * @nd: overlay notification data 408 + * 409 + * Called after an overlay has been removed if the overlay's target was a 410 + * FPGA region. 411 + */ 412 + static void fpga_region_notify_post_remove(struct fpga_region *region, 413 + struct of_overlay_notify_data *nd) 414 + { 415 + fpga_bridges_disable(&region->bridge_list); 416 + fpga_bridges_put(&region->bridge_list); 417 + devm_kfree(&region->dev, region->info); 418 + region->info = NULL; 419 + } 420 + 421 + /** 422 + * of_fpga_region_notify - reconfig notifier for dynamic DT changes 423 + * @nb: notifier block 424 + * @action: notifier action 425 + * @arg: reconfig data 426 + * 427 + * This notifier handles programming a FPGA when a "firmware-name" property is 428 + * added to a fpga-region. 429 + * 430 + * Returns NOTIFY_OK or error if FPGA programming fails. 431 + */ 432 + static int of_fpga_region_notify(struct notifier_block *nb, 433 + unsigned long action, void *arg) 434 + { 435 + struct of_overlay_notify_data *nd = arg; 436 + struct fpga_region *region; 437 + int ret; 438 + 439 + switch (action) { 440 + case OF_OVERLAY_PRE_APPLY: 441 + pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__); 442 + break; 443 + case OF_OVERLAY_POST_APPLY: 444 + pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__); 445 + return NOTIFY_OK; /* not for us */ 446 + case OF_OVERLAY_PRE_REMOVE: 447 + pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__); 448 + return NOTIFY_OK; /* not for us */ 449 + case OF_OVERLAY_POST_REMOVE: 450 + pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__); 451 + break; 452 + default: /* should not happen */ 453 + return NOTIFY_OK; 454 + } 455 + 456 + region = fpga_region_find(nd->target); 457 + if (!region) 458 + return NOTIFY_OK; 459 + 460 + ret = 0; 461 + switch (action) { 462 + case OF_OVERLAY_PRE_APPLY: 463 + ret = fpga_region_notify_pre_apply(region, nd); 464 + break; 465 + 466 + case OF_OVERLAY_POST_REMOVE: 467 + fpga_region_notify_post_remove(region, nd); 468 + break; 469 + } 470 + 471 + put_device(&region->dev); 472 + 473 + if (ret) 474 + return notifier_from_errno(ret); 475 + 476 + return NOTIFY_OK; 477 + } 478 + 479 + static struct notifier_block fpga_region_of_nb = { 480 + .notifier_call = of_fpga_region_notify, 481 + }; 482 + 483 + static int fpga_region_probe(struct platform_device *pdev) 484 + { 485 + struct device *dev = &pdev->dev; 486 + struct device_node *np = dev->of_node; 487 + struct fpga_region *region; 488 + int id, ret = 0; 489 + 490 + region = kzalloc(sizeof(*region), GFP_KERNEL); 491 + if (!region) 492 + return -ENOMEM; 493 + 494 + id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL); 495 + if (id < 0) { 496 + ret = id; 497 + goto err_kfree; 498 + } 499 + 500 + mutex_init(&region->mutex); 501 + INIT_LIST_HEAD(&region->bridge_list); 502 + 503 + device_initialize(&region->dev); 504 + region->dev.class = fpga_region_class; 505 + region->dev.parent = dev; 506 + region->dev.of_node = np; 507 + region->dev.id = id; 508 + dev_set_drvdata(dev, region); 509 + 510 + ret = dev_set_name(&region->dev, "region%d", id); 511 + if (ret) 512 + goto err_remove; 513 + 514 + ret = device_add(&region->dev); 515 + if (ret) 516 + goto err_remove; 517 + 518 + of_platform_populate(np, fpga_region_of_match, NULL, &region->dev); 519 + 520 + dev_info(dev, "FPGA Region probed\n"); 521 + 522 + return 0; 523 + 524 + err_remove: 525 + ida_simple_remove(&fpga_region_ida, id); 526 + err_kfree: 527 + kfree(region); 528 + 529 + return ret; 530 + } 531 + 532 + static int fpga_region_remove(struct platform_device *pdev) 533 + { 534 + struct fpga_region *region = platform_get_drvdata(pdev); 535 + 536 + device_unregister(&region->dev); 537 + 538 + return 0; 539 + } 540 + 541 + static struct platform_driver fpga_region_driver = { 542 + .probe = fpga_region_probe, 543 + .remove = fpga_region_remove, 544 + .driver = { 545 + .name = "fpga-region", 546 + .of_match_table = of_match_ptr(fpga_region_of_match), 547 + }, 548 + }; 549 + 550 + static void fpga_region_dev_release(struct device *dev) 551 + { 552 + struct fpga_region *region = to_fpga_region(dev); 553 + 554 + ida_simple_remove(&fpga_region_ida, region->dev.id); 555 + kfree(region); 556 + } 557 + 558 + /** 559 + * fpga_region_init - init function for fpga_region class 560 + * Creates the fpga_region class and registers a reconfig notifier. 561 + */ 562 + static int __init fpga_region_init(void) 563 + { 564 + int ret; 565 + 566 + fpga_region_class = class_create(THIS_MODULE, "fpga_region"); 567 + if (IS_ERR(fpga_region_class)) 568 + return PTR_ERR(fpga_region_class); 569 + 570 + fpga_region_class->dev_release = fpga_region_dev_release; 571 + 572 + ret = of_overlay_notifier_register(&fpga_region_of_nb); 573 + if (ret) 574 + goto err_class; 575 + 576 + ret = platform_driver_register(&fpga_region_driver); 577 + if (ret) 578 + goto err_plat; 579 + 580 + return 0; 581 + 582 + err_plat: 583 + of_overlay_notifier_unregister(&fpga_region_of_nb); 584 + err_class: 585 + class_destroy(fpga_region_class); 586 + ida_destroy(&fpga_region_ida); 587 + return ret; 588 + } 589 + 590 + static void __exit fpga_region_exit(void) 591 + { 592 + platform_driver_unregister(&fpga_region_driver); 593 + of_overlay_notifier_unregister(&fpga_region_of_nb); 594 + class_destroy(fpga_region_class); 595 + ida_destroy(&fpga_region_ida); 596 + } 597 + 598 + subsys_initcall(fpga_region_init); 599 + module_exit(fpga_region_exit); 600 + 601 + MODULE_DESCRIPTION("FPGA Region"); 602 + MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>"); 603 + MODULE_LICENSE("GPL v2");
+2
include/linux/fpga/fpga-mgr.h
··· 65 65 /* 66 66 * FPGA Manager flags 67 67 * FPGA_MGR_PARTIAL_RECONFIG: do partial reconfiguration if supported 68 + * FPGA_MGR_EXTERNAL_CONFIG: FPGA has been configured prior to Linux booting 68 69 */ 69 70 #define FPGA_MGR_PARTIAL_RECONFIG BIT(0) 71 + #define FPGA_MGR_EXTERNAL_CONFIG BIT(1) 70 72 71 73 /** 72 74 * struct fpga_image_info - information specific to a FPGA image