2 * processor_extcntl.c - channel to external control logic
4 * Copyright (C) 2008, Intel corporation
6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
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.
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.
24 #include <linux/kernel.h>
25 #include <linux/init.h>
26 #include <linux/types.h>
27 #include <linux/acpi.h>
29 #include <linux/cpu.h>
31 #include <acpi/processor.h>
33 #define ACPI_PROCESSOR_CLASS "processor"
34 #define _COMPONENT ACPI_PROCESSOR_COMPONENT
35 ACPI_MODULE_NAME("processor_extcntl")
37 static int processor_extcntl_parse_csd(struct acpi_processor *pr);
38 static int processor_extcntl_get_performance(struct acpi_processor *pr);
40 static int processor_notify_smm(void)
43 static int is_done = 0;
45 /* only need successfully notify BIOS once */
46 /* avoid double notification which may lead to unexpected result */
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"));
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));
60 status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
61 acpi_gbl_FADT.pstate_control, 8);
62 if (ACPI_FAILURE(status))
70 int processor_notify_external(struct acpi_processor *pr, int event, int type)
74 if (!processor_cntl_external())
78 case PROCESSOR_PM_INIT:
79 case PROCESSOR_PM_CHANGE:
80 if ((type >= PM_TYPE_MAX) ||
81 !processor_extcntl_ops->pm_ops[type])
84 ret = processor_extcntl_ops->pm_ops[type](pr, event);
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);
94 pr_err("Unsupported processor event %d.\n", event);
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.
108 int processor_extcntl_prepare(struct acpi_processor *pr)
110 /* parse cstate dependency information */
111 if (processor_pm_external())
112 processor_extcntl_parse_csd(pr);
114 /* Initialize performance states */
115 if (processor_pmperf_external())
116 processor_extcntl_get_performance(pr);
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
127 static int processor_extcntl_parse_csd(struct acpi_processor *pr)
131 for (i = 0; i < pr->power.count; i++) {
132 if (!pr->power.states[i].valid)
135 /* No dependency by default */
136 pr->power.states[i].domain_info = NULL;
137 pr->power.states[i].csd_count = 0;
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.
150 static int processor_extcntl_get_performance(struct acpi_processor *pr)
153 struct acpi_processor_performance *perf;
154 struct acpi_psd_package *pdomain;
159 perf = kzalloc(sizeof(struct acpi_processor_performance), GFP_KERNEL);
163 pr->performance = perf;
164 /* Get basic performance state information */
165 ret = acpi_processor_get_performance_info(pr);
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.
179 pdomain = &pr->performance->domain_info;
180 pdomain->num_processors = 0;
181 ret = acpi_processor_get_psd(pr);
184 * _PSD is optional - assume no coordination if absent (or
185 * broken), matching native kernels' behavior.
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;
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))) {
204 /* Last step is to notify BIOS that external logic exists */
205 processor_notify_smm();
207 processor_notify_external(pr, PROCESSOR_PM_INIT, PM_TYPE_PERF);
211 pr->performance = NULL;