diff mbox

[RFC,4/6] omap: introduce common remoteproc module

Message ID 1278022994-28476-5-git-send-email-ohad@wizery.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ohad Ben Cohen July 1, 2010, 10:23 p.m. UTC
None
diff mbox

Patch

diff --git a/arch/arm/plat-omap/include/plat/remoteproc.h b/arch/arm/plat-omap/include/plat/remoteproc.h
new file mode 100644
index 0000000..1cedd08
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/remoteproc.h
@@ -0,0 +1,75 @@ 
+/*
+ * OMAP Remote Processor driver
+ *
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Written by Ohad Ben-Cohen <ohad@wizery.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef REMOTEPROC_H
+#define REMOTEPROC_H
+
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+
+#define RPROC_IOC_MAGIC		'P'
+
+#define RPROC_IOCSTART		_IOW(RPROC_IOC_MAGIC, 1, int)
+#define RPROC_IOCSTOP		_IO(RPROC_IOC_MAGIC, 2)
+#define RPROC_IOCGETSTATE	_IOR(RPROC_IOC_MAGIC, 3, int)
+
+#define RPROC_IOC_MAXNR		(3)
+
+struct omap_rproc;
+
+struct omap_rproc_ops {
+	int (*start)(struct device *dev, u32 start_addr);
+	int (*stop)(struct device *dev);
+	int (*get_state)(struct device *dev);
+};
+
+struct omap_rproc_clk_t {
+	void *clk_handle;
+	const char *dev_id;
+	const char *con_id;
+};
+
+struct omap_rproc_platform_data {
+	struct omap_rproc_ops *ops;
+	char *name;
+	char *oh_name;
+};
+
+struct omap_rproc {
+	struct device *dev;
+	struct cdev cdev;
+	atomic_t count;
+	int minor;
+};
+
+struct omap_rproc_start_args {
+	u32 start_addr;
+};
+
+struct omap_rproc_platform_data *omap3_get_rproc_data(void);
+int omap3_get_rproc_data_size(void);
+struct omap_rproc_platform_data *omap4_get_rproc_data(void);
+int omap4_get_rproc_data_size(void);
+int omap_get_num_of_remoteproc(void);
+
+#endif /* REMOTEPROC_H */
diff --git a/arch/arm/plat-omap/remoteproc.c b/arch/arm/plat-omap/remoteproc.c
new file mode 100644
index 0000000..7a9862e
--- /dev/null
+++ b/arch/arm/plat-omap/remoteproc.c
@@ -0,0 +1,316 @@ 
+/*
+ * OMAP Remote Processor driver
+ *
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Authors:
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Hari Kanigeri <h-kanigeri2@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/mm.h>
+#include <linux/platform_device.h>
+
+#include <plat/remoteproc.h>
+
+#define OMAP_RPROC_NAME "omap-rproc"
+
+static struct class *omap_rproc_class;
+static dev_t omap_rproc_dev;
+static atomic_t num_of_rprocs;
+
+static inline int rproc_start(struct omap_rproc *rproc, const void __user *arg)
+{
+	struct omap_rproc_platform_data *pdata;
+	struct omap_rproc_start_args start_args;
+
+	if (!rproc->dev)
+		return -EINVAL;
+
+	pdata = rproc->dev->platform_data;
+	if (!pdata->ops)
+		return -EINVAL;
+
+	if (copy_from_user(&start_args, arg, sizeof(start_args)))
+		return -EFAULT;
+
+	return pdata->ops->start(rproc->dev, start_args.start_addr);
+}
+
+static inline int rproc_stop(struct omap_rproc *rproc)
+{
+	struct omap_rproc_platform_data *pdata;
+	if (!rproc->dev)
+		return -EINVAL;
+
+	pdata = rproc->dev->platform_data;
+	if (!pdata->ops)
+		return -EINVAL;
+
+	return pdata->ops->stop(rproc->dev);
+}
+
+static inline int rproc_get_state(struct omap_rproc *rproc)
+{
+	struct omap_rproc_platform_data *pdata;
+	if (!rproc->dev)
+		return -EINVAL;
+
+	pdata = rproc->dev->platform_data;
+	if (!pdata->ops)
+		return -EINVAL;
+
+	return pdata->ops->get_state(rproc->dev);
+}
+
+static int omap_rproc_open(struct inode *inode, struct file *filp)
+{
+	unsigned int count, dev_num = iminor(inode);
+	struct omap_rproc *rproc;
+	struct omap_rproc_platform_data *pdata;
+
+	rproc = container_of(inode->i_cdev, struct omap_rproc, cdev);
+	if (!rproc->dev)
+		return -EINVAL;
+
+	pdata = rproc->dev->platform_data;
+
+	count = atomic_inc_return(&rproc->count);
+	dev_info(rproc->dev, "%s: dev num %d, name %s, count %d\n", __func__,
+							dev_num,
+							pdata->name,
+							count);
+	if (count > 1) {
+		dev_err(rproc->dev, "%s failed: remoteproc already in use\n",
+								__func__);
+		atomic_dec(&rproc->count);
+		return -EBUSY;
+	}
+
+	filp->private_data = rproc;
+
+	return 0;
+}
+
+static int omap_rproc_release(struct inode *inode, struct file *filp)
+{
+	struct omap_rproc_platform_data *pdata;
+	struct omap_rproc *rproc = filp->private_data;
+	if (!rproc || !rproc->dev)
+		return -EINVAL;
+
+	pdata = rproc->dev->platform_data;
+
+	atomic_dec(&rproc->count);
+
+	return 0;
+}
+
+static int omap_rproc_ioctl(struct inode *inode, struct file *filp,
+					unsigned int cmd, unsigned long arg)
+{
+	int rc = 0;
+	struct omap_rproc *rproc = filp->private_data;
+
+	dev_info(rproc->dev, "%s\n", __func__);
+
+	if (!rproc)
+		return -EINVAL;
+
+	if (_IOC_TYPE(cmd) != RPROC_IOC_MAGIC)
+		return -ENOTTY;
+	if (_IOC_NR(cmd) > RPROC_IOC_MAXNR)
+		return -ENOTTY;
+
+	switch (cmd) {
+	case RPROC_IOCSTART:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		rc = rproc_start(rproc, (const void __user *) arg);
+		break;
+	case RPROC_IOCSTOP:
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+		rc = rproc_stop(rproc);
+		break;
+	case RPROC_IOCGETSTATE:
+		rc = rproc_get_state(rproc);
+		break;
+
+	default:
+		return -ENOTTY;
+	}
+
+	return rc;
+}
+
+static const struct file_operations omap_rproc_fops = {
+	.open		=	omap_rproc_open,
+	.release	=	omap_rproc_release,
+	.ioctl		=	omap_rproc_ioctl,
+	.owner		=	THIS_MODULE,
+};
+
+static int omap_rproc_probe(struct platform_device *pdev)
+{
+	int ret = 0, major, minor;
+	struct device *tmpdev;
+	struct device *dev = &pdev->dev;
+	struct omap_rproc_platform_data *pdata = dev->platform_data;
+	struct omap_rproc *rproc;
+
+	if (!pdata || !pdata->name || !pdata->oh_name || !pdata->ops)
+		return -EINVAL;
+
+	dev_info(dev, "%s: adding rproc %s\n", __func__, pdata->name);
+
+	rproc = kzalloc(sizeof(struct omap_rproc), GFP_KERNEL);
+	if (!rproc) {
+		dev_err(dev, "%s: kzalloc failed\n", __func__);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	platform_set_drvdata(pdev, rproc);
+	major = MAJOR(omap_rproc_dev);
+	minor = atomic_read(&num_of_rprocs);
+	atomic_inc(&num_of_rprocs);
+
+	rproc->dev = dev;
+	rproc->minor = minor;
+	atomic_set(&rproc->count, 0);
+	cdev_init(&rproc->cdev, &omap_rproc_fops);
+	rproc->cdev.owner = THIS_MODULE;
+	ret = cdev_add(&rproc->cdev, MKDEV(major, minor), 1);
+	if (ret) {
+		dev_err(dev, "%s: cdev_add failed: %d\n", __func__, ret);
+		goto free_rproc;
+	}
+
+	tmpdev = device_create(omap_rproc_class, NULL,
+				MKDEV(major, minor),
+				NULL,
+				OMAP_RPROC_NAME "%d", minor);
+	if (IS_ERR(tmpdev)) {
+		ret = PTR_ERR(tmpdev);
+		dev_err(dev, "%s: device_create failed: %d\n", __func__, ret);
+		goto clean_cdev;
+	}
+
+	dev_info(dev, "%s initialized %s, major: %d, base-minor: %d\n",
+			OMAP_RPROC_NAME,
+			pdata->name,
+			MAJOR(omap_rproc_dev),
+			minor);
+	return 0;
+
+clean_cdev:
+	cdev_del(&rproc->cdev);
+free_rproc:
+	kfree(rproc);
+out:
+	return ret;
+}
+
+static int __devexit omap_rproc_remove(struct platform_device *pdev)
+{
+	int major = MAJOR(omap_rproc_dev);
+	struct device *dev = &pdev->dev;
+	struct omap_rproc_platform_data *pdata = dev->platform_data;
+	struct omap_rproc *rproc = platform_get_drvdata(pdev);
+
+	if (!pdata || !rproc)
+		return -EINVAL;
+
+	dev_info(dev, "%s removing %s, major: %d, base-minor: %d\n",
+			OMAP_RPROC_NAME,
+			pdata->name,
+			major,
+			rproc->minor);
+
+	device_destroy(omap_rproc_class, MKDEV(major, rproc->minor));
+	cdev_del(&rproc->cdev);
+
+	return 0;
+}
+
+static struct platform_driver omap_rproc_driver = {
+	.probe = omap_rproc_probe,
+	.remove = __devexit_p(omap_rproc_remove),
+	.driver = {
+		.name = OMAP_RPROC_NAME,
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init omap_rproc_init(void)
+{
+	int num = omap_get_num_of_remoteproc();
+	int ret;
+
+	ret = alloc_chrdev_region(&omap_rproc_dev, 0, num, OMAP_RPROC_NAME);
+	if (ret) {
+		pr_err("%s: alloc_chrdev_region failed: %d\n", __func__, ret);
+		goto out;
+	}
+
+	omap_rproc_class = class_create(THIS_MODULE, OMAP_RPROC_NAME);
+	if (IS_ERR(omap_rproc_class)) {
+		ret = PTR_ERR(omap_rproc_class);
+		pr_err("%s: class_create failed: %d\n", __func__, ret);
+		goto unreg_region;
+	}
+
+	atomic_set(&num_of_rprocs, 0);
+
+	ret = platform_driver_register(&omap_rproc_driver);
+	if (ret) {
+		pr_err("%s: platform_driver_register failed: %d\n", __func__,
+									ret);
+		goto out;
+	}
+
+	return 0;
+
+unreg_region:
+	unregister_chrdev_region(omap_rproc_dev, num);
+out:
+	return ret;
+}
+module_init(omap_rproc_init);
+
+static void __exit omap_rproc_exit(void)
+{
+	int num = omap_get_num_of_remoteproc();
+
+	class_destroy(omap_rproc_class);
+	unregister_chrdev_region(omap_rproc_dev, num);
+}
+module_exit(omap_rproc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("OMAP Remote Processor driver");
+MODULE_AUTHOR("Ohad Ben-Cohen <ohad@wizery.com>");
+MODULE_AUTHOR("Hari Kanigeri <h-kanigeri2@ti.com>");