1ef3acdd8SAlan Tull /* 2ef3acdd8SAlan Tull * FPGA Region - Device Tree support for FPGA programming under Linux 3ef3acdd8SAlan Tull * 4ef3acdd8SAlan Tull * Copyright (C) 2013-2016 Altera Corporation 5ef3acdd8SAlan Tull * Copyright (C) 2017 Intel Corporation 6ef3acdd8SAlan Tull * 7ef3acdd8SAlan Tull * This program is free software; you can redistribute it and/or modify it 8ef3acdd8SAlan Tull * under the terms and conditions of the GNU General Public License, 9ef3acdd8SAlan Tull * version 2, as published by the Free Software Foundation. 10ef3acdd8SAlan Tull * 11ef3acdd8SAlan Tull * This program is distributed in the hope it will be useful, but WITHOUT 12ef3acdd8SAlan Tull * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13ef3acdd8SAlan Tull * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14ef3acdd8SAlan Tull * more details. 15ef3acdd8SAlan Tull * 16ef3acdd8SAlan Tull * You should have received a copy of the GNU General Public License along with 17ef3acdd8SAlan Tull * this program. If not, see <http://www.gnu.org/licenses/>. 18ef3acdd8SAlan Tull */ 19ef3acdd8SAlan Tull 20ef3acdd8SAlan Tull #include <linux/fpga/fpga-bridge.h> 21ef3acdd8SAlan Tull #include <linux/fpga/fpga-mgr.h> 22ef3acdd8SAlan Tull #include <linux/fpga/fpga-region.h> 23ef3acdd8SAlan Tull #include <linux/idr.h> 24ef3acdd8SAlan Tull #include <linux/kernel.h> 25ef3acdd8SAlan Tull #include <linux/list.h> 26ef3acdd8SAlan Tull #include <linux/module.h> 27ef3acdd8SAlan Tull #include <linux/of_platform.h> 28ef3acdd8SAlan Tull #include <linux/slab.h> 29ef3acdd8SAlan Tull #include <linux/spinlock.h> 30ef3acdd8SAlan Tull 31ef3acdd8SAlan Tull static const struct of_device_id fpga_region_of_match[] = { 32ef3acdd8SAlan Tull { .compatible = "fpga-region", }, 33ef3acdd8SAlan Tull {}, 34ef3acdd8SAlan Tull }; 35ef3acdd8SAlan Tull MODULE_DEVICE_TABLE(of, fpga_region_of_match); 36ef3acdd8SAlan Tull 37ef3acdd8SAlan Tull static int fpga_region_of_node_match(struct device *dev, const void *data) 38ef3acdd8SAlan Tull { 39ef3acdd8SAlan Tull return dev->of_node == data; 40ef3acdd8SAlan Tull } 41ef3acdd8SAlan Tull 42ef3acdd8SAlan Tull /** 43ef3acdd8SAlan Tull * of_fpga_region_find - find FPGA region 44ef3acdd8SAlan Tull * @np: device node of FPGA Region 45ef3acdd8SAlan Tull * 46ef3acdd8SAlan Tull * Caller will need to put_device(®ion->dev) when done. 47ef3acdd8SAlan Tull * 48ef3acdd8SAlan Tull * Returns FPGA Region struct or NULL 49ef3acdd8SAlan Tull */ 50ef3acdd8SAlan Tull static struct fpga_region *of_fpga_region_find(struct device_node *np) 51ef3acdd8SAlan Tull { 52ef3acdd8SAlan Tull return fpga_region_class_find(NULL, np, fpga_region_of_node_match); 53ef3acdd8SAlan Tull } 54ef3acdd8SAlan Tull 55ef3acdd8SAlan Tull /** 56ef3acdd8SAlan Tull * of_fpga_region_get_mgr - get reference for FPGA manager 57ef3acdd8SAlan Tull * @np: device node of FPGA region 58ef3acdd8SAlan Tull * 59ef3acdd8SAlan Tull * Get FPGA Manager from "fpga-mgr" property or from ancestor region. 60ef3acdd8SAlan Tull * 61ef3acdd8SAlan Tull * Caller should call fpga_mgr_put() when done with manager. 62ef3acdd8SAlan Tull * 63ef3acdd8SAlan Tull * Return: fpga manager struct or IS_ERR() condition containing error code. 64ef3acdd8SAlan Tull */ 65ef3acdd8SAlan Tull static struct fpga_manager *of_fpga_region_get_mgr(struct device_node *np) 66ef3acdd8SAlan Tull { 67ef3acdd8SAlan Tull struct device_node *mgr_node; 68ef3acdd8SAlan Tull struct fpga_manager *mgr; 69ef3acdd8SAlan Tull 70ef3acdd8SAlan Tull of_node_get(np); 71ef3acdd8SAlan Tull while (np) { 72ef3acdd8SAlan Tull if (of_device_is_compatible(np, "fpga-region")) { 73ef3acdd8SAlan Tull mgr_node = of_parse_phandle(np, "fpga-mgr", 0); 74ef3acdd8SAlan Tull if (mgr_node) { 75ef3acdd8SAlan Tull mgr = of_fpga_mgr_get(mgr_node); 76ef3acdd8SAlan Tull of_node_put(np); 77ef3acdd8SAlan Tull return mgr; 78ef3acdd8SAlan Tull } 79ef3acdd8SAlan Tull } 80ef3acdd8SAlan Tull np = of_get_next_parent(np); 81ef3acdd8SAlan Tull } 82ef3acdd8SAlan Tull of_node_put(np); 83ef3acdd8SAlan Tull 84ef3acdd8SAlan Tull return ERR_PTR(-EINVAL); 85ef3acdd8SAlan Tull } 86ef3acdd8SAlan Tull 87ef3acdd8SAlan Tull /** 88ef3acdd8SAlan Tull * of_fpga_region_get_bridges - create a list of bridges 89ef3acdd8SAlan Tull * @region: FPGA region 90ef3acdd8SAlan Tull * 91ef3acdd8SAlan Tull * Create a list of bridges including the parent bridge and the bridges 92ef3acdd8SAlan Tull * specified by "fpga-bridges" property. Note that the 93ef3acdd8SAlan Tull * fpga_bridges_enable/disable/put functions are all fine with an empty list 94ef3acdd8SAlan Tull * if that happens. 95ef3acdd8SAlan Tull * 96ef3acdd8SAlan Tull * Caller should call fpga_bridges_put(®ion->bridge_list) when 97ef3acdd8SAlan Tull * done with the bridges. 98ef3acdd8SAlan Tull * 99ef3acdd8SAlan Tull * Return 0 for success (even if there are no bridges specified) 100ef3acdd8SAlan Tull * or -EBUSY if any of the bridges are in use. 101ef3acdd8SAlan Tull */ 102ef3acdd8SAlan Tull static int of_fpga_region_get_bridges(struct fpga_region *region) 103ef3acdd8SAlan Tull { 104ef3acdd8SAlan Tull struct device *dev = ®ion->dev; 105ef3acdd8SAlan Tull struct device_node *region_np = dev->of_node; 106ef3acdd8SAlan Tull struct fpga_image_info *info = region->info; 107ef3acdd8SAlan Tull struct device_node *br, *np, *parent_br = NULL; 108ef3acdd8SAlan Tull int i, ret; 109ef3acdd8SAlan Tull 110ef3acdd8SAlan Tull /* If parent is a bridge, add to list */ 111ef3acdd8SAlan Tull ret = of_fpga_bridge_get_to_list(region_np->parent, info, 112ef3acdd8SAlan Tull ®ion->bridge_list); 113ef3acdd8SAlan Tull 114ef3acdd8SAlan Tull /* -EBUSY means parent is a bridge that is under use. Give up. */ 115ef3acdd8SAlan Tull if (ret == -EBUSY) 116ef3acdd8SAlan Tull return ret; 117ef3acdd8SAlan Tull 118ef3acdd8SAlan Tull /* Zero return code means parent was a bridge and was added to list. */ 119ef3acdd8SAlan Tull if (!ret) 120ef3acdd8SAlan Tull parent_br = region_np->parent; 121ef3acdd8SAlan Tull 122ef3acdd8SAlan Tull /* If overlay has a list of bridges, use it. */ 123ef3acdd8SAlan Tull if (of_parse_phandle(info->overlay, "fpga-bridges", 0)) 124ef3acdd8SAlan Tull np = info->overlay; 125ef3acdd8SAlan Tull else 126ef3acdd8SAlan Tull np = region_np; 127ef3acdd8SAlan Tull 128ef3acdd8SAlan Tull for (i = 0; ; i++) { 129ef3acdd8SAlan Tull br = of_parse_phandle(np, "fpga-bridges", i); 130ef3acdd8SAlan Tull if (!br) 131ef3acdd8SAlan Tull break; 132ef3acdd8SAlan Tull 133ef3acdd8SAlan Tull /* If parent bridge is in list, skip it. */ 134ef3acdd8SAlan Tull if (br == parent_br) 135ef3acdd8SAlan Tull continue; 136ef3acdd8SAlan Tull 137ef3acdd8SAlan Tull /* If node is a bridge, get it and add to list */ 138ef3acdd8SAlan Tull ret = of_fpga_bridge_get_to_list(br, info, 139ef3acdd8SAlan Tull ®ion->bridge_list); 140ef3acdd8SAlan Tull 141ef3acdd8SAlan Tull /* If any of the bridges are in use, give up */ 142ef3acdd8SAlan Tull if (ret == -EBUSY) { 143ef3acdd8SAlan Tull fpga_bridges_put(®ion->bridge_list); 144ef3acdd8SAlan Tull return -EBUSY; 145ef3acdd8SAlan Tull } 146ef3acdd8SAlan Tull } 147ef3acdd8SAlan Tull 148ef3acdd8SAlan Tull return 0; 149ef3acdd8SAlan Tull } 150ef3acdd8SAlan Tull 151ef3acdd8SAlan Tull /** 152ef3acdd8SAlan Tull * child_regions_with_firmware 153ef3acdd8SAlan Tull * @overlay: device node of the overlay 154ef3acdd8SAlan Tull * 155ef3acdd8SAlan Tull * If the overlay adds child FPGA regions, they are not allowed to have 156ef3acdd8SAlan Tull * firmware-name property. 157ef3acdd8SAlan Tull * 158ef3acdd8SAlan Tull * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name. 159ef3acdd8SAlan Tull */ 160ef3acdd8SAlan Tull static int child_regions_with_firmware(struct device_node *overlay) 161ef3acdd8SAlan Tull { 162ef3acdd8SAlan Tull struct device_node *child_region; 163ef3acdd8SAlan Tull const char *child_firmware_name; 164ef3acdd8SAlan Tull int ret = 0; 165ef3acdd8SAlan Tull 166ef3acdd8SAlan Tull of_node_get(overlay); 167ef3acdd8SAlan Tull 168ef3acdd8SAlan Tull child_region = of_find_matching_node(overlay, fpga_region_of_match); 169ef3acdd8SAlan Tull while (child_region) { 170ef3acdd8SAlan Tull if (!of_property_read_string(child_region, "firmware-name", 171ef3acdd8SAlan Tull &child_firmware_name)) { 172ef3acdd8SAlan Tull ret = -EINVAL; 173ef3acdd8SAlan Tull break; 174ef3acdd8SAlan Tull } 175ef3acdd8SAlan Tull child_region = of_find_matching_node(child_region, 176ef3acdd8SAlan Tull fpga_region_of_match); 177ef3acdd8SAlan Tull } 178ef3acdd8SAlan Tull 179ef3acdd8SAlan Tull of_node_put(child_region); 180ef3acdd8SAlan Tull 181ef3acdd8SAlan Tull if (ret) 182ef3acdd8SAlan Tull pr_err("firmware-name not allowed in child FPGA region: %pOF", 183ef3acdd8SAlan Tull child_region); 184ef3acdd8SAlan Tull 185ef3acdd8SAlan Tull return ret; 186ef3acdd8SAlan Tull } 187ef3acdd8SAlan Tull 188ef3acdd8SAlan Tull /** 189ef3acdd8SAlan Tull * of_fpga_region_parse_ov - parse and check overlay applied to region 190ef3acdd8SAlan Tull * 191ef3acdd8SAlan Tull * @region: FPGA region 192ef3acdd8SAlan Tull * @overlay: overlay applied to the FPGA region 193ef3acdd8SAlan Tull * 194ef3acdd8SAlan Tull * Given an overlay applied to a FPGA region, parse the FPGA image specific 195ef3acdd8SAlan Tull * info in the overlay and do some checking. 196ef3acdd8SAlan Tull * 197ef3acdd8SAlan Tull * Returns: 198ef3acdd8SAlan Tull * NULL if overlay doesn't direct us to program the FPGA. 199ef3acdd8SAlan Tull * fpga_image_info struct if there is an image to program. 200ef3acdd8SAlan Tull * error code for invalid overlay. 201ef3acdd8SAlan Tull */ 202ef3acdd8SAlan Tull static struct fpga_image_info *of_fpga_region_parse_ov( 203ef3acdd8SAlan Tull struct fpga_region *region, 204ef3acdd8SAlan Tull struct device_node *overlay) 205ef3acdd8SAlan Tull { 206ef3acdd8SAlan Tull struct device *dev = ®ion->dev; 207ef3acdd8SAlan Tull struct fpga_image_info *info; 208ef3acdd8SAlan Tull const char *firmware_name; 209ef3acdd8SAlan Tull int ret; 210ef3acdd8SAlan Tull 211ef3acdd8SAlan Tull if (region->info) { 212ef3acdd8SAlan Tull dev_err(dev, "Region already has overlay applied.\n"); 213ef3acdd8SAlan Tull return ERR_PTR(-EINVAL); 214ef3acdd8SAlan Tull } 215ef3acdd8SAlan Tull 216ef3acdd8SAlan Tull /* 217ef3acdd8SAlan Tull * Reject overlay if child FPGA Regions added in the overlay have 218ef3acdd8SAlan Tull * firmware-name property (would mean that an FPGA region that has 219ef3acdd8SAlan Tull * not been added to the live tree yet is doing FPGA programming). 220ef3acdd8SAlan Tull */ 221ef3acdd8SAlan Tull ret = child_regions_with_firmware(overlay); 222ef3acdd8SAlan Tull if (ret) 223ef3acdd8SAlan Tull return ERR_PTR(ret); 224ef3acdd8SAlan Tull 225ef3acdd8SAlan Tull info = fpga_image_info_alloc(dev); 226ef3acdd8SAlan Tull if (!info) 227ef3acdd8SAlan Tull return ERR_PTR(-ENOMEM); 228ef3acdd8SAlan Tull 229ef3acdd8SAlan Tull info->overlay = overlay; 230ef3acdd8SAlan Tull 231ef3acdd8SAlan Tull /* Read FPGA region properties from the overlay */ 232ef3acdd8SAlan Tull if (of_property_read_bool(overlay, "partial-fpga-config")) 233ef3acdd8SAlan Tull info->flags |= FPGA_MGR_PARTIAL_RECONFIG; 234ef3acdd8SAlan Tull 235ef3acdd8SAlan Tull if (of_property_read_bool(overlay, "external-fpga-config")) 236ef3acdd8SAlan Tull info->flags |= FPGA_MGR_EXTERNAL_CONFIG; 237ef3acdd8SAlan Tull 238ef3acdd8SAlan Tull if (of_property_read_bool(overlay, "encrypted-fpga-config")) 239ef3acdd8SAlan Tull info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM; 240ef3acdd8SAlan Tull 241ef3acdd8SAlan Tull if (!of_property_read_string(overlay, "firmware-name", 242ef3acdd8SAlan Tull &firmware_name)) { 243ef3acdd8SAlan Tull info->firmware_name = devm_kstrdup(dev, firmware_name, 244ef3acdd8SAlan Tull GFP_KERNEL); 245ef3acdd8SAlan Tull if (!info->firmware_name) 246ef3acdd8SAlan Tull return ERR_PTR(-ENOMEM); 247ef3acdd8SAlan Tull } 248ef3acdd8SAlan Tull 249ef3acdd8SAlan Tull of_property_read_u32(overlay, "region-unfreeze-timeout-us", 250ef3acdd8SAlan Tull &info->enable_timeout_us); 251ef3acdd8SAlan Tull 252ef3acdd8SAlan Tull of_property_read_u32(overlay, "region-freeze-timeout-us", 253ef3acdd8SAlan Tull &info->disable_timeout_us); 254ef3acdd8SAlan Tull 255ef3acdd8SAlan Tull of_property_read_u32(overlay, "config-complete-timeout-us", 256ef3acdd8SAlan Tull &info->config_complete_timeout_us); 257ef3acdd8SAlan Tull 258ef3acdd8SAlan Tull /* If overlay is not programming the FPGA, don't need FPGA image info */ 259ef3acdd8SAlan Tull if (!info->firmware_name) { 260ef3acdd8SAlan Tull ret = 0; 261ef3acdd8SAlan Tull goto ret_no_info; 262ef3acdd8SAlan Tull } 263ef3acdd8SAlan Tull 264ef3acdd8SAlan Tull /* 265ef3acdd8SAlan Tull * If overlay informs us FPGA was externally programmed, specifying 266ef3acdd8SAlan Tull * firmware here would be ambiguous. 267ef3acdd8SAlan Tull */ 268ef3acdd8SAlan Tull if (info->flags & FPGA_MGR_EXTERNAL_CONFIG) { 269ef3acdd8SAlan Tull dev_err(dev, "error: specified firmware and external-fpga-config"); 270ef3acdd8SAlan Tull ret = -EINVAL; 271ef3acdd8SAlan Tull goto ret_no_info; 272ef3acdd8SAlan Tull } 273ef3acdd8SAlan Tull 274ef3acdd8SAlan Tull return info; 275ef3acdd8SAlan Tull ret_no_info: 276ef3acdd8SAlan Tull fpga_image_info_free(info); 277ef3acdd8SAlan Tull return ERR_PTR(ret); 278ef3acdd8SAlan Tull } 279ef3acdd8SAlan Tull 280ef3acdd8SAlan Tull /** 281ef3acdd8SAlan Tull * of_fpga_region_notify_pre_apply - pre-apply overlay notification 282ef3acdd8SAlan Tull * 283ef3acdd8SAlan Tull * @region: FPGA region that the overlay was applied to 284ef3acdd8SAlan Tull * @nd: overlay notification data 285ef3acdd8SAlan Tull * 286ef3acdd8SAlan Tull * Called when an overlay targeted to a FPGA Region is about to be applied. 287ef3acdd8SAlan Tull * Parses the overlay for properties that influence how the FPGA will be 288ef3acdd8SAlan Tull * programmed and does some checking. If the checks pass, programs the FPGA. 289ef3acdd8SAlan Tull * If the checks fail, overlay is rejected and does not get added to the 290ef3acdd8SAlan Tull * live tree. 291ef3acdd8SAlan Tull * 292ef3acdd8SAlan Tull * Returns 0 for success or negative error code for failure. 293ef3acdd8SAlan Tull */ 294ef3acdd8SAlan Tull static int of_fpga_region_notify_pre_apply(struct fpga_region *region, 295ef3acdd8SAlan Tull struct of_overlay_notify_data *nd) 296ef3acdd8SAlan Tull { 297ef3acdd8SAlan Tull struct device *dev = ®ion->dev; 298ef3acdd8SAlan Tull struct fpga_image_info *info; 299ef3acdd8SAlan Tull int ret; 300ef3acdd8SAlan Tull 301ef3acdd8SAlan Tull info = of_fpga_region_parse_ov(region, nd->overlay); 302ef3acdd8SAlan Tull if (IS_ERR(info)) 303ef3acdd8SAlan Tull return PTR_ERR(info); 304ef3acdd8SAlan Tull 3058a541679SAlan Tull /* If overlay doesn't program the FPGA, accept it anyway. */ 306ef3acdd8SAlan Tull if (!info) 307ef3acdd8SAlan Tull return 0; 308ef3acdd8SAlan Tull 3098a541679SAlan Tull if (region->info) { 3108a541679SAlan Tull dev_err(dev, "Region already has overlay applied.\n"); 3118a541679SAlan Tull return -EINVAL; 3128a541679SAlan Tull } 3138a541679SAlan Tull 314ef3acdd8SAlan Tull region->info = info; 315ef3acdd8SAlan Tull ret = fpga_region_program_fpga(region); 316ef3acdd8SAlan Tull if (ret) { 317ef3acdd8SAlan Tull /* error; reject overlay */ 318ef3acdd8SAlan Tull fpga_image_info_free(info); 319ef3acdd8SAlan Tull region->info = NULL; 320ef3acdd8SAlan Tull } 321ef3acdd8SAlan Tull 322ef3acdd8SAlan Tull return ret; 323ef3acdd8SAlan Tull } 324ef3acdd8SAlan Tull 325ef3acdd8SAlan Tull /** 326ef3acdd8SAlan Tull * of_fpga_region_notify_post_remove - post-remove overlay notification 327ef3acdd8SAlan Tull * 328ef3acdd8SAlan Tull * @region: FPGA region that was targeted by the overlay that was removed 329ef3acdd8SAlan Tull * @nd: overlay notification data 330ef3acdd8SAlan Tull * 331ef3acdd8SAlan Tull * Called after an overlay has been removed if the overlay's target was a 332ef3acdd8SAlan Tull * FPGA region. 333ef3acdd8SAlan Tull */ 334ef3acdd8SAlan Tull static void of_fpga_region_notify_post_remove(struct fpga_region *region, 335ef3acdd8SAlan Tull struct of_overlay_notify_data *nd) 336ef3acdd8SAlan Tull { 337ef3acdd8SAlan Tull fpga_bridges_disable(®ion->bridge_list); 338ef3acdd8SAlan Tull fpga_bridges_put(®ion->bridge_list); 339ef3acdd8SAlan Tull fpga_image_info_free(region->info); 340ef3acdd8SAlan Tull region->info = NULL; 341ef3acdd8SAlan Tull } 342ef3acdd8SAlan Tull 343ef3acdd8SAlan Tull /** 344ef3acdd8SAlan Tull * of_fpga_region_notify - reconfig notifier for dynamic DT changes 345ef3acdd8SAlan Tull * @nb: notifier block 346ef3acdd8SAlan Tull * @action: notifier action 347ef3acdd8SAlan Tull * @arg: reconfig data 348ef3acdd8SAlan Tull * 349ef3acdd8SAlan Tull * This notifier handles programming a FPGA when a "firmware-name" property is 350ef3acdd8SAlan Tull * added to a fpga-region. 351ef3acdd8SAlan Tull * 352ef3acdd8SAlan Tull * Returns NOTIFY_OK or error if FPGA programming fails. 353ef3acdd8SAlan Tull */ 354ef3acdd8SAlan Tull static int of_fpga_region_notify(struct notifier_block *nb, 355ef3acdd8SAlan Tull unsigned long action, void *arg) 356ef3acdd8SAlan Tull { 357ef3acdd8SAlan Tull struct of_overlay_notify_data *nd = arg; 358ef3acdd8SAlan Tull struct fpga_region *region; 359ef3acdd8SAlan Tull int ret; 360ef3acdd8SAlan Tull 361ef3acdd8SAlan Tull switch (action) { 362ef3acdd8SAlan Tull case OF_OVERLAY_PRE_APPLY: 363ef3acdd8SAlan Tull pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__); 364ef3acdd8SAlan Tull break; 365ef3acdd8SAlan Tull case OF_OVERLAY_POST_APPLY: 366ef3acdd8SAlan Tull pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__); 367ef3acdd8SAlan Tull return NOTIFY_OK; /* not for us */ 368ef3acdd8SAlan Tull case OF_OVERLAY_PRE_REMOVE: 369ef3acdd8SAlan Tull pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__); 370ef3acdd8SAlan Tull return NOTIFY_OK; /* not for us */ 371ef3acdd8SAlan Tull case OF_OVERLAY_POST_REMOVE: 372ef3acdd8SAlan Tull pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__); 373ef3acdd8SAlan Tull break; 374ef3acdd8SAlan Tull default: /* should not happen */ 375ef3acdd8SAlan Tull return NOTIFY_OK; 376ef3acdd8SAlan Tull } 377ef3acdd8SAlan Tull 378ef3acdd8SAlan Tull region = of_fpga_region_find(nd->target); 379ef3acdd8SAlan Tull if (!region) 380ef3acdd8SAlan Tull return NOTIFY_OK; 381ef3acdd8SAlan Tull 382ef3acdd8SAlan Tull ret = 0; 383ef3acdd8SAlan Tull switch (action) { 384ef3acdd8SAlan Tull case OF_OVERLAY_PRE_APPLY: 385ef3acdd8SAlan Tull ret = of_fpga_region_notify_pre_apply(region, nd); 386ef3acdd8SAlan Tull break; 387ef3acdd8SAlan Tull 388ef3acdd8SAlan Tull case OF_OVERLAY_POST_REMOVE: 389ef3acdd8SAlan Tull of_fpga_region_notify_post_remove(region, nd); 390ef3acdd8SAlan Tull break; 391ef3acdd8SAlan Tull } 392ef3acdd8SAlan Tull 393ef3acdd8SAlan Tull put_device(®ion->dev); 394ef3acdd8SAlan Tull 395ef3acdd8SAlan Tull if (ret) 396ef3acdd8SAlan Tull return notifier_from_errno(ret); 397ef3acdd8SAlan Tull 398ef3acdd8SAlan Tull return NOTIFY_OK; 399ef3acdd8SAlan Tull } 400ef3acdd8SAlan Tull 401ef3acdd8SAlan Tull static struct notifier_block fpga_region_of_nb = { 402ef3acdd8SAlan Tull .notifier_call = of_fpga_region_notify, 403ef3acdd8SAlan Tull }; 404ef3acdd8SAlan Tull 405ef3acdd8SAlan Tull static int of_fpga_region_probe(struct platform_device *pdev) 406ef3acdd8SAlan Tull { 407ef3acdd8SAlan Tull struct device *dev = &pdev->dev; 408ef3acdd8SAlan Tull struct device_node *np = dev->of_node; 409ef3acdd8SAlan Tull struct fpga_region *region; 410ef3acdd8SAlan Tull struct fpga_manager *mgr; 411ef3acdd8SAlan Tull int ret; 412ef3acdd8SAlan Tull 413ef3acdd8SAlan Tull /* Find the FPGA mgr specified by region or parent region. */ 414ef3acdd8SAlan Tull mgr = of_fpga_region_get_mgr(np); 415ef3acdd8SAlan Tull if (IS_ERR(mgr)) 416ef3acdd8SAlan Tull return -EPROBE_DEFER; 417ef3acdd8SAlan Tull 418ef3acdd8SAlan Tull region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); 419ef3acdd8SAlan Tull if (!region) { 420ef3acdd8SAlan Tull ret = -ENOMEM; 421ef3acdd8SAlan Tull goto eprobe_mgr_put; 422ef3acdd8SAlan Tull } 423ef3acdd8SAlan Tull 424ef3acdd8SAlan Tull region->mgr = mgr; 425ef3acdd8SAlan Tull 426ef3acdd8SAlan Tull /* Specify how to get bridges for this type of region. */ 427ef3acdd8SAlan Tull region->get_bridges = of_fpga_region_get_bridges; 428ef3acdd8SAlan Tull 429ef3acdd8SAlan Tull ret = fpga_region_register(dev, region); 430ef3acdd8SAlan Tull if (ret) 431ef3acdd8SAlan Tull goto eprobe_mgr_put; 432ef3acdd8SAlan Tull 433ef3acdd8SAlan Tull of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); 434ef3acdd8SAlan Tull 435ef3acdd8SAlan Tull dev_info(dev, "FPGA Region probed\n"); 436ef3acdd8SAlan Tull 437ef3acdd8SAlan Tull return 0; 438ef3acdd8SAlan Tull 439ef3acdd8SAlan Tull eprobe_mgr_put: 440ef3acdd8SAlan Tull fpga_mgr_put(mgr); 441ef3acdd8SAlan Tull return ret; 442ef3acdd8SAlan Tull } 443ef3acdd8SAlan Tull 444ef3acdd8SAlan Tull static int of_fpga_region_remove(struct platform_device *pdev) 445ef3acdd8SAlan Tull { 446ef3acdd8SAlan Tull struct fpga_region *region = platform_get_drvdata(pdev); 447ef3acdd8SAlan Tull 448ef3acdd8SAlan Tull fpga_region_unregister(region); 449ef3acdd8SAlan Tull fpga_mgr_put(region->mgr); 450ef3acdd8SAlan Tull 451ef3acdd8SAlan Tull return 0; 452ef3acdd8SAlan Tull } 453ef3acdd8SAlan Tull 454ef3acdd8SAlan Tull static struct platform_driver of_fpga_region_driver = { 455ef3acdd8SAlan Tull .probe = of_fpga_region_probe, 456ef3acdd8SAlan Tull .remove = of_fpga_region_remove, 457ef3acdd8SAlan Tull .driver = { 458ef3acdd8SAlan Tull .name = "of-fpga-region", 459ef3acdd8SAlan Tull .of_match_table = of_match_ptr(fpga_region_of_match), 460ef3acdd8SAlan Tull }, 461ef3acdd8SAlan Tull }; 462ef3acdd8SAlan Tull 463ef3acdd8SAlan Tull /** 464ef3acdd8SAlan Tull * fpga_region_init - init function for fpga_region class 465ef3acdd8SAlan Tull * Creates the fpga_region class and registers a reconfig notifier. 466ef3acdd8SAlan Tull */ 467ef3acdd8SAlan Tull static int __init of_fpga_region_init(void) 468ef3acdd8SAlan Tull { 469ef3acdd8SAlan Tull int ret; 470ef3acdd8SAlan Tull 471ef3acdd8SAlan Tull ret = of_overlay_notifier_register(&fpga_region_of_nb); 472ef3acdd8SAlan Tull if (ret) 473ef3acdd8SAlan Tull return ret; 474ef3acdd8SAlan Tull 475ef3acdd8SAlan Tull ret = platform_driver_register(&of_fpga_region_driver); 476ef3acdd8SAlan Tull if (ret) 477ef3acdd8SAlan Tull goto err_plat; 478ef3acdd8SAlan Tull 479ef3acdd8SAlan Tull return 0; 480ef3acdd8SAlan Tull 481ef3acdd8SAlan Tull err_plat: 482ef3acdd8SAlan Tull of_overlay_notifier_unregister(&fpga_region_of_nb); 483ef3acdd8SAlan Tull return ret; 484ef3acdd8SAlan Tull } 485ef3acdd8SAlan Tull 486ef3acdd8SAlan Tull static void __exit of_fpga_region_exit(void) 487ef3acdd8SAlan Tull { 488ef3acdd8SAlan Tull platform_driver_unregister(&of_fpga_region_driver); 489ef3acdd8SAlan Tull of_overlay_notifier_unregister(&fpga_region_of_nb); 490ef3acdd8SAlan Tull } 491ef3acdd8SAlan Tull 492ef3acdd8SAlan Tull subsys_initcall(of_fpga_region_init); 493ef3acdd8SAlan Tull module_exit(of_fpga_region_exit); 494ef3acdd8SAlan Tull 495ef3acdd8SAlan Tull MODULE_DESCRIPTION("FPGA Region"); 496ef3acdd8SAlan Tull MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); 497ef3acdd8SAlan Tull MODULE_LICENSE("GPL v2"); 498