123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317 |
- #include <linux/fpga/fpga-bridge.h>
- #include <linux/fpga/fpga-mgr.h>
- #include <linux/fpga/fpga-region.h>
- #include <linux/idr.h>
- #include <linux/kernel.h>
- #include <linux/list.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- static DEFINE_IDA(fpga_region_ida);
- static struct class *fpga_region_class;
- struct fpga_region *
- fpga_region_class_find(struct device *start, const void *data,
- int (*match)(struct device *, const void *))
- {
- struct device *dev;
- dev = class_find_device(fpga_region_class, start, data, match);
- if (!dev)
- return NULL;
- return to_fpga_region(dev);
- }
- EXPORT_SYMBOL_GPL(fpga_region_class_find);
- static struct fpga_region *fpga_region_get(struct fpga_region *region)
- {
- struct device *dev = ®ion->dev;
- if (!mutex_trylock(®ion->mutex)) {
- dev_dbg(dev, "%s: FPGA Region already in use\n", __func__);
- return ERR_PTR(-EBUSY);
- }
- get_device(dev);
- if (!try_module_get(dev->parent->driver->owner)) {
- put_device(dev);
- mutex_unlock(®ion->mutex);
- return ERR_PTR(-ENODEV);
- }
- dev_dbg(dev, "get\n");
- return region;
- }
- static void fpga_region_put(struct fpga_region *region)
- {
- struct device *dev = ®ion->dev;
- dev_dbg(dev, "put\n");
- module_put(dev->parent->driver->owner);
- put_device(dev);
- mutex_unlock(®ion->mutex);
- }
- int fpga_region_program_fpga(struct fpga_region *region)
- {
- struct device *dev = ®ion->dev;
- struct fpga_image_info *info = region->info;
- int ret;
- region = fpga_region_get(region);
- if (IS_ERR(region)) {
- dev_err(dev, "failed to get FPGA region\n");
- return PTR_ERR(region);
- }
- ret = fpga_mgr_lock(region->mgr);
- if (ret) {
- dev_err(dev, "FPGA manager is busy\n");
- goto err_put_region;
- }
-
- if (region->get_bridges) {
- ret = region->get_bridges(region);
- if (ret) {
- dev_err(dev, "failed to get fpga region bridges\n");
- goto err_unlock_mgr;
- }
- }
- ret = fpga_bridges_disable(®ion->bridge_list);
- if (ret) {
- dev_err(dev, "failed to disable bridges\n");
- goto err_put_br;
- }
- ret = fpga_mgr_load(region->mgr, info);
- if (ret) {
- dev_err(dev, "failed to load FPGA image\n");
- goto err_put_br;
- }
- ret = fpga_bridges_enable(®ion->bridge_list);
- if (ret) {
- dev_err(dev, "failed to enable region bridges\n");
- goto err_put_br;
- }
- fpga_mgr_unlock(region->mgr);
- fpga_region_put(region);
- return 0;
- err_put_br:
- if (region->get_bridges)
- fpga_bridges_put(®ion->bridge_list);
- err_unlock_mgr:
- fpga_mgr_unlock(region->mgr);
- err_put_region:
- fpga_region_put(region);
- return ret;
- }
- EXPORT_SYMBOL_GPL(fpga_region_program_fpga);
- static ssize_t compat_id_show(struct device *dev,
- struct device_attribute *attr, char *buf)
- {
- struct fpga_region *region = to_fpga_region(dev);
- if (!region->compat_id)
- return -ENOENT;
- return sprintf(buf, "%016llx%016llx\n",
- (unsigned long long)region->compat_id->id_h,
- (unsigned long long)region->compat_id->id_l);
- }
- static DEVICE_ATTR_RO(compat_id);
- static struct attribute *fpga_region_attrs[] = {
- &dev_attr_compat_id.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(fpga_region);
- struct fpga_region *
- fpga_region_register_full(struct device *parent, const struct fpga_region_info *info)
- {
- struct fpga_region *region;
- int id, ret = 0;
- if (!info) {
- dev_err(parent,
- "Attempt to register without required info structure\n");
- return ERR_PTR(-EINVAL);
- }
- region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return ERR_PTR(-ENOMEM);
- id = ida_alloc(&fpga_region_ida, GFP_KERNEL);
- if (id < 0) {
- ret = id;
- goto err_free;
- }
- region->mgr = info->mgr;
- region->compat_id = info->compat_id;
- region->priv = info->priv;
- region->get_bridges = info->get_bridges;
- mutex_init(®ion->mutex);
- INIT_LIST_HEAD(®ion->bridge_list);
- region->dev.class = fpga_region_class;
- region->dev.parent = parent;
- region->dev.of_node = parent->of_node;
- region->dev.id = id;
- ret = dev_set_name(®ion->dev, "region%d", id);
- if (ret)
- goto err_remove;
- ret = device_register(®ion->dev);
- if (ret) {
- put_device(®ion->dev);
- return ERR_PTR(ret);
- }
- return region;
- err_remove:
- ida_free(&fpga_region_ida, id);
- err_free:
- kfree(region);
- return ERR_PTR(ret);
- }
- EXPORT_SYMBOL_GPL(fpga_region_register_full);
- struct fpga_region *
- fpga_region_register(struct device *parent, struct fpga_manager *mgr,
- int (*get_bridges)(struct fpga_region *))
- {
- struct fpga_region_info info = { 0 };
- info.mgr = mgr;
- info.get_bridges = get_bridges;
- return fpga_region_register_full(parent, &info);
- }
- EXPORT_SYMBOL_GPL(fpga_region_register);
- void fpga_region_unregister(struct fpga_region *region)
- {
- device_unregister(®ion->dev);
- }
- EXPORT_SYMBOL_GPL(fpga_region_unregister);
- static void fpga_region_dev_release(struct device *dev)
- {
- struct fpga_region *region = to_fpga_region(dev);
- ida_free(&fpga_region_ida, region->dev.id);
- kfree(region);
- }
- static int __init fpga_region_init(void)
- {
- fpga_region_class = class_create(THIS_MODULE, "fpga_region");
- if (IS_ERR(fpga_region_class))
- return PTR_ERR(fpga_region_class);
- fpga_region_class->dev_groups = fpga_region_groups;
- fpga_region_class->dev_release = fpga_region_dev_release;
- return 0;
- }
- static void __exit fpga_region_exit(void)
- {
- class_destroy(fpga_region_class);
- ida_destroy(&fpga_region_ida);
- }
- subsys_initcall(fpga_region_init);
- module_exit(fpga_region_exit);
- MODULE_DESCRIPTION("FPGA Region");
- MODULE_AUTHOR("Alan Tull <atull@kernel.org>");
- MODULE_LICENSE("GPL v2");
|