- Update Xen patches to 3.3-rc5 and c/s 1157.
[linux-flexiantxendom0-3.2.10.git] / drivers / acpi / processor_extcntl.c
1 /*
2  * processor_extcntl.c - channel to external control logic
3  *
4  *  Copyright (C) 2008, Intel corporation
5  *
6  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 of the License, or (at
11  *  your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful, but
14  *  WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License along
19  *  with this program; if not, write to the Free Software Foundation, Inc.,
20  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
21  *
22  */
23
24 #include <linux/kernel.h>
25 #include <linux/init.h>
26 #include <linux/types.h>
27 #include <linux/acpi.h>
28 #include <linux/pm.h>
29 #include <linux/cpu.h>
30
31 #include <acpi/processor.h>
32
33 #define ACPI_PROCESSOR_CLASS            "processor"
34 #define _COMPONENT              ACPI_PROCESSOR_COMPONENT
35 ACPI_MODULE_NAME("processor_extcntl")
36
37 static int processor_extcntl_parse_csd(struct acpi_processor *pr);
38 static int processor_extcntl_get_performance(struct acpi_processor *pr);
39
40 static int processor_notify_smm(void)
41 {
42         acpi_status status;
43         static int is_done = 0;
44
45         /* only need successfully notify BIOS once */
46         /* avoid double notification which may lead to unexpected result */
47         if (is_done)
48                 return 0;
49
50         /* Can't write pstate_cnt to smi_cmd if either value is zero */
51         if (!acpi_gbl_FADT.smi_command || !acpi_gbl_FADT.pstate_control) {
52                 ACPI_DEBUG_PRINT((ACPI_DB_INFO,"No SMI port or pstate_cnt\n"));
53                 return 0;
54         }
55
56         ACPI_DEBUG_PRINT((ACPI_DB_INFO,
57                 "Writing pstate_cnt [0x%x] to smi_cmd [0x%x]\n",
58                 acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command));
59
60         status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
61                                     acpi_gbl_FADT.pstate_control, 8);
62         if (ACPI_FAILURE(status))
63                 return status;
64
65         is_done = 1;
66
67         return 0;
68 }
69
70 int processor_notify_external(struct acpi_processor *pr, int event, int type)
71 {
72         int ret = -EINVAL;
73
74         if (!processor_cntl_external())
75                 return -EINVAL;
76
77         switch (event) {
78         case PROCESSOR_PM_INIT:
79         case PROCESSOR_PM_CHANGE:
80                 if ((type >= PM_TYPE_MAX) ||
81                         !processor_extcntl_ops->pm_ops[type])
82                         break;
83
84                 ret = processor_extcntl_ops->pm_ops[type](pr, event);
85                 break;
86 #ifdef CONFIG_ACPI_HOTPLUG_CPU
87         case PROCESSOR_HOTPLUG:
88                 if (processor_extcntl_ops->hotplug)
89                         ret = processor_extcntl_ops->hotplug(pr, type);
90                 xen_pcpu_hotplug(type);
91                 break;
92 #endif
93         default:
94                 pr_err("Unsupported processor event %d.\n", event);
95                 break;
96         }
97
98         return ret;
99 }
100
101 /*
102  * This is called from ACPI processor init, and targeted to hold
103  * some tricky housekeeping jobs to satisfy external control model.
104  * For example, we may put dependency parse stub here for idle
105  * and performance state. Those information may be not available
106  * if splitting from dom0 control logic like cpufreq driver.
107  */
108 int processor_extcntl_prepare(struct acpi_processor *pr)
109 {
110         /* parse cstate dependency information */
111         if (processor_pm_external())
112                 processor_extcntl_parse_csd(pr);
113
114         /* Initialize performance states */
115         if (processor_pmperf_external())
116                 processor_extcntl_get_performance(pr);
117
118         return 0;
119 }
120
121 /*
122  * Currently no _CSD is implemented which is why existing ACPI code
123  * doesn't parse _CSD at all. But to keep interface complete with
124  * external control logic, we put a placeholder here for future
125  * compatibility.
126  */
127 static int processor_extcntl_parse_csd(struct acpi_processor *pr)
128 {
129         int i;
130
131         for (i = 0; i < pr->power.count; i++) {
132                 if (!pr->power.states[i].valid)
133                         continue;
134
135                 /* No dependency by default */
136                 pr->power.states[i].domain_info = NULL;
137                 pr->power.states[i].csd_count = 0;
138         }
139
140         return 0;
141 }
142
143 /*
144  * Existing ACPI module does parse performance states at some point,
145  * when acpi-cpufreq driver is loaded which however is something
146  * we'd like to disable to avoid confliction with external control
147  * logic. So we have to collect raw performance information here
148  * when ACPI processor object is found and started.
149  */
150 static int processor_extcntl_get_performance(struct acpi_processor *pr)
151 {
152         int ret;
153         struct acpi_processor_performance *perf;
154         struct acpi_psd_package *pdomain;
155
156         if (pr->performance)
157                 return -EBUSY;
158
159         perf = kzalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL);
160         if (!perf)
161                 return -ENOMEM;
162
163         pr->performance = perf;
164         /* Get basic performance state information */
165         ret = acpi_processor_get_performance_info(pr);
166         if (ret < 0)
167                 goto err_out;
168
169         /*
170          * Well, here we need retrieve performance dependency information
171          * from _PSD object. The reason why existing interface is not used
172          * is due to the reason that existing interface sticks to Linux cpu
173          * id to construct some bitmap, however we want to split ACPI
174          * processor objects from Linux cpu id logic. For example, even
175          * when Linux is configured as UP, we still want to parse all ACPI
176          * processor objects to external logic. In this case, it's preferred
177          * to use ACPI ID instead.
178          */
179         pdomain = &pr->performance->domain_info;
180         pdomain->num_processors = 0;
181         ret = acpi_processor_get_psd(pr);
182         if (ret < 0) {
183                 /*
184                  * _PSD is optional - assume no coordination if absent (or
185                  * broken), matching native kernels' behavior.
186                  */
187                 pdomain->num_entries = ACPI_PSD_REV0_ENTRIES;
188                 pdomain->revision = ACPI_PSD_REV0_REVISION;
189                 pdomain->domain = pr->acpi_id;
190                 pdomain->coord_type = DOMAIN_COORD_TYPE_SW_ALL;
191                 pdomain->num_processors = 1;
192         }
193
194         /* Some sanity check */
195         if ((pdomain->revision != ACPI_PSD_REV0_REVISION) ||
196             (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) ||
197             ((pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL) &&
198              (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY) &&
199              (pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL))) {
200                 ret = -EINVAL;
201                 goto err_out;
202         }
203
204         /* Last step is to notify BIOS that external logic exists */
205         processor_notify_smm();
206
207         processor_notify_external(pr, PROCESSOR_PM_INIT, PM_TYPE_PERF);
208
209         return 0;
210 err_out:
211         pr->performance = NULL;
212         kfree(perf);
213         return ret;
214 }