brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · 753cd14 Raw
323 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * FPGA Region - Support for FPGA programming under Linux4 *5 *  Copyright (C) 2013-2016 Altera Corporation6 *  Copyright (C) 2017 Intel Corporation7 */8#include <linux/fpga/fpga-bridge.h>9#include <linux/fpga/fpga-mgr.h>10#include <linux/fpga/fpga-region.h>11#include <linux/idr.h>12#include <linux/kernel.h>13#include <linux/list.h>14#include <linux/module.h>15#include <linux/slab.h>16#include <linux/spinlock.h>17 18static DEFINE_IDA(fpga_region_ida);19static const struct class fpga_region_class;20 21struct fpga_region *22fpga_region_class_find(struct device *start, const void *data,23		       int (*match)(struct device *, const void *))24{25	struct device *dev;26 27	dev = class_find_device(&fpga_region_class, start, data, match);28	if (!dev)29		return NULL;30 31	return to_fpga_region(dev);32}33EXPORT_SYMBOL_GPL(fpga_region_class_find);34 35/**36 * fpga_region_get - get an exclusive reference to an fpga region37 * @region: FPGA Region struct38 *39 * Caller should call fpga_region_put() when done with region.40 *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.45 */46static struct fpga_region *fpga_region_get(struct fpga_region *region)47{48	struct device *dev = &region->dev;49 50	if (!mutex_trylock(&region->mutex)) {51		dev_dbg(dev, "%s: FPGA Region already in use\n", __func__);52		return ERR_PTR(-EBUSY);53	}54 55	get_device(dev);56	if (!try_module_get(region->ops_owner)) {57		put_device(dev);58		mutex_unlock(&region->mutex);59		return ERR_PTR(-ENODEV);60	}61 62	dev_dbg(dev, "get\n");63 64	return region;65}66 67/**68 * fpga_region_put - release a reference to a region69 *70 * @region: FPGA region71 */72static void fpga_region_put(struct fpga_region *region)73{74	struct device *dev = &region->dev;75 76	dev_dbg(dev, "put\n");77 78	module_put(region->ops_owner);79	put_device(dev);80	mutex_unlock(&region->mutex);81}82 83/**84 * fpga_region_program_fpga - program FPGA85 *86 * @region: FPGA region87 *88 * Program an FPGA using fpga image info (region->info).89 * If the region has a get_bridges function, the exclusive reference for the90 * bridges will be held if programming succeeds.  This is intended to prevent91 * reprogramming the region until the caller considers it safe to do so.92 * The caller will need to call fpga_bridges_put() before attempting to93 * reprogram the region.94 *95 * Return: 0 for success or negative error code.96 */97int fpga_region_program_fpga(struct fpga_region *region)98{99	struct device *dev = &region->dev;100	struct fpga_image_info *info = region->info;101	int ret;102 103	region = fpga_region_get(region);104	if (IS_ERR(region)) {105		dev_err(dev, "failed to get FPGA region\n");106		return PTR_ERR(region);107	}108 109	ret = fpga_mgr_lock(region->mgr);110	if (ret) {111		dev_err(dev, "FPGA manager is busy\n");112		goto err_put_region;113	}114 115	/*116	 * In some cases, we already have a list of bridges in the117	 * 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		}125	}126 127	ret = fpga_bridges_disable(&region->bridge_list);128	if (ret) {129		dev_err(dev, "failed to disable bridges\n");130		goto err_put_br;131	}132 133	ret = fpga_mgr_load(region->mgr, info);134	if (ret) {135		dev_err(dev, "failed to load FPGA image\n");136		goto err_put_br;137	}138 139	ret = fpga_bridges_enable(&region->bridge_list);140	if (ret) {141		dev_err(dev, "failed to enable region bridges\n");142		goto err_put_br;143	}144 145	fpga_mgr_unlock(region->mgr);146	fpga_region_put(region);147 148	return 0;149 150err_put_br:151	if (region->get_bridges)152		fpga_bridges_put(&region->bridge_list);153err_unlock_mgr:154	fpga_mgr_unlock(region->mgr);155err_put_region:156	fpga_region_put(region);157 158	return ret;159}160EXPORT_SYMBOL_GPL(fpga_region_program_fpga);161 162static 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 175static DEVICE_ATTR_RO(compat_id);176 177static struct attribute *fpga_region_attrs[] = {178	&dev_attr_compat_id.attr,179	NULL,180};181ATTRIBUTE_GROUPS(fpga_region);182 183/**184 * __fpga_region_register_full - create and register an FPGA Region device185 * @parent: device parent186 * @info: parameters for FPGA Region187 * @owner: module containing the get_bridges function188 *189 * Return: struct fpga_region or ERR_PTR()190 */191struct fpga_region *192__fpga_region_register_full(struct device *parent, const struct fpga_region_info *info,193			    struct module *owner)194{195	struct fpga_region *region;196	int id, ret = 0;197 198	if (!info) {199		dev_err(parent,200			"Attempt to register without required info structure\n");201		return ERR_PTR(-EINVAL);202	}203 204	region = kzalloc(sizeof(*region), GFP_KERNEL);205	if (!region)206		return ERR_PTR(-ENOMEM);207 208	id = ida_alloc(&fpga_region_ida, GFP_KERNEL);209	if (id < 0) {210		ret = id;211		goto err_free;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;218	region->ops_owner = owner;219 220	mutex_init(&region->mutex);221	INIT_LIST_HEAD(&region->bridge_list);222 223	region->dev.class = &fpga_region_class;224	region->dev.parent = parent;225	region->dev.of_node = parent->of_node;226	region->dev.id = id;227 228	ret = dev_set_name(&region->dev, "region%d", id);229	if (ret)230		goto err_remove;231 232	ret = device_register(&region->dev);233	if (ret) {234		put_device(&region->dev);235		return ERR_PTR(ret);236	}237 238	return region;239 240err_remove:241	ida_free(&fpga_region_ida, id);242err_free:243	kfree(region);244 245	return ERR_PTR(ret);246}247EXPORT_SYMBOL_GPL(__fpga_region_register_full);248 249/**250 * __fpga_region_register - create and register an FPGA Region device251 * @parent: device parent252 * @mgr: manager that programs this region253 * @get_bridges: optional function to get bridges to a list254 * @owner: module containing the get_bridges function255 *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 to258 * pass additional, optional parameters.259 *260 * Return: struct fpga_region or ERR_PTR()261 */262struct fpga_region *263__fpga_region_register(struct device *parent, struct fpga_manager *mgr,264		       int (*get_bridges)(struct fpga_region *), struct module *owner)265{266	struct fpga_region_info info = { 0 };267 268	info.mgr = mgr;269	info.get_bridges = get_bridges;270 271	return __fpga_region_register_full(parent, &info, owner);272}273EXPORT_SYMBOL_GPL(__fpga_region_register);274 275/**276 * fpga_region_unregister - unregister an FPGA region277 * @region: FPGA region278 *279 * This function is intended for use in an FPGA region driver's remove function.280 */281void fpga_region_unregister(struct fpga_region *region)282{283	device_unregister(&region->dev);284}285EXPORT_SYMBOL_GPL(fpga_region_unregister);286 287static void fpga_region_dev_release(struct device *dev)288{289	struct fpga_region *region = to_fpga_region(dev);290 291	ida_free(&fpga_region_ida, region->dev.id);292	kfree(region);293}294 295static 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 301/**302 * fpga_region_init - creates the fpga_region class.303 *304 * Return: 0 on success or ERR_PTR() on error.305 */306static int __init fpga_region_init(void)307{308	return class_register(&fpga_region_class);309}310 311static void __exit fpga_region_exit(void)312{313	class_unregister(&fpga_region_class);314	ida_destroy(&fpga_region_ida);315}316 317subsys_initcall(fpga_region_init);318module_exit(fpga_region_exit);319 320MODULE_DESCRIPTION("FPGA Region");321MODULE_AUTHOR("Alan Tull <atull@kernel.org>");322MODULE_LICENSE("GPL v2");323