aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/apf/xlnk-eng.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/apf/xlnk-eng.c')
-rw-r--r--drivers/staging/apf/xlnk-eng.c242
1 files changed, 242 insertions, 0 deletions
diff --git a/drivers/staging/apf/xlnk-eng.c b/drivers/staging/apf/xlnk-eng.c
new file mode 100644
index 000000000000..bc40128e93cf
--- /dev/null
+++ b/drivers/staging/apf/xlnk-eng.c
@@ -0,0 +1,242 @@
+/*
+ * Xilinx XLNK Engine Driver
+ *
+ * Copyright (C) 2010 Xilinx, Inc. All rights reserved.
+ *
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/spinlock_types.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/uio_driver.h>
+
+
+#include "xlnk-eng.h"
+
+static DEFINE_MUTEX(xlnk_eng_list_mutex);
+static LIST_HEAD(xlnk_eng_list);
+
+int xlnk_eng_register_device(struct xlnk_eng_device *xlnk_dev)
+{
+ mutex_lock(&xlnk_eng_list_mutex);
+ /* todo: need to add more error checking */
+
+ list_add_tail(&xlnk_dev->global_node, &xlnk_eng_list);
+
+ mutex_unlock(&xlnk_eng_list_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(xlnk_eng_register_device);
+
+
+void xlnk_eng_unregister_device(struct xlnk_eng_device *xlnk_dev)
+{
+ mutex_lock(&xlnk_eng_list_mutex);
+ /* todo: need to add more error checking */
+
+ list_del(&xlnk_dev->global_node);
+
+ mutex_unlock(&xlnk_eng_list_mutex);
+}
+EXPORT_SYMBOL(xlnk_eng_unregister_device);
+
+struct xlnk_eng_device *xlnk_eng_request_by_name(char *name)
+{
+ struct xlnk_eng_device *device, *_d;
+ int found = 0;
+
+ mutex_lock(&xlnk_eng_list_mutex);
+
+ list_for_each_entry_safe(device, _d, &xlnk_eng_list, global_node) {
+ if (!strcmp(dev_name(device->dev), name)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ device = device->alloc(device);
+ else
+ device = NULL;
+
+ mutex_unlock(&xlnk_eng_list_mutex);
+
+ return device;
+}
+EXPORT_SYMBOL(xlnk_eng_request_by_name);
+
+/**
+ * struct xilinx_xlnk_eng_device - device structure for xilinx_xlnk_eng
+ * @common: common device info
+ * @base: base address for device
+ * @lock: lock used by device
+ * @cnt: usage count
+ * @info: info for registering and unregistering uio device
+ */
+struct xilinx_xlnk_eng_device {
+ struct xlnk_eng_device common;
+ void __iomem *base;
+ spinlock_t lock;
+ int cnt;
+ struct uio_info *info;
+};
+
+static void xlnk_eng_release(struct device *dev)
+{
+ struct xilinx_xlnk_eng_device *xdev;
+ struct xlnk_eng_device *xlnk_dev;
+
+ xdev = dev_get_drvdata(dev);
+ xlnk_dev = &xdev->common;
+ if (!xlnk_dev)
+ return;
+
+ xlnk_dev->free(xlnk_dev);
+}
+
+#define DRIVER_NAME "xilinx-xlnk-eng"
+
+#define to_xilinx_xlnk(dev) container_of(dev, \
+ struct xilinx_xlnk_eng_device, common)
+
+static struct xlnk_eng_device *xilinx_xlnk_alloc(
+ struct xlnk_eng_device *xlnkdev)
+{
+ struct xilinx_xlnk_eng_device *xdev;
+ struct xlnk_eng_device *retdev;
+
+ xdev = to_xilinx_xlnk(xlnkdev);
+
+ if (xdev->cnt == 0) {
+ xdev->cnt++;
+ retdev = xlnkdev;
+ } else
+ retdev = NULL;
+
+ return retdev;
+}
+
+static void xilinx_xlnk_free(struct xlnk_eng_device *xlnkdev)
+{
+ struct xilinx_xlnk_eng_device *xdev;
+
+ xdev = to_xilinx_xlnk(xlnkdev);
+
+ xdev->cnt = 0;
+}
+
+static int xlnk_eng_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct xilinx_xlnk_eng_device *xdev;
+ struct uio_info *info;
+ char *devname;
+
+ pr_info("xlnk_eng_probe ...\n");
+ xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
+ if (!xdev) {
+ dev_err(&pdev->dev, "Not enough memory for device\n");
+ return -ENOMEM;
+ }
+
+ /* more error handling */
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&pdev->dev, "Not enough memory for device\n");
+ return -ENOMEM;
+ }
+ xdev->info = info;
+ devname = devm_kzalloc(&pdev->dev, 64, GFP_KERNEL);
+ if (!devname) {
+ dev_err(&pdev->dev, "Not enough memory for device\n");
+ return -ENOMEM;
+ }
+ sprintf(devname, "%s.%d", DRIVER_NAME, pdev->id);
+ pr_info("uio name %s\n", devname);
+ /* iomap registers */
+
+ /* Get the data from the platform device */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ xdev->base = devm_ioremap_resource(&pdev->dev, res);
+
+ /* %pa types should be used here */
+ dev_info(&pdev->dev, "physical base : 0x%lx\n",
+ (unsigned long)res->start);
+ dev_info(&pdev->dev, "register range : 0x%lx\n",
+ (unsigned long)resource_size(res));
+ dev_info(&pdev->dev, "base remapped to: 0x%lx\n",
+ (unsigned long)xdev->base);
+ if (!xdev->base) {
+ dev_err(&pdev->dev, "unable to iomap registers\n");
+ return -ENOMEM;
+ }
+
+ info->mem[0].addr = res->start;
+ info->mem[0].size = resource_size(res);
+ info->mem[0].memtype = UIO_MEM_PHYS;
+ info->mem[0].internal_addr = xdev->base;
+
+ /* info->name = DRIVER_NAME; */
+ info->name = devname;
+ info->version = "0.0.1";
+
+ info->irq = -1;
+
+ xdev->common.dev = &pdev->dev;
+
+ xdev->common.alloc = xilinx_xlnk_alloc;
+ xdev->common.free = xilinx_xlnk_free;
+ xdev->common.dev->release = xlnk_eng_release;
+
+ dev_set_drvdata(&pdev->dev, xdev);
+
+ spin_lock_init(&xdev->lock);
+
+ xdev->cnt = 0;
+
+ xlnk_eng_register_device(&xdev->common);
+
+ if (uio_register_device(&pdev->dev, info)) {
+ dev_err(&pdev->dev, "uio_register_device failed\n");
+ return -ENODEV;
+ }
+ dev_info(&pdev->dev, "xilinx-xlnk-eng uio registered\n");
+
+ return 0;
+}
+
+static int xlnk_eng_remove(struct platform_device *pdev)
+{
+ struct uio_info *info;
+ struct xilinx_xlnk_eng_device *xdev;
+
+ xdev = dev_get_drvdata(&pdev->dev);
+ info = xdev->info;
+
+ uio_unregister_device(info);
+ dev_info(&pdev->dev, "xilinx-xlnk-eng uio unregistered\n");
+ xlnk_eng_unregister_device(&xdev->common);
+
+ return 0;
+}
+
+static struct platform_driver xlnk_eng_driver = {
+ .probe = xlnk_eng_probe,
+ .remove = xlnk_eng_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRIVER_NAME,
+ },
+};
+
+module_platform_driver(xlnk_eng_driver);
+
+MODULE_DESCRIPTION("Xilinx xlnk engine generic driver");
+MODULE_LICENSE("GPL");