18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for FPGA Management Engine (FME) Partial Reconfiguration 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017-2018 Intel Corporation, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Kang Luwei <luwei.kang@intel.com> 98c2ecf20Sopenharmony_ci * Xiao Guangrong <guangrong.xiao@linux.intel.com> 108c2ecf20Sopenharmony_ci * Wu Hao <hao.wu@intel.com> 118c2ecf20Sopenharmony_ci * Joseph Grecco <joe.grecco@intel.com> 128c2ecf20Sopenharmony_ci * Enno Luebbers <enno.luebbers@intel.com> 138c2ecf20Sopenharmony_ci * Tim Whisonant <tim.whisonant@intel.com> 148c2ecf20Sopenharmony_ci * Ananda Ravuri <ananda.ravuri@intel.com> 158c2ecf20Sopenharmony_ci * Christopher Rauer <christopher.rauer@intel.com> 168c2ecf20Sopenharmony_ci * Henry Mitchel <henry.mitchel@intel.com> 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <linux/device.h> 218c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 228c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 238c2ecf20Sopenharmony_ci#include <linux/fpga/fpga-mgr.h> 248c2ecf20Sopenharmony_ci#include <linux/fpga/fpga-bridge.h> 258c2ecf20Sopenharmony_ci#include <linux/fpga/fpga-region.h> 268c2ecf20Sopenharmony_ci#include <linux/fpga-dfl.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#include "dfl.h" 298c2ecf20Sopenharmony_ci#include "dfl-fme.h" 308c2ecf20Sopenharmony_ci#include "dfl-fme-pr.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic struct dfl_fme_region * 338c2ecf20Sopenharmony_cidfl_fme_region_find_by_port_id(struct dfl_fme *fme, int port_id) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci struct dfl_fme_region *fme_region; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci list_for_each_entry(fme_region, &fme->region_list, node) 388c2ecf20Sopenharmony_ci if (fme_region->port_id == port_id) 398c2ecf20Sopenharmony_ci return fme_region; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci return NULL; 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic int dfl_fme_region_match(struct device *dev, const void *data) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return dev->parent == data; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic struct fpga_region *dfl_fme_region_find(struct dfl_fme *fme, int port_id) 508c2ecf20Sopenharmony_ci{ 518c2ecf20Sopenharmony_ci struct dfl_fme_region *fme_region; 528c2ecf20Sopenharmony_ci struct fpga_region *region; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci fme_region = dfl_fme_region_find_by_port_id(fme, port_id); 558c2ecf20Sopenharmony_ci if (!fme_region) 568c2ecf20Sopenharmony_ci return NULL; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci region = fpga_region_class_find(NULL, &fme_region->region->dev, 598c2ecf20Sopenharmony_ci dfl_fme_region_match); 608c2ecf20Sopenharmony_ci if (!region) 618c2ecf20Sopenharmony_ci return NULL; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return region; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int fme_pr(struct platform_device *pdev, unsigned long arg) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 698c2ecf20Sopenharmony_ci void __user *argp = (void __user *)arg; 708c2ecf20Sopenharmony_ci struct dfl_fpga_fme_port_pr port_pr; 718c2ecf20Sopenharmony_ci struct fpga_image_info *info; 728c2ecf20Sopenharmony_ci struct fpga_region *region; 738c2ecf20Sopenharmony_ci void __iomem *fme_hdr; 748c2ecf20Sopenharmony_ci struct dfl_fme *fme; 758c2ecf20Sopenharmony_ci unsigned long minsz; 768c2ecf20Sopenharmony_ci void *buf = NULL; 778c2ecf20Sopenharmony_ci size_t length; 788c2ecf20Sopenharmony_ci int ret = 0; 798c2ecf20Sopenharmony_ci u64 v; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci minsz = offsetofend(struct dfl_fpga_fme_port_pr, buffer_address); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (copy_from_user(&port_pr, argp, minsz)) 848c2ecf20Sopenharmony_ci return -EFAULT; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (port_pr.argsz < minsz || port_pr.flags) 878c2ecf20Sopenharmony_ci return -EINVAL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* get fme header region */ 908c2ecf20Sopenharmony_ci fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, 918c2ecf20Sopenharmony_ci FME_FEATURE_ID_HEADER); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci /* check port id */ 948c2ecf20Sopenharmony_ci v = readq(fme_hdr + FME_HDR_CAP); 958c2ecf20Sopenharmony_ci if (port_pr.port_id >= FIELD_GET(FME_CAP_NUM_PORTS, v)) { 968c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "port number more than maximum\n"); 978c2ecf20Sopenharmony_ci return -EINVAL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* 1018c2ecf20Sopenharmony_ci * align PR buffer per PR bandwidth, as HW ignores the extra padding 1028c2ecf20Sopenharmony_ci * data automatically. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_ci length = ALIGN(port_pr.buffer_size, 4); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci buf = vmalloc(length); 1078c2ecf20Sopenharmony_ci if (!buf) 1088c2ecf20Sopenharmony_ci return -ENOMEM; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (copy_from_user(buf, 1118c2ecf20Sopenharmony_ci (void __user *)(unsigned long)port_pr.buffer_address, 1128c2ecf20Sopenharmony_ci port_pr.buffer_size)) { 1138c2ecf20Sopenharmony_ci ret = -EFAULT; 1148c2ecf20Sopenharmony_ci goto free_exit; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci /* prepare fpga_image_info for PR */ 1188c2ecf20Sopenharmony_ci info = fpga_image_info_alloc(&pdev->dev); 1198c2ecf20Sopenharmony_ci if (!info) { 1208c2ecf20Sopenharmony_ci ret = -ENOMEM; 1218c2ecf20Sopenharmony_ci goto free_exit; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci info->flags |= FPGA_MGR_PARTIAL_RECONFIG; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mutex_lock(&pdata->lock); 1278c2ecf20Sopenharmony_ci fme = dfl_fpga_pdata_get_private(pdata); 1288c2ecf20Sopenharmony_ci /* fme device has been unregistered. */ 1298c2ecf20Sopenharmony_ci if (!fme) { 1308c2ecf20Sopenharmony_ci ret = -EINVAL; 1318c2ecf20Sopenharmony_ci goto unlock_exit; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci region = dfl_fme_region_find(fme, port_pr.port_id); 1358c2ecf20Sopenharmony_ci if (!region) { 1368c2ecf20Sopenharmony_ci ret = -EINVAL; 1378c2ecf20Sopenharmony_ci goto unlock_exit; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci fpga_image_info_free(region->info); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci info->buf = buf; 1438c2ecf20Sopenharmony_ci info->count = length; 1448c2ecf20Sopenharmony_ci info->region_id = port_pr.port_id; 1458c2ecf20Sopenharmony_ci region->info = info; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ret = fpga_region_program_fpga(region); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* 1508c2ecf20Sopenharmony_ci * it allows userspace to reset the PR region's logic by disabling and 1518c2ecf20Sopenharmony_ci * reenabling the bridge to clear things out between accleration runs. 1528c2ecf20Sopenharmony_ci * so no need to hold the bridges after partial reconfiguration. 1538c2ecf20Sopenharmony_ci */ 1548c2ecf20Sopenharmony_ci if (region->get_bridges) 1558c2ecf20Sopenharmony_ci fpga_bridges_put(®ion->bridge_list); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci put_device(®ion->dev); 1588c2ecf20Sopenharmony_ciunlock_exit: 1598c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 1608c2ecf20Sopenharmony_cifree_exit: 1618c2ecf20Sopenharmony_ci vfree(buf); 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/** 1668c2ecf20Sopenharmony_ci * dfl_fme_create_mgr - create fpga mgr platform device as child device 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * @pdata: fme platform_device's pdata 1698c2ecf20Sopenharmony_ci * 1708c2ecf20Sopenharmony_ci * Return: mgr platform device if successful, and error code otherwise. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_cistatic struct platform_device * 1738c2ecf20Sopenharmony_cidfl_fme_create_mgr(struct dfl_feature_platform_data *pdata, 1748c2ecf20Sopenharmony_ci struct dfl_feature *feature) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct platform_device *mgr, *fme = pdata->dev; 1778c2ecf20Sopenharmony_ci struct dfl_fme_mgr_pdata mgr_pdata; 1788c2ecf20Sopenharmony_ci int ret = -ENOMEM; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!feature->ioaddr) 1818c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci mgr_pdata.ioaddr = feature->ioaddr; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* 1868c2ecf20Sopenharmony_ci * Each FME has only one fpga-mgr, so allocate platform device using 1878c2ecf20Sopenharmony_ci * the same FME platform device id. 1888c2ecf20Sopenharmony_ci */ 1898c2ecf20Sopenharmony_ci mgr = platform_device_alloc(DFL_FPGA_FME_MGR, fme->id); 1908c2ecf20Sopenharmony_ci if (!mgr) 1918c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci mgr->dev.parent = &fme->dev; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci ret = platform_device_add_data(mgr, &mgr_pdata, sizeof(mgr_pdata)); 1968c2ecf20Sopenharmony_ci if (ret) 1978c2ecf20Sopenharmony_ci goto create_mgr_err; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci ret = platform_device_add(mgr); 2008c2ecf20Sopenharmony_ci if (ret) 2018c2ecf20Sopenharmony_ci goto create_mgr_err; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return mgr; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cicreate_mgr_err: 2068c2ecf20Sopenharmony_ci platform_device_put(mgr); 2078c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/** 2118c2ecf20Sopenharmony_ci * dfl_fme_destroy_mgr - destroy fpga mgr platform device 2128c2ecf20Sopenharmony_ci * @pdata: fme platform device's pdata 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_cistatic void dfl_fme_destroy_mgr(struct dfl_feature_platform_data *pdata) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci platform_device_unregister(priv->mgr); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci/** 2228c2ecf20Sopenharmony_ci * dfl_fme_create_bridge - create fme fpga bridge platform device as child 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * @pdata: fme platform device's pdata 2258c2ecf20Sopenharmony_ci * @port_id: port id for the bridge to be created. 2268c2ecf20Sopenharmony_ci * 2278c2ecf20Sopenharmony_ci * Return: bridge platform device if successful, and error code otherwise. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_cistatic struct dfl_fme_bridge * 2308c2ecf20Sopenharmony_cidfl_fme_create_bridge(struct dfl_feature_platform_data *pdata, int port_id) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci struct device *dev = &pdata->dev->dev; 2338c2ecf20Sopenharmony_ci struct dfl_fme_br_pdata br_pdata; 2348c2ecf20Sopenharmony_ci struct dfl_fme_bridge *fme_br; 2358c2ecf20Sopenharmony_ci int ret = -ENOMEM; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci fme_br = devm_kzalloc(dev, sizeof(*fme_br), GFP_KERNEL); 2388c2ecf20Sopenharmony_ci if (!fme_br) 2398c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci br_pdata.cdev = pdata->dfl_cdev; 2428c2ecf20Sopenharmony_ci br_pdata.port_id = port_id; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci fme_br->br = platform_device_alloc(DFL_FPGA_FME_BRIDGE, 2458c2ecf20Sopenharmony_ci PLATFORM_DEVID_AUTO); 2468c2ecf20Sopenharmony_ci if (!fme_br->br) 2478c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci fme_br->br->dev.parent = dev; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci ret = platform_device_add_data(fme_br->br, &br_pdata, sizeof(br_pdata)); 2528c2ecf20Sopenharmony_ci if (ret) 2538c2ecf20Sopenharmony_ci goto create_br_err; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci ret = platform_device_add(fme_br->br); 2568c2ecf20Sopenharmony_ci if (ret) 2578c2ecf20Sopenharmony_ci goto create_br_err; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return fme_br; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cicreate_br_err: 2628c2ecf20Sopenharmony_ci platform_device_put(fme_br->br); 2638c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/** 2678c2ecf20Sopenharmony_ci * dfl_fme_destroy_bridge - destroy fpga bridge platform device 2688c2ecf20Sopenharmony_ci * @fme_br: fme bridge to destroy 2698c2ecf20Sopenharmony_ci */ 2708c2ecf20Sopenharmony_cistatic void dfl_fme_destroy_bridge(struct dfl_fme_bridge *fme_br) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci platform_device_unregister(fme_br->br); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci/** 2768c2ecf20Sopenharmony_ci * dfl_fme_destroy_bridge - destroy all fpga bridge platform device 2778c2ecf20Sopenharmony_ci * @pdata: fme platform device's pdata 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_cistatic void dfl_fme_destroy_bridges(struct dfl_feature_platform_data *pdata) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); 2828c2ecf20Sopenharmony_ci struct dfl_fme_bridge *fbridge, *tmp; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci list_for_each_entry_safe(fbridge, tmp, &priv->bridge_list, node) { 2858c2ecf20Sopenharmony_ci list_del(&fbridge->node); 2868c2ecf20Sopenharmony_ci dfl_fme_destroy_bridge(fbridge); 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/** 2918c2ecf20Sopenharmony_ci * dfl_fme_create_region - create fpga region platform device as child 2928c2ecf20Sopenharmony_ci * 2938c2ecf20Sopenharmony_ci * @pdata: fme platform device's pdata 2948c2ecf20Sopenharmony_ci * @mgr: mgr platform device needed for region 2958c2ecf20Sopenharmony_ci * @br: br platform device needed for region 2968c2ecf20Sopenharmony_ci * @port_id: port id 2978c2ecf20Sopenharmony_ci * 2988c2ecf20Sopenharmony_ci * Return: fme region if successful, and error code otherwise. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_cistatic struct dfl_fme_region * 3018c2ecf20Sopenharmony_cidfl_fme_create_region(struct dfl_feature_platform_data *pdata, 3028c2ecf20Sopenharmony_ci struct platform_device *mgr, 3038c2ecf20Sopenharmony_ci struct platform_device *br, int port_id) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct dfl_fme_region_pdata region_pdata; 3068c2ecf20Sopenharmony_ci struct device *dev = &pdata->dev->dev; 3078c2ecf20Sopenharmony_ci struct dfl_fme_region *fme_region; 3088c2ecf20Sopenharmony_ci int ret = -ENOMEM; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci fme_region = devm_kzalloc(dev, sizeof(*fme_region), GFP_KERNEL); 3118c2ecf20Sopenharmony_ci if (!fme_region) 3128c2ecf20Sopenharmony_ci return ERR_PTR(ret); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci region_pdata.mgr = mgr; 3158c2ecf20Sopenharmony_ci region_pdata.br = br; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* 3188c2ecf20Sopenharmony_ci * Each FPGA device may have more than one port, so allocate platform 3198c2ecf20Sopenharmony_ci * device using the same port platform device id. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci fme_region->region = platform_device_alloc(DFL_FPGA_FME_REGION, br->id); 3228c2ecf20Sopenharmony_ci if (!fme_region->region) 3238c2ecf20Sopenharmony_ci return ERR_PTR(ret); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci fme_region->region->dev.parent = dev; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci ret = platform_device_add_data(fme_region->region, ®ion_pdata, 3288c2ecf20Sopenharmony_ci sizeof(region_pdata)); 3298c2ecf20Sopenharmony_ci if (ret) 3308c2ecf20Sopenharmony_ci goto create_region_err; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = platform_device_add(fme_region->region); 3338c2ecf20Sopenharmony_ci if (ret) 3348c2ecf20Sopenharmony_ci goto create_region_err; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci fme_region->port_id = port_id; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return fme_region; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cicreate_region_err: 3418c2ecf20Sopenharmony_ci platform_device_put(fme_region->region); 3428c2ecf20Sopenharmony_ci return ERR_PTR(ret); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci/** 3468c2ecf20Sopenharmony_ci * dfl_fme_destroy_region - destroy fme region 3478c2ecf20Sopenharmony_ci * @fme_region: fme region to destroy 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_cistatic void dfl_fme_destroy_region(struct dfl_fme_region *fme_region) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci platform_device_unregister(fme_region->region); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci/** 3558c2ecf20Sopenharmony_ci * dfl_fme_destroy_regions - destroy all fme regions 3568c2ecf20Sopenharmony_ci * @pdata: fme platform device's pdata 3578c2ecf20Sopenharmony_ci */ 3588c2ecf20Sopenharmony_cistatic void dfl_fme_destroy_regions(struct dfl_feature_platform_data *pdata) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci struct dfl_fme *priv = dfl_fpga_pdata_get_private(pdata); 3618c2ecf20Sopenharmony_ci struct dfl_fme_region *fme_region, *tmp; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci list_for_each_entry_safe(fme_region, tmp, &priv->region_list, node) { 3648c2ecf20Sopenharmony_ci list_del(&fme_region->node); 3658c2ecf20Sopenharmony_ci dfl_fme_destroy_region(fme_region); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int pr_mgmt_init(struct platform_device *pdev, 3708c2ecf20Sopenharmony_ci struct dfl_feature *feature) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 3738c2ecf20Sopenharmony_ci struct dfl_fme_region *fme_region; 3748c2ecf20Sopenharmony_ci struct dfl_fme_bridge *fme_br; 3758c2ecf20Sopenharmony_ci struct platform_device *mgr; 3768c2ecf20Sopenharmony_ci struct dfl_fme *priv; 3778c2ecf20Sopenharmony_ci void __iomem *fme_hdr; 3788c2ecf20Sopenharmony_ci int ret = -ENODEV, i = 0; 3798c2ecf20Sopenharmony_ci u64 fme_cap, port_offset; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci fme_hdr = dfl_get_feature_ioaddr_by_id(&pdev->dev, 3828c2ecf20Sopenharmony_ci FME_FEATURE_ID_HEADER); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci mutex_lock(&pdata->lock); 3858c2ecf20Sopenharmony_ci priv = dfl_fpga_pdata_get_private(pdata); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Initialize the region and bridge sub device list */ 3888c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->region_list); 3898c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&priv->bridge_list); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci /* Create fpga mgr platform device */ 3928c2ecf20Sopenharmony_ci mgr = dfl_fme_create_mgr(pdata, feature); 3938c2ecf20Sopenharmony_ci if (IS_ERR(mgr)) { 3948c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "fail to create fpga mgr pdev\n"); 3958c2ecf20Sopenharmony_ci goto unlock; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci priv->mgr = mgr; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* Read capability register to check number of regions and bridges */ 4018c2ecf20Sopenharmony_ci fme_cap = readq(fme_hdr + FME_HDR_CAP); 4028c2ecf20Sopenharmony_ci for (; i < FIELD_GET(FME_CAP_NUM_PORTS, fme_cap); i++) { 4038c2ecf20Sopenharmony_ci port_offset = readq(fme_hdr + FME_HDR_PORT_OFST(i)); 4048c2ecf20Sopenharmony_ci if (!(port_offset & FME_PORT_OFST_IMP)) 4058c2ecf20Sopenharmony_ci continue; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* Create bridge for each port */ 4088c2ecf20Sopenharmony_ci fme_br = dfl_fme_create_bridge(pdata, i); 4098c2ecf20Sopenharmony_ci if (IS_ERR(fme_br)) { 4108c2ecf20Sopenharmony_ci ret = PTR_ERR(fme_br); 4118c2ecf20Sopenharmony_ci goto destroy_region; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci list_add(&fme_br->node, &priv->bridge_list); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci /* Create region for each port */ 4178c2ecf20Sopenharmony_ci fme_region = dfl_fme_create_region(pdata, mgr, 4188c2ecf20Sopenharmony_ci fme_br->br, i); 4198c2ecf20Sopenharmony_ci if (IS_ERR(fme_region)) { 4208c2ecf20Sopenharmony_ci ret = PTR_ERR(fme_region); 4218c2ecf20Sopenharmony_ci goto destroy_region; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci list_add(&fme_region->node, &priv->region_list); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_cidestroy_region: 4318c2ecf20Sopenharmony_ci dfl_fme_destroy_regions(pdata); 4328c2ecf20Sopenharmony_ci dfl_fme_destroy_bridges(pdata); 4338c2ecf20Sopenharmony_ci dfl_fme_destroy_mgr(pdata); 4348c2ecf20Sopenharmony_ciunlock: 4358c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 4368c2ecf20Sopenharmony_ci return ret; 4378c2ecf20Sopenharmony_ci} 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic void pr_mgmt_uinit(struct platform_device *pdev, 4408c2ecf20Sopenharmony_ci struct dfl_feature *feature) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci mutex_lock(&pdata->lock); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci dfl_fme_destroy_regions(pdata); 4478c2ecf20Sopenharmony_ci dfl_fme_destroy_bridges(pdata); 4488c2ecf20Sopenharmony_ci dfl_fme_destroy_mgr(pdata); 4498c2ecf20Sopenharmony_ci mutex_unlock(&pdata->lock); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic long fme_pr_ioctl(struct platform_device *pdev, 4538c2ecf20Sopenharmony_ci struct dfl_feature *feature, 4548c2ecf20Sopenharmony_ci unsigned int cmd, unsigned long arg) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci long ret; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci switch (cmd) { 4598c2ecf20Sopenharmony_ci case DFL_FPGA_FME_PORT_PR: 4608c2ecf20Sopenharmony_ci ret = fme_pr(pdev, arg); 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci default: 4638c2ecf20Sopenharmony_ci ret = -ENODEV; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ciconst struct dfl_feature_id fme_pr_mgmt_id_table[] = { 4708c2ecf20Sopenharmony_ci {.id = FME_FEATURE_ID_PR_MGMT,}, 4718c2ecf20Sopenharmony_ci {0} 4728c2ecf20Sopenharmony_ci}; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ciconst struct dfl_feature_ops fme_pr_mgmt_ops = { 4758c2ecf20Sopenharmony_ci .init = pr_mgmt_init, 4768c2ecf20Sopenharmony_ci .uinit = pr_mgmt_uinit, 4778c2ecf20Sopenharmony_ci .ioctl = fme_pr_ioctl, 4788c2ecf20Sopenharmony_ci}; 479