1473f01f7SAlan Tull // SPDX-License-Identifier: GPL-2.0
20fa20cdfSAlan Tull /*
3c71e8050SMoritz Fischer * FPGA Region - Support for FPGA programming under Linux
40fa20cdfSAlan Tull *
50fa20cdfSAlan Tull * Copyright (C) 2013-2016 Altera Corporation
6ef3acdd8SAlan Tull * Copyright (C) 2017 Intel Corporation
70fa20cdfSAlan Tull */
80fa20cdfSAlan Tull #include <linux/fpga/fpga-bridge.h>
90fa20cdfSAlan Tull #include <linux/fpga/fpga-mgr.h>
1059460a93SAlan Tull #include <linux/fpga/fpga-region.h>
110fa20cdfSAlan Tull #include <linux/idr.h>
120fa20cdfSAlan Tull #include <linux/kernel.h>
130fa20cdfSAlan Tull #include <linux/list.h>
140fa20cdfSAlan Tull #include <linux/module.h>
150fa20cdfSAlan Tull #include <linux/slab.h>
160fa20cdfSAlan Tull #include <linux/spinlock.h>
170fa20cdfSAlan Tull
180fa20cdfSAlan Tull static DEFINE_IDA(fpga_region_ida);
191a22ec09SIvan Orlov static const struct class fpga_region_class;
200fa20cdfSAlan Tull
2157ce2e40SNava kishore Manne struct fpga_region *
fpga_region_class_find(struct device * start,const void * data,int (* match)(struct device *,const void *))2257ce2e40SNava kishore Manne fpga_region_class_find(struct device *start, const void *data,
23503d4b7aSAlan Tull int (*match)(struct device *, const void *))
24503d4b7aSAlan Tull {
25503d4b7aSAlan Tull struct device *dev;
26503d4b7aSAlan Tull
271a22ec09SIvan Orlov dev = class_find_device(&fpga_region_class, start, data, match);
28503d4b7aSAlan Tull if (!dev)
29503d4b7aSAlan Tull return NULL;
30503d4b7aSAlan Tull
31503d4b7aSAlan Tull return to_fpga_region(dev);
32503d4b7aSAlan Tull }
33503d4b7aSAlan Tull EXPORT_SYMBOL_GPL(fpga_region_class_find);
34503d4b7aSAlan Tull
350fa20cdfSAlan Tull /**
36011c49e3STom Rix * fpga_region_get - get an exclusive reference to an fpga region
370fa20cdfSAlan Tull * @region: FPGA Region struct
380fa20cdfSAlan Tull *
390fa20cdfSAlan Tull * Caller should call fpga_region_put() when done with region.
400fa20cdfSAlan Tull *
41baa57b33SMarco Pagani * Return:
42baa57b33SMarco Pagani * * fpga_region struct if successful.
43baa57b33SMarco Pagani * * -EBUSY if someone already has a reference to the region.
44baa57b33SMarco Pagani * * -ENODEV if can't take parent driver module refcount.
450fa20cdfSAlan Tull */
fpga_region_get(struct fpga_region * region)460fa20cdfSAlan Tull static struct fpga_region *fpga_region_get(struct fpga_region *region)
470fa20cdfSAlan Tull {
480fa20cdfSAlan Tull struct device *dev = ®ion->dev;
490fa20cdfSAlan Tull
500fa20cdfSAlan Tull if (!mutex_trylock(®ion->mutex)) {
510fa20cdfSAlan Tull dev_dbg(dev, "%s: FPGA Region already in use\n", __func__);
520fa20cdfSAlan Tull return ERR_PTR(-EBUSY);
530fa20cdfSAlan Tull }
540fa20cdfSAlan Tull
550fa20cdfSAlan Tull get_device(dev);
56*4d7d12b6SMarco Pagani if (!try_module_get(region->ops_owner)) {
570fa20cdfSAlan Tull put_device(dev);
580fa20cdfSAlan Tull mutex_unlock(®ion->mutex);
590fa20cdfSAlan Tull return ERR_PTR(-ENODEV);
600fa20cdfSAlan Tull }
610fa20cdfSAlan Tull
62c3d971adSAlan Tull dev_dbg(dev, "get\n");
630fa20cdfSAlan Tull
640fa20cdfSAlan Tull return region;
650fa20cdfSAlan Tull }
660fa20cdfSAlan Tull
670fa20cdfSAlan Tull /**
680fa20cdfSAlan Tull * fpga_region_put - release a reference to a region
690fa20cdfSAlan Tull *
700fa20cdfSAlan Tull * @region: FPGA region
710fa20cdfSAlan Tull */
fpga_region_put(struct fpga_region * region)720fa20cdfSAlan Tull static void fpga_region_put(struct fpga_region *region)
730fa20cdfSAlan Tull {
740fa20cdfSAlan Tull struct device *dev = ®ion->dev;
750fa20cdfSAlan Tull
76c3d971adSAlan Tull dev_dbg(dev, "put\n");
770fa20cdfSAlan Tull
78*4d7d12b6SMarco Pagani module_put(region->ops_owner);
790fa20cdfSAlan Tull put_device(dev);
800fa20cdfSAlan Tull mutex_unlock(®ion->mutex);
810fa20cdfSAlan Tull }
820fa20cdfSAlan Tull
830fa20cdfSAlan Tull /**
840fa20cdfSAlan Tull * fpga_region_program_fpga - program FPGA
85917a4304SAlan Tull *
860fa20cdfSAlan Tull * @region: FPGA region
87917a4304SAlan Tull *
8861c32102SAlan Tull * Program an FPGA using fpga image info (region->info).
89093a89d4SAlan Tull * If the region has a get_bridges function, the exclusive reference for the
90093a89d4SAlan Tull * bridges will be held if programming succeeds. This is intended to prevent
91093a89d4SAlan Tull * reprogramming the region until the caller considers it safe to do so.
92093a89d4SAlan Tull * The caller will need to call fpga_bridges_put() before attempting to
93093a89d4SAlan Tull * reprogram the region.
94917a4304SAlan Tull *
95baa57b33SMarco Pagani * Return: 0 for success or negative error code.
960fa20cdfSAlan Tull */
fpga_region_program_fpga(struct fpga_region * region)9759460a93SAlan Tull int fpga_region_program_fpga(struct fpga_region *region)
980fa20cdfSAlan Tull {
99ebf877a5SAlan Tull struct device *dev = ®ion->dev;
10061c32102SAlan Tull struct fpga_image_info *info = region->info;
1010fa20cdfSAlan Tull int ret;
1020fa20cdfSAlan Tull
1030fa20cdfSAlan Tull region = fpga_region_get(region);
1040fa20cdfSAlan Tull if (IS_ERR(region)) {
105c3d971adSAlan Tull dev_err(dev, "failed to get FPGA region\n");
1060fa20cdfSAlan Tull return PTR_ERR(region);
1070fa20cdfSAlan Tull }
1080fa20cdfSAlan Tull
1091df2dd7fSAlan Tull ret = fpga_mgr_lock(region->mgr);
110ebf877a5SAlan Tull if (ret) {
111ebf877a5SAlan Tull dev_err(dev, "FPGA manager is busy\n");
1121df2dd7fSAlan Tull goto err_put_region;
113ebf877a5SAlan Tull }
114ebf877a5SAlan Tull
11552a3a7ccSAlan Tull /*
11652a3a7ccSAlan Tull * In some cases, we already have a list of bridges in the
11752a3a7ccSAlan Tull * fpga region struct. Or we don't have any bridges.
11852a3a7ccSAlan Tull */
11952a3a7ccSAlan Tull if (region->get_bridges) {
12052a3a7ccSAlan Tull ret = region->get_bridges(region);
1210fa20cdfSAlan Tull if (ret) {
12252a3a7ccSAlan Tull dev_err(dev, "failed to get fpga region bridges\n");
123ebf877a5SAlan Tull goto err_unlock_mgr;
1240fa20cdfSAlan Tull }
12552a3a7ccSAlan Tull }
1260fa20cdfSAlan Tull
1270fa20cdfSAlan Tull ret = fpga_bridges_disable(®ion->bridge_list);
1280fa20cdfSAlan Tull if (ret) {
129c3d971adSAlan Tull dev_err(dev, "failed to disable bridges\n");
1300fa20cdfSAlan Tull goto err_put_br;
1310fa20cdfSAlan Tull }
1320fa20cdfSAlan Tull
13361c32102SAlan Tull ret = fpga_mgr_load(region->mgr, info);
1340fa20cdfSAlan Tull if (ret) {
135c3d971adSAlan Tull dev_err(dev, "failed to load FPGA image\n");
1360fa20cdfSAlan Tull goto err_put_br;
1370fa20cdfSAlan Tull }
1380fa20cdfSAlan Tull
1390fa20cdfSAlan Tull ret = fpga_bridges_enable(®ion->bridge_list);
1400fa20cdfSAlan Tull if (ret) {
141c3d971adSAlan Tull dev_err(dev, "failed to enable region bridges\n");
1420fa20cdfSAlan Tull goto err_put_br;
1430fa20cdfSAlan Tull }
1440fa20cdfSAlan Tull
1451df2dd7fSAlan Tull fpga_mgr_unlock(region->mgr);
1460fa20cdfSAlan Tull fpga_region_put(region);
1470fa20cdfSAlan Tull
1480fa20cdfSAlan Tull return 0;
1490fa20cdfSAlan Tull
1500fa20cdfSAlan Tull err_put_br:
15152a3a7ccSAlan Tull if (region->get_bridges)
1520fa20cdfSAlan Tull fpga_bridges_put(®ion->bridge_list);
153ebf877a5SAlan Tull err_unlock_mgr:
1541df2dd7fSAlan Tull fpga_mgr_unlock(region->mgr);
155e73bbf64STobias Klauser err_put_region:
1560fa20cdfSAlan Tull fpga_region_put(region);
1570fa20cdfSAlan Tull
1580fa20cdfSAlan Tull return ret;
1590fa20cdfSAlan Tull }
16059460a93SAlan Tull EXPORT_SYMBOL_GPL(fpga_region_program_fpga);
1610fa20cdfSAlan Tull
compat_id_show(struct device * dev,struct device_attribute * attr,char * buf)16241a8b2c5SWu Hao static ssize_t compat_id_show(struct device *dev,
16341a8b2c5SWu Hao struct device_attribute *attr, char *buf)
16441a8b2c5SWu Hao {
16541a8b2c5SWu Hao struct fpga_region *region = to_fpga_region(dev);
16641a8b2c5SWu Hao
16741a8b2c5SWu Hao if (!region->compat_id)
16841a8b2c5SWu Hao return -ENOENT;
16941a8b2c5SWu Hao
17041a8b2c5SWu Hao return sprintf(buf, "%016llx%016llx\n",
17141a8b2c5SWu Hao (unsigned long long)region->compat_id->id_h,
17241a8b2c5SWu Hao (unsigned long long)region->compat_id->id_l);
17341a8b2c5SWu Hao }
17441a8b2c5SWu Hao
17541a8b2c5SWu Hao static DEVICE_ATTR_RO(compat_id);
17641a8b2c5SWu Hao
17741a8b2c5SWu Hao static struct attribute *fpga_region_attrs[] = {
17841a8b2c5SWu Hao &dev_attr_compat_id.attr,
17941a8b2c5SWu Hao NULL,
18041a8b2c5SWu Hao };
18141a8b2c5SWu Hao ATTRIBUTE_GROUPS(fpga_region);
18241a8b2c5SWu Hao
1839f368977SAlan Tull /**
184*4d7d12b6SMarco Pagani * __fpga_region_register_full - create and register an FPGA Region device
1855e77886dSRuss Weight * @parent: device parent
1868886a579SRuss Weight * @info: parameters for FPGA Region
187*4d7d12b6SMarco Pagani * @owner: module containing the get_bridges function
1889f368977SAlan Tull *
1898886a579SRuss Weight * Return: struct fpga_region or ERR_PTR()
1909f368977SAlan Tull */
1918886a579SRuss Weight struct fpga_region *
__fpga_region_register_full(struct device * parent,const struct fpga_region_info * info,struct module * owner)192*4d7d12b6SMarco Pagani __fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,
193*4d7d12b6SMarco Pagani struct module *owner)
1940fa20cdfSAlan Tull {
1959f368977SAlan Tull struct fpga_region *region;
1960fa20cdfSAlan Tull int id, ret = 0;
1970fa20cdfSAlan Tull
1988886a579SRuss Weight if (!info) {
1998886a579SRuss Weight dev_err(parent,
2008886a579SRuss Weight "Attempt to register without required info structure\n");
2018886a579SRuss Weight return ERR_PTR(-EINVAL);
2028886a579SRuss Weight }
2038886a579SRuss Weight
2049f368977SAlan Tull region = kzalloc(sizeof(*region), GFP_KERNEL);
2059f368977SAlan Tull if (!region)
2068886a579SRuss Weight return ERR_PTR(-ENOMEM);
2079f368977SAlan Tull
208a5e3d775Skeliu id = ida_alloc(&fpga_region_ida, GFP_KERNEL);
2098886a579SRuss Weight if (id < 0) {
2108886a579SRuss Weight ret = id;
2119f368977SAlan Tull goto err_free;
2128886a579SRuss Weight }
2130fa20cdfSAlan Tull
2148886a579SRuss Weight region->mgr = info->mgr;
2158886a579SRuss Weight region->compat_id = info->compat_id;
2168886a579SRuss Weight region->priv = info->priv;
2178886a579SRuss Weight region->get_bridges = info->get_bridges;
218*4d7d12b6SMarco Pagani region->ops_owner = owner;
2198886a579SRuss Weight
2200fa20cdfSAlan Tull mutex_init(®ion->mutex);
2210fa20cdfSAlan Tull INIT_LIST_HEAD(®ion->bridge_list);
2229f368977SAlan Tull
2231a22ec09SIvan Orlov region->dev.class = &fpga_region_class;
2245e77886dSRuss Weight region->dev.parent = parent;
2255e77886dSRuss Weight region->dev.of_node = parent->of_node;
2260fa20cdfSAlan Tull region->dev.id = id;
2270fa20cdfSAlan Tull
2280fa20cdfSAlan Tull ret = dev_set_name(®ion->dev, "region%d", id);
2290fa20cdfSAlan Tull if (ret)
2300fa20cdfSAlan Tull goto err_remove;
2310fa20cdfSAlan Tull
2328886a579SRuss Weight ret = device_register(®ion->dev);
2338886a579SRuss Weight if (ret) {
2348886a579SRuss Weight put_device(®ion->dev);
2358886a579SRuss Weight return ERR_PTR(ret);
2368886a579SRuss Weight }
2378886a579SRuss Weight
2389f368977SAlan Tull return region;
23952a3a7ccSAlan Tull
24052a3a7ccSAlan Tull err_remove:
241a5e3d775Skeliu ida_free(&fpga_region_ida, id);
2429f368977SAlan Tull err_free:
2439f368977SAlan Tull kfree(region);
2449f368977SAlan Tull
2458886a579SRuss Weight return ERR_PTR(ret);
2469f368977SAlan Tull }
247*4d7d12b6SMarco Pagani EXPORT_SYMBOL_GPL(__fpga_region_register_full);
2489f368977SAlan Tull
2499f368977SAlan Tull /**
250*4d7d12b6SMarco Pagani * __fpga_region_register - create and register an FPGA Region device
2515e77886dSRuss Weight * @parent: device parent
252fea82b7fSAlan Tull * @mgr: manager that programs this region
253fea82b7fSAlan Tull * @get_bridges: optional function to get bridges to a list
254*4d7d12b6SMarco Pagani * @owner: module containing the get_bridges function
255fea82b7fSAlan Tull *
2568886a579SRuss Weight * This simple version of the register function should be sufficient for most users.
2578886a579SRuss Weight * The fpga_region_register_full() function is available for users that need to
2588886a579SRuss Weight * pass additional, optional parameters.
259fea82b7fSAlan Tull *
2608886a579SRuss Weight * Return: struct fpga_region or ERR_PTR()
261fea82b7fSAlan Tull */
2628886a579SRuss Weight struct fpga_region *
__fpga_region_register(struct device * parent,struct fpga_manager * mgr,int (* get_bridges)(struct fpga_region *),struct module * owner)263*4d7d12b6SMarco Pagani __fpga_region_register(struct device *parent, struct fpga_manager *mgr,
264*4d7d12b6SMarco Pagani int (*get_bridges)(struct fpga_region *), struct module *owner)
265fea82b7fSAlan Tull {
2668886a579SRuss Weight struct fpga_region_info info = { 0 };
267fea82b7fSAlan Tull
2688886a579SRuss Weight info.mgr = mgr;
2698886a579SRuss Weight info.get_bridges = get_bridges;
270fea82b7fSAlan Tull
271*4d7d12b6SMarco Pagani return __fpga_region_register_full(parent, &info, owner);
27252a3a7ccSAlan Tull }
273*4d7d12b6SMarco Pagani EXPORT_SYMBOL_GPL(__fpga_region_register);
27452a3a7ccSAlan Tull
275917a4304SAlan Tull /**
276011c49e3STom Rix * fpga_region_unregister - unregister an FPGA region
2779f368977SAlan Tull * @region: FPGA region
278fea82b7fSAlan Tull *
279011c49e3STom Rix * This function is intended for use in an FPGA region driver's remove function.
2809f368977SAlan Tull */
fpga_region_unregister(struct fpga_region * region)2819f368977SAlan Tull void fpga_region_unregister(struct fpga_region *region)
28252a3a7ccSAlan Tull {
28352a3a7ccSAlan Tull device_unregister(®ion->dev);
28452a3a7ccSAlan Tull }
28552a3a7ccSAlan Tull EXPORT_SYMBOL_GPL(fpga_region_unregister);
28652a3a7ccSAlan Tull
fpga_region_dev_release(struct device * dev)2870fa20cdfSAlan Tull static void fpga_region_dev_release(struct device *dev)
2880fa20cdfSAlan Tull {
2898886a579SRuss Weight struct fpga_region *region = to_fpga_region(dev);
2908886a579SRuss Weight
291a5e3d775Skeliu ida_free(&fpga_region_ida, region->dev.id);
2928886a579SRuss Weight kfree(region);
2930fa20cdfSAlan Tull }
2940fa20cdfSAlan Tull
2951a22ec09SIvan Orlov static const struct class fpga_region_class = {
2961a22ec09SIvan Orlov .name = "fpga_region",
2971a22ec09SIvan Orlov .dev_groups = fpga_region_groups,
2981a22ec09SIvan Orlov .dev_release = fpga_region_dev_release,
2991a22ec09SIvan Orlov };
3001a22ec09SIvan Orlov
3010fa20cdfSAlan Tull /**
302baa57b33SMarco Pagani * fpga_region_init - creates the fpga_region class.
303baa57b33SMarco Pagani *
304baa57b33SMarco Pagani * Return: 0 on success or ERR_PTR() on error.
3050fa20cdfSAlan Tull */
fpga_region_init(void)3060fa20cdfSAlan Tull static int __init fpga_region_init(void)
3070fa20cdfSAlan Tull {
3081a22ec09SIvan Orlov return class_register(&fpga_region_class);
3090fa20cdfSAlan Tull }
3100fa20cdfSAlan Tull
fpga_region_exit(void)3110fa20cdfSAlan Tull static void __exit fpga_region_exit(void)
3120fa20cdfSAlan Tull {
3131a22ec09SIvan Orlov class_unregister(&fpga_region_class);
3140fa20cdfSAlan Tull ida_destroy(&fpga_region_ida);
3150fa20cdfSAlan Tull }
3160fa20cdfSAlan Tull
3170fa20cdfSAlan Tull subsys_initcall(fpga_region_init);
3180fa20cdfSAlan Tull module_exit(fpga_region_exit);
3190fa20cdfSAlan Tull
3200fa20cdfSAlan Tull MODULE_DESCRIPTION("FPGA Region");
32159460a93SAlan Tull MODULE_AUTHOR("Alan Tull <atull@kernel.org>");
3220fa20cdfSAlan Tull MODULE_LICENSE("GPL v2");
323