/* * Remote processor resources * * Copyright (C) 2011 Texas Instruments, Inc. * Copyright (C) 2011 Google, Inc. * * Fernando Guzman Lugo * * 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 #include #include #include #include #include #include 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 char * const 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); unsigned long rpres_get_max_freq(struct rpres *obj) { struct platform_device *pdev = obj->pdev; struct opp *opp; unsigned long maxfreq = ULONG_MAX; rcu_read_lock(); opp = opp_find_freq_floor(&pdev->dev, &maxfreq); if (IS_ERR(opp)) maxfreq = 0; rcu_read_unlock(); return maxfreq; } EXPORT_SYMBOL(rpres_get_max_freq); 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");