aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/remoteproc/rpres.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/remoteproc/rpres.c')
-rw-r--r--drivers/remoteproc/rpres.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/drivers/remoteproc/rpres.c b/drivers/remoteproc/rpres.c
new file mode 100644
index 0000000..e839f70
--- /dev/null
+++ b/drivers/remoteproc/rpres.c
@@ -0,0 +1,204 @@
+/*
+ * Remote processor resources
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <plat/omap_device.h>
+#include <plat/rpres.h>
+
+static LIST_HEAD(rpres_list);
+static DEFINE_SPINLOCK(rpres_lock);
+
+static struct rpres *__find_by_name(const char *name)
+{
+ struct rpres *obj;
+
+ list_for_each_entry(obj, &rpres_list, next)
+ if (!strcmp(obj->name, name))
+ return obj;
+ return NULL;
+}
+
+struct rpres *rpres_get(const char *name)
+{
+ int ret;
+ struct rpres *r;
+ struct rpres_platform_data *pdata;
+
+ spin_lock(&rpres_lock);
+ r = __find_by_name(name);
+ spin_unlock(&rpres_lock);
+ if (!r)
+ return ERR_PTR(-ENOENT);
+
+ mutex_lock(&r->lock);
+ if (r->state == RPRES_ACTIVE) {
+ pr_err("%s:resource already active\n", __func__);
+ ret = -EINVAL;
+ goto out;
+ }
+ pdata = r->pdev->dev.platform_data;
+ ret = pdata->ops->start(r->pdev);
+ if (!ret)
+ r->state = RPRES_ACTIVE;
+out:
+ mutex_unlock(&r->lock);
+ if (ret)
+ return ERR_PTR(ret);
+ return r;
+}
+EXPORT_SYMBOL(rpres_get);
+
+void rpres_put(struct rpres *obj)
+{
+ struct rpres_platform_data *pdata = obj->pdev->dev.platform_data;
+ mutex_lock(&obj->lock);
+ if (obj->state == RPRES_INACTIVE) {
+ pr_err("%s:resource already inactive\n", __func__);
+ } else {
+ pdata->ops->stop(obj->pdev);
+ obj->state = RPRES_INACTIVE;
+ }
+ mutex_unlock(&obj->lock);
+}
+EXPORT_SYMBOL(rpres_put);
+
+int rpres_set_constraints(struct rpres *obj, enum rpres_constraint type, long val)
+{
+ int ret;
+ struct rpres_platform_data *pdata = obj->pdev->dev.platform_data;
+ struct platform_device *pdev = obj->pdev;
+ static const char *cname[] = {"scale", "latency", "bandwidth"};
+ int (*func)(struct platform_device *, long);
+
+ switch (type) {
+ case RPRES_CONSTRAINT_SCALE:
+ func = pdata->ops->scale_dev;
+ break;
+ case RPRES_CONSTRAINT_LATENCY:
+ func = pdata->ops->set_lat;
+ break;
+ case RPRES_CONSTRAINT_BANDWIDTH:
+ func = pdata->ops->set_bw;
+ break;
+ default:
+ dev_err(&pdev->dev, "%s: invalid constraint %d\n",
+ __func__, type);
+ return -EINVAL;
+ }
+
+ if (!func) {
+ dev_err(&pdev->dev, "%s: No %s constraint\n",
+ __func__, cname[type]);
+ return -EINVAL;
+ }
+
+ mutex_lock(&obj->lock);
+ if (obj->state == RPRES_INACTIVE) {
+ mutex_unlock(&obj->lock);
+ pr_err("%s: resource inactive\n", __func__);
+ return -EPERM;
+ }
+
+ dev_dbg(&pdev->dev, "set %s constraint %ld\n", cname[type], val);
+ ret = func(pdev, val);
+ if (ret)
+ dev_err(&pdev->dev, "%s: error setting constraint %s\n",
+ __func__, cname[type]);
+ mutex_unlock(&obj->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL(rpres_set_constraints);
+
+static int rpres_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rpres_platform_data *pdata = dev->platform_data;
+ struct rpres *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (!obj)
+ return -ENOMEM;
+
+ obj->pdev = pdev;
+ obj->name = pdata->name;
+ obj->state = RPRES_INACTIVE;
+ mutex_init(&obj->lock);
+
+ spin_lock(&rpres_lock);
+ list_add_tail(&obj->next, &rpres_list);
+ spin_unlock(&rpres_lock);
+
+ return 0;
+}
+
+static int __devexit rpres_remove(struct platform_device *pdev)
+{
+ struct rpres_platform_data *pdata = pdev->dev.platform_data;
+ struct rpres *obj;
+
+ spin_lock(&rpres_lock);
+ obj = __find_by_name(pdata->name);
+ if (!obj) {
+ spin_unlock(&rpres_lock);
+ dev_err(&pdev->dev, "fail to remove %s\n", pdata->name);
+ return -ENOENT;
+ }
+ list_del(&obj->next);
+ spin_unlock(&rpres_lock);
+
+ kfree(obj);
+
+ return 0;
+}
+
+static struct platform_device_id rpres_id_table[] = {
+ {
+ .name = "iva",
+ },
+ {
+ .name = "fdif",
+ },
+ {
+ .name = "rpres",
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, rpres_id_table);
+
+static struct platform_driver omap_rpres_driver = {
+ .id_table = rpres_id_table,
+ .probe = rpres_probe,
+ .remove = __devexit_p(rpres_remove),
+ .driver = {
+ .name = "rpres",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init rpres_init(void)
+{
+ return platform_driver_register(&omap_rpres_driver);
+}
+late_initcall(rpres_init);
+
+static void __exit rpres_exit(void)
+{
+ platform_driver_unregister(&omap_rpres_driver);
+}
+module_exit(rpres_exit);
+
+MODULE_LICENSE("GPL v2");