Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* linux/arch/arm/mach-msm/msm_zen_decision.c
- *
- * In-kernel replacement for MSM/MPDecision userspace service.
- *
- * Copyright (c) 2015 Brandon Berhent
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only 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.
- */
- #include <linux/module.h>
- #include <linux/kobject.h>
- #include <linux/sysfs.h>
- #include <linux/pm.h>
- #include <linux/cpu.h>
- #include <linux/cpumask.h>
- #include <linux/platform_device.h>
- #include <linux/workqueue.h>
- #define ZEN_DECISION "zen_decision"
- /* Enable/Disable driver */
- unsigned int enabled = 1;
- /* How long to wait to disable cores on suspend (in ms) */
- unsigned int suspend_wait_time = 5000;
- static struct workqueue_struct *zen_suspend_wq;
- static struct delayed_work suspend_work;
- struct kobject *zendecision_kobj;
- /*
- * __msm_zen_dec_suspend
- *
- * Core suspend work function.
- * Brings all CPUs except CPU0 offline
- */
- static void __msm_zen_dec_suspend(struct work_struct *work)
- {
- int cpu;
- pr_info("[%s]: SUSPEND DETECT", ZEN_DECISION);
- for_each_online_cpu(cpu) {
- /* Don't call cpu_down if cpu0 */
- if (cpu == 0) continue;
- cpu_down(cpu);
- }
- }
- /*
- * msm_zen_dec_suspend
- *
- * Call __msm_zen_dec_suspend as delayed work by suspend_wait_time
- * This is configurably delayed to avoid excessive CPU downing
- */
- static int msm_zen_dec_suspend(struct device *dev)
- {
- INIT_DELAYED_WORK(&suspend_work, __msm_zen_dec_suspend);
- queue_delayed_work_on(0, zen_suspend_wq, &suspend_work,
- msecs_to_jiffies(suspend_wait_time));
- }
- /*
- * msm_zen_dec_resume
- *
- * Core resume function.
- * Cancels suspend work and brings all CPUs online.
- */
- static int msm_zen_dec_resume(struct device *dev)
- {
- int cpu;
- /* Clear suspend workqueue */
- flush_workqueue(zen_suspend_wq);
- cancel_delayed_work_sync(&suspend_work);
- pr_info("[%s]: WAKE DETECT", ZEN_DECISION);
- for_each_cpu_not(cpu, cpu_online_mask) {
- /* Don't call cpu_up if cpu0 */
- if (cpu == 0) continue;
- cpu_up(cpu);
- }
- return 0;
- }
- static const struct dev_pm_ops msm_zen_dec_pm_ops = {
- .suspend = msm_zen_dec_suspend,
- .resume = msm_zen_dec_resume
- };
- /* Sysfs Start */
- static ssize_t enable_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- return sprintf(buf, "%u\n", enabled);
- }
- static ssize_t enable_store(struct kobject *kobj,
- struct kobj_attribute *attr, const char *buf, size_t size)
- {
- int ret;
- unsigned long new_val;
- ret = kstrtoul(buf, 0, &new_val);
- if (ret < 0)
- return ret;
- if (new_val > 0)
- enabled = 1;
- else
- enabled = 0;
- return size;
- }
- static ssize_t suspend_delay_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
- {
- return sprintf(buf, "%u\n", enabled);
- }
- static ssize_t suspend_delay_store(struct kobject *kobj,
- struct kobj_attribute *attr, const char *buf, size_t size)
- {
- int ret;
- unsigned long new_val;
- ret = kstrtoul(buf, 0, &new_val);
- if (ret < 0)
- return ret;
- if (new_val < 0)
- suspend_wait_time = 0;
- return size;
- }
- static struct kobj_attribute kobj_enabled =
- __ATTR(enabled, 0644, enable_show,
- enable_store);
- static struct kobj_attribute kobj_suspend_wait =
- __ATTR(suspend_wait_time, 0644, suspend_delay_show,
- suspend_delay_store);
- static struct attribute *zen_decision_attrs[] = {
- &kobj_enabled.attr, &kobj_suspend_wait, NULL,
- };
- static struct attribute_group zen_decision_option_group = {
- .attrs = zen_decision_attrs,
- };
- /* Sysfs End */
- static int zen_decision_probe(struct platform_device *pdev)
- {
- int ret;
- /* Set default settings */
- enabled = 1;
- /* Setup sysfs */
- zendecision_kobj = kobject_create_and_add("zen_decision", kernel_kobj);
- if (zendecision_kobj == NULL) {
- pr_err("[%s]: subsystem register failed. \n", ZEN_DECISION);
- return -ENOMEM;
- }
- ret = sysfs_create_group(zendecision_kobj, &zen_decision_option_group);
- if (ret) {
- pr_info("[%s]: sysfs interface failed to initialize\n", ZEN_DECISION);
- return -EINVAL;
- } else
- pr_info("[%s]: sysfs interface initialized.\n", ZEN_DECISION);
- /* Setup Workqueues */
- zen_suspend_wq = alloc_workqueue("zen_suspend_wq", WQ_FREEZABLE, 0);
- if (!zen_suspend_wq) {
- pr_err("[%s]: Failed to allocate suspend workqueue\n", ZEN_DECISION);
- return -ENOMEM;
- }
- return ret;
- }
- static int zen_decision_remove(struct platform_device *pdev)
- {
- kobject_put(zendecision_kobj);
- flush_workqueue(zen_suspend_wq);
- cancel_delayed_work_sync(&suspend_work);
- destroy_workqueue(suspend_workqueue);
- return 0;
- }
- static struct platform_driver zen_decision_driver = {
- .probe = zen_decision_probe,
- .remove = zen_decision_remove,
- .driver = {
- .name = ZEN_DECISION,
- .owner = THIS_MODULE,
- .pm = &msm_zen_dec_pm_ops,
- }
- };
- static int __init zen_decision_init(void)
- {
- int ret = platform_driver_register(&zen_decision_driver);
- if (ret)
- pr_err("[%s]: platform_driver_register_failed: %d\n", ZEN_DECISION, ret);
- return ret;
- }
- static void __exit zen_decision_exit(void)
- {
- platform_driver_unregister(&zen_decision_driver);
- }
- late_initcall(zen_decision_init);
- module_exit(zen_decision_exit);
- MODULE_VERSION("1.0");
- MODULE_DESCRIPTION("Zen Decision MPDecision Replacement");
- MODULE_LICENSE("GPL v2");
- MODULE_AUTHOR("Brandon Berhent <bbedward@gmail.com>");
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement