| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- /*
- * drivers/dynamic_boost/dynamic_boost.c
- *
- * Copyright (C) 2010 MediaTek <www.MediaTek.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.
- *
- * 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/cpu.h>
- #include <linux/cpumask.h>
- #include <linux/cpufreq.h>
- #include <linux/delay.h>
- #include <linux/module.h>
- #include <linux/moduleparam.h>
- #include <linux/mutex.h>
- #include <linux/platform_device.h>
- #include <linux/rwsem.h>
- #include <linux/sched.h>
- #include <linux/sched/rt.h>
- #include <linux/sysfs.h>
- #include <linux/kthread.h>
- #include <linux/slab.h>
- #include <linux/spinlock.h>
- #include <mt_hotplug_strategy.h>
- #include <mt_hotplug_strategy_internal.h>
- #include "mt_cpufreq.h"
- #include <linux/input.h>
- #include <linux/workqueue.h>
- #include "dynamic_boost.h"
- struct boost_state {
- int active;
- struct delayed_work work;
- };
- struct dynamic_boost {
- spinlock_t boost_lock;
- int last_req_mode;
- wait_queue_head_t wq;
- struct task_struct *thread;
- struct boost_state state[PRIO_DEFAULT];
- atomic_t event;
- };
- static struct dynamic_boost dboost;
- struct dboost_input_handle {
- struct input_handle handle;
- int duration;
- int prio_mode;
- };
- #define MAX_CORES_NUMBER nr_cpu_ids
- #define MAX_FREQUENCY 1
- #define MAX_DURATION 10000
- static ssize_t dynamic_boost_show(struct device *dev, struct device_attribute *attr, char *buf);
- static ssize_t dynamic_boost_store(struct device *dev, struct device_attribute *attr, const char *buf,
- size_t n);
- static struct device_attribute dynamic_boost_attr = __ATTR(dynamic_boost, 0750,
- dynamic_boost_show, dynamic_boost_store);
- static void dboost_disable_work(struct work_struct *work)
- {
- unsigned long flags;
- struct boost_state *state = container_of(work, struct boost_state, work.work);
- spin_lock_irqsave(&dboost.boost_lock, flags);
- if (state->active > 0)
- state->active--;
- spin_unlock_irqrestore(&dboost.boost_lock, flags);
- atomic_inc(&dboost.event);
- wake_up(&dboost.wq);
- }
- /*
- * Set up performance boost mode with requested duration
- * @duration: How long the user want this mode to keep. Specify with ms.
- * @mode: Request mode.
- */
- int set_dynamic_boost(int duration, int prio_mode)
- {
- unsigned long flags;
- struct boost_state *state;
- if (duration > MAX_DURATION ||
- prio_mode < 0 || prio_mode > PRIO_DEFAULT)
- return -EINVAL;
- if (prio_mode == PRIO_DEFAULT || !duration)
- return 0;
- spin_lock_irqsave(&dboost.boost_lock, flags);
- state = &dboost.state[prio_mode];
- if (duration == ON)
- state->active++;
- else if (duration == OFF)
- state->active--;
- else if (!mod_delayed_work(system_wq, &state->work, msecs_to_jiffies(duration)))
- state->active++;
- spin_unlock_irqrestore(&dboost.boost_lock, flags);
- atomic_inc(&dboost.event);
- wake_up(&dboost.wq);
- return 0;
- }
- EXPORT_SYMBOL(set_dynamic_boost);
- static int dboost_dvfs_hotplug_thread(void *ptr)
- {
- int max_freq, cores_to_set_b, cores_to_set_l;
- unsigned long flags;
- set_user_nice(current, -10);
- while (!kthread_should_stop()) {
- int i, set_mode = PRIO_DEFAULT;
- spin_lock_irqsave(&dboost.boost_lock, flags);
- for (i = PRIO_DEFAULT - 1; i >= 0; i--) {
- if (dboost.state[i].active) {
- set_mode = i;
- break;
- }
- }
- spin_unlock_irqrestore(&dboost.boost_lock, flags);
- switch (set_mode) {
- case PRIO_MAX_CORES_MAX_FREQ:
- cores_to_set_b = num_possible_big_cpus();
- cores_to_set_l = num_possible_little_cpus();
- max_freq = MAX_FREQUENCY;
- break;
- case PRIO_MAX_CORES:
- cores_to_set_b = num_possible_big_cpus();
- cores_to_set_l = num_possible_little_cpus();
- max_freq = 0;
- break;
- case PRIO_FOUR_BIGS_MAX_FREQ:
- cores_to_set_b = 4;
- cores_to_set_l = 0;
- max_freq = MAX_FREQUENCY;
- break;
- case PRIO_FOUR_BIGS:
- cores_to_set_b = 4;
- cores_to_set_l = 0;
- max_freq = 0;
- break;
- case PRIO_TWO_BIGS_TWO_LITTLES_MAX_FREQ:
- cores_to_set_b = 2;
- cores_to_set_l = 2;
- max_freq = MAX_FREQUENCY;
- break;
- case PRIO_TWO_BIGS_TWO_LITTLES:
- cores_to_set_b = 2;
- cores_to_set_l = 2;
- max_freq = 0;
- break;
- case PRIO_FOUR_LITTLES_MAX_FREQ:
- cores_to_set_b = 0;
- cores_to_set_l = 4;
- max_freq = MAX_FREQUENCY;
- break;
- case PRIO_FOUR_LITTLES:
- cores_to_set_b = 0;
- cores_to_set_l = 4;
- max_freq = 0;
- break;
- case PRIO_TWO_BIGS_MAX_FREQ:
- cores_to_set_b = 2;
- cores_to_set_l = 0;
- max_freq = MAX_FREQUENCY;
- break;
- case PRIO_TWO_BIGS:
- cores_to_set_b = 2;
- cores_to_set_l = 0;
- max_freq = 0;
- break;
- case PRIO_ONE_BIG_ONE_LITTLE_MAX_FREQ:
- cores_to_set_b = 1;
- cores_to_set_l = 1;
- max_freq = MAX_FREQUENCY;
- break;
- case PRIO_ONE_BIG_ONE_LITTLE:
- cores_to_set_b = 1;
- cores_to_set_l = 1;
- max_freq = 0;
- break;
- case PRIO_ONE_BIG_MAX_FREQ:
- cores_to_set_b = 1;
- cores_to_set_l = 0;
- max_freq = MAX_FREQUENCY;
- break;
- case PRIO_ONE_BIG:
- cores_to_set_b = 1;
- cores_to_set_l = 0;
- max_freq = 0;
- break;
- case PRIO_TWO_LITTLES_MAX_FREQ:
- cores_to_set_b = 0;
- cores_to_set_l = 2;
- max_freq = MAX_FREQUENCY;
- break;
- case PRIO_TWO_LITTLES:
- cores_to_set_b = 0;
- cores_to_set_l = 2;
- max_freq = 0;
- break;
- case PRIO_RESET:
- for (i = PRIO_DEFAULT - 1; i >= 0; i--)
- dboost.state[i].active = 0;
- default:
- cores_to_set_b = 0;
- cores_to_set_l = 0;
- max_freq = 0;
- break;
- }
- interactive_boost_cpu(max_freq);
- if (!cores_to_set_l)
- cores_to_set_l = 1;
- hps_set_cpu_num_base(BASE_PERF_SERV, cores_to_set_l, cores_to_set_b);
- /* printk("dynamic boost: Mode=%d cpu_min_num_big=%d cpu_min_num_little=%d\n",
- set_mode, cores_to_set_b, cores_to_set_l);*/
- dboost.last_req_mode = set_mode;
- while (!atomic_read(&dboost.event))
- wait_event(dboost.wq, atomic_read(&dboost.event));
- atomic_dec(&dboost.event);
- }
- return 0;
- }
- static ssize_t dynamic_boost_show(struct device *dev, struct device_attribute *attr, char *buf)
- {
- int i;
- i = snprintf(buf, PAGE_SIZE, "Mode: %d\n", dboost.last_req_mode);
- return i;
- }
- static ssize_t dynamic_boost_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t n)
- {
- int now_req_duration = 0;
- int now_req_mode = 0;
- if ((n == 0) || (buf == NULL))
- return -EINVAL;
- if (sscanf(buf, "%d %d", &now_req_duration, &now_req_mode) != 2)
- return -EINVAL;
- if (now_req_mode < 0)
- return -EINVAL;
- set_dynamic_boost(now_req_duration, now_req_mode);
- return n;
- }
- static int dynamic_boost_probe(struct platform_device *dev)
- {
- int ret_device_file = 0;
- ret_device_file = device_create_file(&(dev->dev), &dynamic_boost_attr);
- return ret_device_file;
- }
- struct platform_device dynamic_boost_device = {
- .name = "dynamic_boost",
- .id = -1,
- };
- int dboost_suspend(struct device *dev)
- {
- unsigned long flags;
- int i;
- /* cancel all boost jobs if system suspend is requested */
- for (i = 0; i < ARRAY_SIZE(dboost.state); ++i) {
- cancel_delayed_work_sync(&dboost.state[i].work);
- spin_lock_irqsave(&dboost.boost_lock, flags);
- dboost.state[i].active = 0;
- spin_unlock_irqrestore(&dboost.boost_lock, flags);
- }
- atomic_inc(&dboost.event);
- wake_up(&dboost.wq);
- return 0;
- }
- int dboost_resume(struct device *dev)
- {
- return 0;
- }
- static struct platform_driver dynamic_boost_driver = {
- .probe = dynamic_boost_probe,
- .driver = {
- .name = "dynamic_boost",
- .pm = &(const struct dev_pm_ops){
- .suspend = dboost_suspend,
- .resume = dboost_resume,
- },
- },
- };
- #ifdef CONFIG_TOUCH_BOOST
- /******************************************************************************
- * Handle touch boost *
- ******************************************************************************/
- static void dboost_input_event(struct input_handle *handle, unsigned int type,
- unsigned int code, int value)
- {
- struct dboost_input_handle *in = container_of(handle, struct dboost_input_handle, handle);
- if ((type == EV_KEY && code == BTN_TOUCH) ||
- (type == EV_ABS && code == ABS_MT_TRACKING_ID && value != 0))
- set_dynamic_boost(in->duration, in->prio_mode);
- }
- static int dboost_input_connect(struct input_handler *handler,
- struct input_dev *dev, const struct input_device_id *id)
- {
- struct dboost_input_handle *in;
- int error;
- in = kzalloc(sizeof(struct dboost_input_handle), GFP_KERNEL);
- if (!in)
- return -ENOMEM;
- in->handle.dev = dev;
- in->handle.handler = handler;
- in->handle.name = "dynamic_boost";
- /* TODO: the following parameters should be configured through platform data */
- in->prio_mode = PRIO_TWO_BIGS_TWO_LITTLES_MAX_FREQ;
- in->duration = 150;
- error = input_register_handle(&in->handle);
- if (error)
- goto err2;
- error = input_open_device(&in->handle);
- if (error)
- goto err1;
- return 0;
- err1:
- input_unregister_handle(&in->handle);
- err2:
- kfree(&in->handle);
- return error;
- }
- static void dboost_input_disconnect(struct input_handle *handle)
- {
- struct dboost_input_handle *in = container_of(handle, struct dboost_input_handle, handle);
- input_close_device(handle);
- input_unregister_handle(handle);
- kfree(in);
- }
- static const struct input_device_id dboost_ids[] = {
- { .driver_info = 1 },
- { },
- };
- static struct input_handler dboost_input_handler = {
- .event = dboost_input_event,
- .connect = dboost_input_connect,
- .disconnect = dboost_input_disconnect,
- .name = "touch_boost_ond",
- .id_table = dboost_ids,
- };
- #endif /* CONFIG_TOUCH_BOOST */
- static int __init dynamic_boost_init(void)
- {
- int ret = 0, i;
- ret = platform_device_register(&dynamic_boost_device);
- if (ret)
- return ret;
- ret = platform_driver_register(&dynamic_boost_driver);
- if (ret)
- return ret;
- spin_lock_init(&dboost.boost_lock);
- dboost.last_req_mode = PRIO_DEFAULT;
- atomic_set(&dboost.event, 0);
- for (i = 0; i < ARRAY_SIZE(dboost.state); ++i) {
- INIT_DELAYED_WORK(&dboost.state[i].work, dboost_disable_work);
- #if 0 /* TODO: should 2 littles default always on? */
- if (i == PRIO_TWO_LITTLES)
- dboost.state[i].active = 1;
- else
- #endif
- dboost.state[i].active = 0;
- }
- init_waitqueue_head(&dboost.wq);
- dboost.thread = kthread_run(dboost_dvfs_hotplug_thread, &dboost, "dynamic_boost");
- if (IS_ERR(dboost.thread))
- return -EINVAL;
- #ifdef CONFIG_TOUCH_BOOST
- ret = input_register_handler(&dboost_input_handler);
- if (ret)
- return ret;
- #endif
- return 0;
- }
- late_initcall(dynamic_boost_init);
- static void __exit dynamic_boost_exit(void)
- {
- #ifdef CONFIG_TOUCH_BOOST
- input_unregister_handler(&dboost_input_handler);
- #endif
- kthread_stop(dboost.thread);
- }
- module_exit(dynamic_boost_exit);
- MODULE_AUTHOR("MediaTek Inc.");
- MODULE_DESCRIPTION("'dynamic boost' - provide performance boost");
- MODULE_LICENSE("GPL");
|