Merge tag 'mac80211-for-john-2014-11-04' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / drivers / cpuidle / cpuidle-cps.c
1 /*
2  * Copyright (C) 2014 Imagination Technologies
3  * Author: Paul Burton <paul.burton@imgtec.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation;  either version 2 of the  License, or (at your
8  * option) any later version.
9  */
10
11 #include <linux/cpu_pm.h>
12 #include <linux/cpuidle.h>
13 #include <linux/init.h>
14
15 #include <asm/idle.h>
16 #include <asm/pm-cps.h>
17
18 /* Enumeration of the various idle states this driver may enter */
19 enum cps_idle_state {
20         STATE_WAIT = 0,         /* MIPS wait instruction, coherent */
21         STATE_NC_WAIT,          /* MIPS wait instruction, non-coherent */
22         STATE_CLOCK_GATED,      /* Core clock gated */
23         STATE_POWER_GATED,      /* Core power gated */
24         STATE_COUNT
25 };
26
27 static int cps_nc_enter(struct cpuidle_device *dev,
28                         struct cpuidle_driver *drv, int index)
29 {
30         enum cps_pm_state pm_state;
31         int err;
32
33         /*
34          * At least one core must remain powered up & clocked in order for the
35          * system to have any hope of functioning.
36          *
37          * TODO: don't treat core 0 specially, just prevent the final core
38          * TODO: remap interrupt affinity temporarily
39          */
40         if (!cpu_data[dev->cpu].core && (index > STATE_NC_WAIT))
41                 index = STATE_NC_WAIT;
42
43         /* Select the appropriate cps_pm_state */
44         switch (index) {
45         case STATE_NC_WAIT:
46                 pm_state = CPS_PM_NC_WAIT;
47                 break;
48         case STATE_CLOCK_GATED:
49                 pm_state = CPS_PM_CLOCK_GATED;
50                 break;
51         case STATE_POWER_GATED:
52                 pm_state = CPS_PM_POWER_GATED;
53                 break;
54         default:
55                 BUG();
56                 return -EINVAL;
57         }
58
59         /* Notify listeners the CPU is about to power down */
60         if ((pm_state == CPS_PM_POWER_GATED) && cpu_pm_enter())
61                 return -EINTR;
62
63         /* Enter that state */
64         err = cps_pm_enter_state(pm_state);
65
66         /* Notify listeners the CPU is back up */
67         if (pm_state == CPS_PM_POWER_GATED)
68                 cpu_pm_exit();
69
70         return err ?: index;
71 }
72
73 static struct cpuidle_driver cps_driver = {
74         .name                   = "cpc_cpuidle",
75         .owner                  = THIS_MODULE,
76         .states = {
77                 [STATE_WAIT] = MIPS_CPUIDLE_WAIT_STATE,
78                 [STATE_NC_WAIT] = {
79                         .enter  = cps_nc_enter,
80                         .exit_latency           = 200,
81                         .target_residency       = 450,
82                         .flags  = CPUIDLE_FLAG_TIME_VALID,
83                         .name   = "nc-wait",
84                         .desc   = "non-coherent MIPS wait",
85                 },
86                 [STATE_CLOCK_GATED] = {
87                         .enter  = cps_nc_enter,
88                         .exit_latency           = 300,
89                         .target_residency       = 700,
90                         .flags  = CPUIDLE_FLAG_TIME_VALID |
91                                   CPUIDLE_FLAG_TIMER_STOP,
92                         .name   = "clock-gated",
93                         .desc   = "core clock gated",
94                 },
95                 [STATE_POWER_GATED] = {
96                         .enter  = cps_nc_enter,
97                         .exit_latency           = 600,
98                         .target_residency       = 1000,
99                         .flags  = CPUIDLE_FLAG_TIME_VALID |
100                                   CPUIDLE_FLAG_TIMER_STOP,
101                         .name   = "power-gated",
102                         .desc   = "core power gated",
103                 },
104         },
105         .state_count            = STATE_COUNT,
106         .safe_state_index       = 0,
107 };
108
109 static void __init cps_cpuidle_unregister(void)
110 {
111         int cpu;
112         struct cpuidle_device *device;
113
114         for_each_possible_cpu(cpu) {
115                 device = &per_cpu(cpuidle_dev, cpu);
116                 cpuidle_unregister_device(device);
117         }
118
119         cpuidle_unregister_driver(&cps_driver);
120 }
121
122 static int __init cps_cpuidle_init(void)
123 {
124         int err, cpu, core, i;
125         struct cpuidle_device *device;
126
127         /* Detect supported states */
128         if (!cps_pm_support_state(CPS_PM_POWER_GATED))
129                 cps_driver.state_count = STATE_CLOCK_GATED + 1;
130         if (!cps_pm_support_state(CPS_PM_CLOCK_GATED))
131                 cps_driver.state_count = STATE_NC_WAIT + 1;
132         if (!cps_pm_support_state(CPS_PM_NC_WAIT))
133                 cps_driver.state_count = STATE_WAIT + 1;
134
135         /* Inform the user if some states are unavailable */
136         if (cps_driver.state_count < STATE_COUNT) {
137                 pr_info("cpuidle-cps: limited to ");
138                 switch (cps_driver.state_count - 1) {
139                 case STATE_WAIT:
140                         pr_cont("coherent wait\n");
141                         break;
142                 case STATE_NC_WAIT:
143                         pr_cont("non-coherent wait\n");
144                         break;
145                 case STATE_CLOCK_GATED:
146                         pr_cont("clock gating\n");
147                         break;
148                 }
149         }
150
151         /*
152          * Set the coupled flag on the appropriate states if this system
153          * requires it.
154          */
155         if (coupled_coherence)
156                 for (i = STATE_NC_WAIT; i < cps_driver.state_count; i++)
157                         cps_driver.states[i].flags |= CPUIDLE_FLAG_COUPLED;
158
159         err = cpuidle_register_driver(&cps_driver);
160         if (err) {
161                 pr_err("Failed to register CPS cpuidle driver\n");
162                 return err;
163         }
164
165         for_each_possible_cpu(cpu) {
166                 core = cpu_data[cpu].core;
167                 device = &per_cpu(cpuidle_dev, cpu);
168                 device->cpu = cpu;
169 #ifdef CONFIG_MIPS_MT
170                 cpumask_copy(&device->coupled_cpus, &cpu_sibling_map[cpu]);
171 #endif
172
173                 err = cpuidle_register_device(device);
174                 if (err) {
175                         pr_err("Failed to register CPU%d cpuidle device\n",
176                                cpu);
177                         goto err_out;
178                 }
179         }
180
181         return 0;
182 err_out:
183         cps_cpuidle_unregister();
184         return err;
185 }
186 device_initcall(cps_cpuidle_init);