Commit | Line | Data |
---|---|---|
74889e41 CK |
1 | /* |
2 | * pmi backend for the cbe_cpufreq driver | |
3 | * | |
4 | * (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007 | |
5 | * | |
6 | * Author: Christian Krafft <krafft@de.ibm.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2, or (at your option) | |
11 | * any later version. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | */ | |
22 | ||
23 | #include <linux/kernel.h> | |
24 | #include <linux/types.h> | |
25 | #include <linux/timer.h> | |
7dfe293c | 26 | #include <linux/module.h> |
d8caf74f JL |
27 | #include <linux/of_platform.h> |
28 | ||
74889e41 CK |
29 | #include <asm/processor.h> |
30 | #include <asm/prom.h> | |
31 | #include <asm/pmi.h> | |
eef686a0 | 32 | #include <asm/cell-regs.h> |
74889e41 CK |
33 | |
34 | #ifdef DEBUG | |
35 | #include <asm/time.h> | |
36 | #endif | |
37 | ||
6eb1c377 | 38 | #include "ppc_cbe_cpufreq.h" |
74889e41 CK |
39 | |
40 | static u8 pmi_slow_mode_limit[MAX_CBE]; | |
41 | ||
42 | bool cbe_cpufreq_has_pmi = false; | |
43 | EXPORT_SYMBOL_GPL(cbe_cpufreq_has_pmi); | |
44 | ||
45 | /* | |
46 | * hardware specific functions | |
47 | */ | |
48 | ||
49 | int cbe_cpufreq_set_pmode_pmi(int cpu, unsigned int pmode) | |
50 | { | |
51 | int ret; | |
52 | pmi_message_t pmi_msg; | |
53 | #ifdef DEBUG | |
54 | long time; | |
55 | #endif | |
56 | pmi_msg.type = PMI_TYPE_FREQ_CHANGE; | |
57 | pmi_msg.data1 = cbe_cpu_to_node(cpu); | |
58 | pmi_msg.data2 = pmode; | |
59 | ||
60 | #ifdef DEBUG | |
61 | time = jiffies; | |
62 | #endif | |
63 | pmi_send_message(pmi_msg); | |
64 | ||
65 | #ifdef DEBUG | |
66 | time = jiffies - time; | |
67 | time = jiffies_to_msecs(time); | |
68 | pr_debug("had to wait %lu ms for a transition using " \ | |
69 | "PMI\n", time); | |
70 | #endif | |
71 | ret = pmi_msg.data2; | |
72 | pr_debug("PMI returned slow mode %d\n", ret); | |
73 | ||
74 | return ret; | |
75 | } | |
76 | EXPORT_SYMBOL_GPL(cbe_cpufreq_set_pmode_pmi); | |
77 | ||
78 | ||
79 | static void cbe_cpufreq_handle_pmi(pmi_message_t pmi_msg) | |
80 | { | |
81 | u8 node, slow_mode; | |
82 | ||
83 | BUG_ON(pmi_msg.type != PMI_TYPE_FREQ_CHANGE); | |
84 | ||
85 | node = pmi_msg.data1; | |
86 | slow_mode = pmi_msg.data2; | |
87 | ||
88 | pmi_slow_mode_limit[node] = slow_mode; | |
89 | ||
90 | pr_debug("cbe_handle_pmi: node: %d max_freq: %d\n", node, slow_mode); | |
91 | } | |
92 | ||
93 | static int pmi_notifier(struct notifier_block *nb, | |
94 | unsigned long event, void *data) | |
95 | { | |
96 | struct cpufreq_policy *policy = data; | |
97 | struct cpufreq_frequency_table *cbe_freqs; | |
98 | u8 node; | |
99 | ||
6bfb7c74 VK |
100 | /* Should this really be called for CPUFREQ_ADJUST and CPUFREQ_NOTIFY |
101 | * policy events?) | |
a1531acd TR |
102 | */ |
103 | if (event == CPUFREQ_START) | |
104 | return 0; | |
105 | ||
74889e41 CK |
106 | cbe_freqs = cpufreq_frequency_get_table(policy->cpu); |
107 | node = cbe_cpu_to_node(policy->cpu); | |
108 | ||
109 | pr_debug("got notified, event=%lu, node=%u\n", event, node); | |
110 | ||
111 | if (pmi_slow_mode_limit[node] != 0) { | |
112 | pr_debug("limiting node %d to slow mode %d\n", | |
113 | node, pmi_slow_mode_limit[node]); | |
114 | ||
115 | cpufreq_verify_within_limits(policy, 0, | |
116 | ||
117 | cbe_freqs[pmi_slow_mode_limit[node]].frequency); | |
118 | } | |
119 | ||
120 | return 0; | |
121 | } | |
122 | ||
123 | static struct notifier_block pmi_notifier_block = { | |
124 | .notifier_call = pmi_notifier, | |
125 | }; | |
126 | ||
127 | static struct pmi_handler cbe_pmi_handler = { | |
128 | .type = PMI_TYPE_FREQ_CHANGE, | |
129 | .handle_pmi_message = cbe_cpufreq_handle_pmi, | |
130 | }; | |
131 | ||
132 | ||
133 | ||
134 | static int __init cbe_cpufreq_pmi_init(void) | |
135 | { | |
136 | cbe_cpufreq_has_pmi = pmi_register_handler(&cbe_pmi_handler) == 0; | |
137 | ||
138 | if (!cbe_cpufreq_has_pmi) | |
139 | return -ENODEV; | |
140 | ||
141 | cpufreq_register_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER); | |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static void __exit cbe_cpufreq_pmi_exit(void) | |
147 | { | |
148 | cpufreq_unregister_notifier(&pmi_notifier_block, CPUFREQ_POLICY_NOTIFIER); | |
149 | pmi_unregister_handler(&cbe_pmi_handler); | |
150 | } | |
151 | ||
152 | module_init(cbe_cpufreq_pmi_init); | |
153 | module_exit(cbe_cpufreq_pmi_exit); | |
154 | ||
155 | MODULE_LICENSE("GPL"); | |
156 | MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); |