xrt-lib kernel module infrastructure code to add xrt_bus_type, xrt_driver and xrt_device Signed-off-by: Sonal Santan <sonal.santan@xxxxxxxxxx> Signed-off-by: Max Zhen <max.zhen@xxxxxxxxxx> Signed-off-by: Lizhi Hou <lizhi.hou@xxxxxxxxxx> --- drivers/fpga/xrt/lib/lib-drv.c | 192 +++++++++++++++++++++++++++++++++ include/linux/xrt/xdevice.h | 128 ++++++++++++++++++++++ 2 files changed, 320 insertions(+) create mode 100644 include/linux/xrt/xdevice.h diff --git a/drivers/fpga/xrt/lib/lib-drv.c b/drivers/fpga/xrt/lib/lib-drv.c index d4597cd4767f..3ad02a7c2aac 100644 --- a/drivers/fpga/xrt/lib/lib-drv.c +++ b/drivers/fpga/xrt/lib/lib-drv.c @@ -11,10 +11,195 @@ #include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/of_device.h> +#include <linux/xrt/xdevice.h> #include "lib-drv.h" static int xrt_bus_ovcs_id; +#define XRT_DRVNAME(drv) ((drv)->driver.name) + +static DEFINE_IDA(xrt_device_ida); + +static int xrt_bus_match(struct device *dev, struct device_driver *drv) +{ + if (of_driver_match_device(dev, drv)) + return 1; + + return 0; +} + +static int xrt_bus_probe(struct device *dev) +{ + struct xrt_driver *xdrv = to_xrt_drv(dev->driver); + struct xrt_device *xdev = to_xrt_dev(dev); + + return xdrv->probe(xdev); +} + +static void xrt_bus_remove(struct device *dev) +{ + struct xrt_driver *xdrv = to_xrt_drv(dev->driver); + struct xrt_device *xdev = to_xrt_dev(dev); + + if (xdrv->remove) + xdrv->remove(xdev); +} + +struct bus_type xrt_bus_type = { + .name = "xrt", + .match = xrt_bus_match, + .probe = xrt_bus_probe, + .remove = xrt_bus_remove, +}; + +int xrt_register_driver(struct xrt_driver *drv) +{ + const char *drvname = XRT_DRVNAME(drv); + int rc = 0; + + /* Initialize dev_t for char dev node. */ + if (drv->file_ops.xsf_ops.open) { + rc = alloc_chrdev_region(&drv->file_ops.xsf_dev_t, 0, + XRT_MAX_DEVICE_NODES, drvname); + if (rc) { + pr_err("failed to alloc dev minor for %s: %d\n", drvname, rc); + return rc; + } + } else { + drv->file_ops.xsf_dev_t = (dev_t)-1; + } + + drv->driver.bus = &xrt_bus_type; + + rc = driver_register(&drv->driver); + if (rc) { + pr_err("register %s xrt driver failed\n", drvname); + if (drv->file_ops.xsf_dev_t != (dev_t)-1) { + unregister_chrdev_region(drv->file_ops.xsf_dev_t, + XRT_MAX_DEVICE_NODES); + } + return rc; + } + + pr_info("%s registered successfully\n", drvname); + + return 0; +} +EXPORT_SYMBOL_GPL(xrt_register_driver); + +void xrt_unregister_driver(struct xrt_driver *drv) +{ + driver_unregister(&drv->driver); + + if (drv->file_ops.xsf_dev_t != (dev_t)-1) + unregister_chrdev_region(drv->file_ops.xsf_dev_t, XRT_MAX_DEVICE_NODES); + + pr_info("%s unregistered successfully\n", XRT_DRVNAME(drv)); +} +EXPORT_SYMBOL_GPL(xrt_unregister_driver); + +static int xrt_dev_get_instance(void) +{ + int ret; + + ret = ida_alloc_range(&xrt_device_ida, 0, 0x7fffffff, GFP_KERNEL); + if (ret < 0) + return ret; + + return ret; +} + +static void xrt_dev_put_instance(int instance) +{ + ida_free(&xrt_device_ida, instance); +} + +static void xrt_device_release(struct device *dev) +{ + struct xrt_device *xdev = container_of(dev, struct xrt_device, dev); + + kfree(xdev); +} + +void xrt_device_unregister(struct xrt_device *xdev) +{ + if (xdev->state == XRT_DEVICE_STATE_ADDED) + device_del(&xdev->dev); + + vfree(xdev->sdev_data); + kfree(xdev->resource); + + if (xdev->instance != XRT_INVALID_DEVICE_INST) + xrt_dev_put_instance(xdev->instance); + + if (xdev->dev.of_node) + of_node_put(xdev->dev.of_node); + + if (xdev->dev.release == xrt_device_release) + put_device(&xdev->dev); +} + +struct xrt_device * +xrt_device_register(struct device *parent, struct device_node *dn, + struct resource *res, u32 res_num, + void *pdata, size_t data_sz) +{ + struct xrt_device *xdev = NULL; + int ret; + + xdev = kzalloc(sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return NULL; + xdev->instance = XRT_INVALID_DEVICE_INST; + + /* Obtain dev instance number. */ + ret = xrt_dev_get_instance(); + if (ret < 0) { + dev_err(parent, "failed get instance, ret %d", ret); + goto fail; + } + + xdev->instance = ret; + xdev->name = dn->full_name; + device_initialize(&xdev->dev); + xdev->dev.release = xrt_device_release; + xdev->dev.parent = parent; + + xdev->dev.bus = &xrt_bus_type; + dev_set_name(&xdev->dev, "%s.%d", xdev->name, xdev->instance); + + if (res_num > 0) { + xdev->num_resources = res_num; + xdev->resource = kmemdup(res, sizeof(*res) * res_num, GFP_KERNEL); + if (!xdev->resource) + goto fail; + } + + if (data_sz > 0) { + xdev->sdev_data = vzalloc(data_sz); + if (!xdev->sdev_data) + goto fail; + + memcpy(xdev->sdev_data, pdata, data_sz); + } + + ret = device_add(&xdev->dev); + if (ret) { + dev_err(parent, "failed add device, ret %d", ret); + goto fail; + } + xdev->state = XRT_DEVICE_STATE_ADDED; + xdev->dev.of_node = of_node_get(dn); + + return xdev; + +fail: + xrt_device_unregister(xdev); + kfree(xdev); + + return NULL; +} + static __init int xrt_lib_init(void) { int ret; @@ -25,11 +210,18 @@ static __init int xrt_lib_init(void) if (ret) return ret; + ret = bus_register(&xrt_bus_type); + if (ret) { + of_overlay_remove(&xrt_bus_ovcs_id); + return ret; + } + return 0; } static __exit void xrt_lib_fini(void) { + bus_unregister(&xrt_bus_type); of_overlay_remove(&xrt_bus_ovcs_id); } diff --git a/include/linux/xrt/xdevice.h b/include/linux/xrt/xdevice.h new file mode 100644 index 000000000000..0b3fec9ae598 --- /dev/null +++ b/include/linux/xrt/xdevice.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020-2021 Xilinx, Inc. + * + * Authors: + * Lizhi Hou <lizhi.hou@xxxxxxxxxx> + */ + +#ifndef _XRT_DEVICE_H_ +#define _XRT_DEVICE_H_ + +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/of.h> + +#define XRT_MAX_DEVICE_NODES 128 +#define XRT_INVALID_DEVICE_INST (XRT_MAX_DEVICE_NODES + 1) + +enum { + XRT_DEVICE_STATE_NONE = 0, + XRT_DEVICE_STATE_ADDED +}; + +/* + * struct xrt_device - represent an xrt device on xrt bus + * + * dev: generic device interface. + * id: id of the xrt device. + */ +struct xrt_device { + struct device dev; + const char *name; + u32 instance; + u32 state; + u32 num_resources; + struct resource *resource; + void *sdev_data; +}; + +/* + * If populated by xrt device driver, infra will handle the mechanics of + * char device (un)registration. + */ +enum xrt_dev_file_mode { + /* Infra create cdev, default file name */ + XRT_DEV_FILE_DEFAULT = 0, + /* Infra create cdev, need to encode inst num in file name */ + XRT_DEV_FILE_MULTI_INST, + /* No auto creation of cdev by infra, leaf handles it by itself */ + XRT_DEV_FILE_NO_AUTO, +}; + +struct xrt_dev_file_ops { + const struct file_operations xsf_ops; + dev_t xsf_dev_t; + const char *xsf_dev_name; + enum xrt_dev_file_mode xsf_mode; +}; + +/* + * this struct define the endpoints belong to the same xrt device + */ +struct xrt_dev_ep_names { + const char *ep_name; + const char *compat; +}; + +/* + * struct xrt_driver - represent a xrt device driver + * + * driver: driver model structure. + * id_table: pointer to table of device IDs the driver is interested in. + * { } member terminated. + * probe: mandatory callback for device binding. + * remove: callback for device unbinding. + */ +struct xrt_driver { + struct device_driver driver; + struct xrt_dev_file_ops file_ops; + + /* + * Subdev driver callbacks populated by subdev driver. + */ + int (*probe)(struct xrt_device *xrt_dev); + void (*remove)(struct xrt_device *xrt_dev); + /* + * If leaf_call is defined, these are called by other leaf drivers. + * Note that root driver may call into leaf_call of a group driver. + */ + int (*leaf_call)(struct xrt_device *xrt_dev, u32 cmd, void *arg); +}; + +#define to_xrt_dev(d) container_of(d, struct xrt_device, dev) +#define to_xrt_drv(d) container_of(d, struct xrt_driver, driver) +/* + * module_xrt_driver() - Helper macro for drivers that don't do + * anything special in module init/exit. + */ +#define module_xrt_driver(__xrt_driver) \ + module_driver(__xrt_driver, xrt_register_driver, \ + xrt_unregister_driver) + +static inline void *xrt_get_drvdata(const struct xrt_device *xdev) +{ + return dev_get_drvdata(&xdev->dev); +} + +static inline void xrt_set_drvdata(struct xrt_device *xdev, void *data) +{ + dev_set_drvdata(&xdev->dev, data); +} + +static inline void *xrt_get_xdev_data(struct device *dev) +{ + struct xrt_device *xdev = to_xrt_dev(dev); + + return xdev->sdev_data; +} + +struct xrt_device * +xrt_device_register(struct device *parent, struct device_node *dn, + struct resource *res, u32 res_num, + void *pdata, size_t data_sz); +void xrt_device_unregister(struct xrt_device *xdev); +int xrt_register_driver(struct xrt_driver *drv); +void xrt_unregister_driver(struct xrt_driver *drv); + +#endif /* _XRT_DEVICE_H_ */ -- 2.27.0