blob: 179e17015fd209390bc3668e8dfe602fbc0c69f6 [file] [log] [blame]
Praveen Chidambaram8ea3dcd2011-12-07 14:46:31 -07001/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/kernel.h>
15#include <linux/init.h>
16#include <linux/module.h>
17#include <linux/cpu_pm.h>
18#include <linux/platform_device.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070019#include <linux/pm_qos.h>
Praveen Chidambaram8ea3dcd2011-12-07 14:46:31 -070020#include <linux/hrtimer.h>
21#include <linux/tick.h>
22#include <mach/msm_dcvs.h>
23
24struct cpu_idle_info {
25 int cpu;
26 int enabled;
27 int handle;
28 struct msm_dcvs_idle dcvs_notifier;
29};
30
31static DEFINE_PER_CPU_SHARED_ALIGNED(struct cpu_idle_info, cpu_idle_info);
32static DEFINE_PER_CPU_SHARED_ALIGNED(u64, iowait_on_cpu);
33static char core_name[NR_CPUS][10];
Steve Mucklef132c6c2012-06-06 18:30:57 -070034static struct pm_qos_request qos_req;
Praveen Chidambaram8ea3dcd2011-12-07 14:46:31 -070035static uint32_t latency;
36
37static int msm_dcvs_idle_notifier(struct msm_dcvs_idle *self,
38 enum msm_core_control_event event)
39{
40 struct cpu_idle_info *info = container_of(self,
41 struct cpu_idle_info, dcvs_notifier);
42
43 switch (event) {
44 case MSM_DCVS_ENABLE_IDLE_PULSE:
45 info->enabled = true;
46 break;
47
48 case MSM_DCVS_DISABLE_IDLE_PULSE:
49 info->enabled = false;
50 break;
51
52 case MSM_DCVS_ENABLE_HIGH_LATENCY_MODES:
53 pm_qos_update_request(&qos_req, PM_QOS_DEFAULT_VALUE);
54 break;
55
56 case MSM_DCVS_DISABLE_HIGH_LATENCY_MODES:
57 pm_qos_update_request(&qos_req, latency);
58 break;
59 }
60
61 return 0;
62}
63
64static int msm_cpuidle_notifier(struct notifier_block *self, unsigned long cmd,
65 void *v)
66{
67 struct cpu_idle_info *info =
68 &per_cpu(cpu_idle_info, smp_processor_id());
69 u64 io_wait_us = 0;
70 u64 prev_io_wait_us = 0;
71 u64 last_update_time = 0;
72 u64 val = 0;
73 uint32_t iowaited = 0;
74
75 if (!info->enabled)
76 return NOTIFY_OK;
77
78 switch (cmd) {
79 case CPU_PM_ENTER:
80 val = get_cpu_iowait_time_us(smp_processor_id(),
81 &last_update_time);
82 /* val could be -1 when NOHZ is not enabled */
83 if (val == (u64)-1)
84 val = 0;
85 per_cpu(iowait_on_cpu, smp_processor_id()) = val;
86 msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_ENTER, 0);
87 break;
88
89 case CPU_PM_ENTER_FAILED:
90 case CPU_PM_EXIT:
91 prev_io_wait_us = per_cpu(iowait_on_cpu, smp_processor_id());
92 val = get_cpu_iowait_time_us(smp_processor_id(),
93 &last_update_time);
94 if (val == (u64)-1)
95 val = 0;
96 io_wait_us = val;
97 iowaited = (io_wait_us - prev_io_wait_us);
98 msm_dcvs_idle(info->handle, MSM_DCVS_IDLE_EXIT, iowaited);
99 break;
100 }
101
102 return NOTIFY_OK;
103}
104
105static struct notifier_block idle_nb = {
106 .notifier_call = msm_cpuidle_notifier,
107};
108
109static int msm_dcvs_idle_probe(struct platform_device *pdev)
110{
111 int cpu;
112 struct cpu_idle_info *info = NULL;
113 struct msm_dcvs_idle *inotify = NULL;
114
115 for_each_possible_cpu(cpu) {
116 info = &per_cpu(cpu_idle_info, cpu);
117 info->cpu = cpu;
118 inotify = &info->dcvs_notifier;
119 snprintf(core_name[cpu], 10, "cpu%d", cpu);
120 inotify->core_name = core_name[cpu];
121 inotify->enable = msm_dcvs_idle_notifier;
122 info->handle = msm_dcvs_idle_source_register(inotify);
123 BUG_ON(info->handle < 0);
124 }
125
126 latency = *((uint32_t *)pdev->dev.platform_data);
127 pm_qos_add_request(&qos_req, PM_QOS_CPU_DMA_LATENCY,
128 PM_QOS_DEFAULT_VALUE);
129
130 return cpu_pm_register_notifier(&idle_nb);
131}
132
133static int msm_dcvs_idle_remove(struct platform_device *pdev)
134{
135 int ret = 0;
136 int rc = 0;
137 int cpu = 0;
138 struct msm_dcvs_idle *inotify = NULL;
139 struct cpu_idle_info *info = NULL;
140
141 rc = cpu_pm_unregister_notifier(&idle_nb);
142
143 for_each_possible_cpu(cpu) {
144 info = &per_cpu(cpu_idle_info, cpu);
145 inotify = &info->dcvs_notifier;
146 ret = msm_dcvs_idle_source_unregister(inotify);
147 if (ret) {
148 rc = -EFAULT;
149 pr_err("Error de-registering core %d idle notifier.\n",
150 cpu);
151 }
152 }
153
154 return rc;
155}
156
157static struct platform_driver idle_pdrv = {
158 .probe = msm_dcvs_idle_probe,
159 .remove = __devexit_p(msm_dcvs_idle_remove),
160 .driver = {
161 .name = "msm_cpu_idle",
162 .owner = THIS_MODULE,
163 },
164};
165
166static int msm_dcvs_idle_init(void)
167{
168 return platform_driver_register(&idle_pdrv);
169}
170late_initcall(msm_dcvs_idle_init);