1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $)
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
61da177e4SLinus Torvalds  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
71da177e4SLinus Torvalds  *  Copyright (C) 2004       Dominik Brodowski <linux@brodo.de>
81da177e4SLinus Torvalds  *  Copyright (C) 2004  Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
91da177e4SLinus Torvalds  *  			- Added processor hotplug support
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
126183a684SHanjun Guo #define pr_fmt(fmt) "ACPI: " fmt
136183a684SHanjun Guo 
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/module.h>
161da177e4SLinus Torvalds #include <linux/init.h>
171da177e4SLinus Torvalds #include <linux/cpufreq.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
198b48463fSLv Zheng #include <linux/acpi.h>
208b48463fSLv Zheng #include <acpi/processor.h>
2116be87eaSMiao Xie #ifdef CONFIG_X86
22910dfae2SThomas Renninger #include <asm/cpufeature.h>
2316be87eaSMiao Xie #endif
241da177e4SLinus Torvalds 
251da177e4SLinus Torvalds #define ACPI_PROCESSOR_FILE_PERFORMANCE	"performance"
261da177e4SLinus Torvalds 
2765c19bbdSArjan van de Ven static DEFINE_MUTEX(performance_mutex);
281da177e4SLinus Torvalds 
291da177e4SLinus Torvalds /*
301da177e4SLinus Torvalds  * _PPC support is implemented as a CPUfreq policy notifier:
311da177e4SLinus Torvalds  * This means each time a CPUfreq driver registered also with
321da177e4SLinus Torvalds  * the ACPI core is asked to change the speed policy, the maximum
331da177e4SLinus Torvalds  * value is adjusted so that it is within the platform limit.
341da177e4SLinus Torvalds  *
351da177e4SLinus Torvalds  * Also, when a new platform limit value is detected, the CPUfreq
361da177e4SLinus Torvalds  * policy is adjusted accordingly.
371da177e4SLinus Torvalds  */
381da177e4SLinus Torvalds 
39a1531acdSThomas Renninger /* ignore_ppc:
40a1531acdSThomas Renninger  * -1 -> cpufreq low level drivers not initialized -> _PSS, etc. not called yet
41a1531acdSThomas Renninger  *       ignore _PPC
42a1531acdSThomas Renninger  *  0 -> cpufreq low level drivers initialized -> consider _PPC values
43a1531acdSThomas Renninger  *  1 -> ignore _PPC totally -> forced by user through boot param
44a1531acdSThomas Renninger  */
459f497bccSMilan Broz static int ignore_ppc = -1;
46613e5f33SMilan Broz module_param(ignore_ppc, int, 0644);
47623b78c3SThomas Renninger MODULE_PARM_DESC(ignore_ppc, "If the frequency of your machine gets wrongly" \
48623b78c3SThomas Renninger 		 "limited by BIOS, this should help");
49623b78c3SThomas Renninger 
50d15ce412SViresh Kumar static bool acpi_processor_ppc_in_use;
511da177e4SLinus Torvalds 
acpi_processor_get_platform_limit(struct acpi_processor * pr)524be44fcdSLen Brown static int acpi_processor_get_platform_limit(struct acpi_processor *pr)
531da177e4SLinus Torvalds {
541da177e4SLinus Torvalds 	acpi_status status = 0;
5527663c58SMatthew Wilcox 	unsigned long long ppc = 0;
56c02d5febSRafael J. Wysocki 	s32 qos_value;
57c02d5febSRafael J. Wysocki 	int index;
58d15ce412SViresh Kumar 	int ret;
591da177e4SLinus Torvalds 
601da177e4SLinus Torvalds 	if (!pr)
61d550d98dSPatrick Mochel 		return -EINVAL;
621da177e4SLinus Torvalds 
631da177e4SLinus Torvalds 	/*
641da177e4SLinus Torvalds 	 * _PPC indicates the maximum state currently supported by the platform
651da177e4SLinus Torvalds 	 * (e.g. 0 = states 0..n; 1 = states 1..n; etc.
661da177e4SLinus Torvalds 	 */
671da177e4SLinus Torvalds 	status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc);
682c25fabdSRafael J. Wysocki 	if (status != AE_NOT_FOUND) {
69d15ce412SViresh Kumar 		acpi_processor_ppc_in_use = true;
701da177e4SLinus Torvalds 
712c25fabdSRafael J. Wysocki 		if (ACPI_FAILURE(status)) {
724c324548SRafael J. Wysocki 			acpi_evaluation_failure_warn(pr->handle, "_PPC", status);
73d550d98dSPatrick Mochel 			return -ENODEV;
741da177e4SLinus Torvalds 		}
752c25fabdSRafael J. Wysocki 	}
761da177e4SLinus Torvalds 
77c02d5febSRafael J. Wysocki 	index = ppc;
78919158d1SThomas Renninger 
79*99387b01SRafael J. Wysocki 	if (pr->performance_platform_limit == index ||
80*99387b01SRafael J. Wysocki 	    ppc >= pr->performance->state_count)
81*99387b01SRafael J. Wysocki 		return 0;
82*99387b01SRafael J. Wysocki 
83c02d5febSRafael J. Wysocki 	pr_debug("CPU %d: _PPC is %d - frequency %s limited\n", pr->id,
84c02d5febSRafael J. Wysocki 		 index, index ? "is" : "is not");
85c02d5febSRafael J. Wysocki 
86c02d5febSRafael J. Wysocki 	pr->performance_platform_limit = index;
871da177e4SLinus Torvalds 
88*99387b01SRafael J. Wysocki 	if (unlikely(!freq_qos_request_active(&pr->perflib_req)))
89d15ce412SViresh Kumar 		return 0;
90d15ce412SViresh Kumar 
91c02d5febSRafael J. Wysocki 	/*
92c02d5febSRafael J. Wysocki 	 * If _PPC returns 0, it means that all of the available states can be
93c02d5febSRafael J. Wysocki 	 * used ("no limit").
94c02d5febSRafael J. Wysocki 	 */
95c02d5febSRafael J. Wysocki 	if (index == 0)
96c02d5febSRafael J. Wysocki 		qos_value = FREQ_QOS_MAX_DEFAULT_VALUE;
97c02d5febSRafael J. Wysocki 	else
98c02d5febSRafael J. Wysocki 		qos_value = pr->performance->states[index].core_frequency * 1000;
99c02d5febSRafael J. Wysocki 
100c02d5febSRafael J. Wysocki 	ret = freq_qos_update_request(&pr->perflib_req, qos_value);
101d15ce412SViresh Kumar 	if (ret < 0) {
102d15ce412SViresh Kumar 		pr_warn("Failed to update perflib freq constraint: CPU%d (%d)\n",
103d15ce412SViresh Kumar 			pr->id, ret);
104d15ce412SViresh Kumar 	}
105d15ce412SViresh Kumar 
106d550d98dSPatrick Mochel 	return 0;
1071da177e4SLinus Torvalds }
1081da177e4SLinus Torvalds 
109d81c45e1SZhao Yakui #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE	0x80
110d81c45e1SZhao Yakui /*
111d81c45e1SZhao Yakui  * acpi_processor_ppc_ost: Notify firmware the _PPC evaluation status
112d81c45e1SZhao Yakui  * @handle: ACPI processor handle
113d81c45e1SZhao Yakui  * @status: the status code of _PPC evaluation
114935ab850STom Saeger  *	0: success. OSPM is now using the performance state specified.
115d81c45e1SZhao Yakui  *	1: failure. OSPM has not changed the number of P-states in use
116d81c45e1SZhao Yakui  */
acpi_processor_ppc_ost(acpi_handle handle,int status)117d81c45e1SZhao Yakui static void acpi_processor_ppc_ost(acpi_handle handle, int status)
118d81c45e1SZhao Yakui {
1194a6172a4SJiang Liu 	if (acpi_has_method(handle, "_OST"))
1204a6172a4SJiang Liu 		acpi_evaluate_ost(handle, ACPI_PROCESSOR_NOTIFY_PERFORMANCE,
1214a6172a4SJiang Liu 				  status, NULL);
122d81c45e1SZhao Yakui }
123d81c45e1SZhao Yakui 
acpi_processor_ppc_has_changed(struct acpi_processor * pr,int event_flag)124bca5f557SRafael J. Wysocki void acpi_processor_ppc_has_changed(struct acpi_processor *pr, int event_flag)
1251da177e4SLinus Torvalds {
126623b78c3SThomas Renninger 	int ret;
127623b78c3SThomas Renninger 
128ba1edb9aSChen Yu 	if (ignore_ppc || !pr->performance) {
129d81c45e1SZhao Yakui 		/*
130d81c45e1SZhao Yakui 		 * Only when it is notification event, the _OST object
131d81c45e1SZhao Yakui 		 * will be evaluated. Otherwise it is skipped.
132d81c45e1SZhao Yakui 		 */
133d81c45e1SZhao Yakui 		if (event_flag)
134d81c45e1SZhao Yakui 			acpi_processor_ppc_ost(pr->handle, 1);
135bca5f557SRafael J. Wysocki 		return;
136d81c45e1SZhao Yakui 	}
137623b78c3SThomas Renninger 
138623b78c3SThomas Renninger 	ret = acpi_processor_get_platform_limit(pr);
139d81c45e1SZhao Yakui 	/*
140d81c45e1SZhao Yakui 	 * Only when it is notification event, the _OST object
141d81c45e1SZhao Yakui 	 * will be evaluated. Otherwise it is skipped.
142d81c45e1SZhao Yakui 	 */
143d81c45e1SZhao Yakui 	if (event_flag) {
144d81c45e1SZhao Yakui 		if (ret < 0)
145d81c45e1SZhao Yakui 			acpi_processor_ppc_ost(pr->handle, 1);
146d81c45e1SZhao Yakui 		else
147d81c45e1SZhao Yakui 			acpi_processor_ppc_ost(pr->handle, 0);
148d81c45e1SZhao Yakui 	}
149bca5f557SRafael J. Wysocki 	if (ret >= 0)
1505a25e3f7SRafael J. Wysocki 		cpufreq_update_limits(pr->id);
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds 
acpi_processor_get_bios_limit(int cpu,unsigned int * limit)153e2f74f35SThomas Renninger int acpi_processor_get_bios_limit(int cpu, unsigned int *limit)
154e2f74f35SThomas Renninger {
155e2f74f35SThomas Renninger 	struct acpi_processor *pr;
156e2f74f35SThomas Renninger 
157e2f74f35SThomas Renninger 	pr = per_cpu(processors, cpu);
158e2f74f35SThomas Renninger 	if (!pr || !pr->performance || !pr->performance->state_count)
159e2f74f35SThomas Renninger 		return -ENODEV;
1603d9e9a96SRafael J. Wysocki 
161e2f74f35SThomas Renninger 	*limit = pr->performance->states[pr->performance_platform_limit].
162e2f74f35SThomas Renninger 		core_frequency * 1000;
163e2f74f35SThomas Renninger 	return 0;
164e2f74f35SThomas Renninger }
165e2f74f35SThomas Renninger EXPORT_SYMBOL(acpi_processor_get_bios_limit);
166e2f74f35SThomas Renninger 
acpi_processor_ignore_ppc_init(void)167d15ce412SViresh Kumar void acpi_processor_ignore_ppc_init(void)
1684be44fcdSLen Brown {
169d15ce412SViresh Kumar 	if (ignore_ppc < 0)
170d15ce412SViresh Kumar 		ignore_ppc = 0;
1711da177e4SLinus Torvalds }
1721da177e4SLinus Torvalds 
acpi_processor_ppc_init(struct cpufreq_policy * policy)1733000ce3cSRafael J. Wysocki void acpi_processor_ppc_init(struct cpufreq_policy *policy)
1744be44fcdSLen Brown {
175a1bb46c3SRafael J. Wysocki 	unsigned int cpu;
176a1bb46c3SRafael J. Wysocki 
177a1bb46c3SRafael J. Wysocki 	for_each_cpu(cpu, policy->related_cpus) {
178d15ce412SViresh Kumar 		struct acpi_processor *pr = per_cpu(processors, cpu);
179d15ce412SViresh Kumar 		int ret;
1801da177e4SLinus Torvalds 
1812d8b39a6SRafael J. Wysocki 		if (!pr)
182a1bb46c3SRafael J. Wysocki 			continue;
1832d8b39a6SRafael J. Wysocki 
184*99387b01SRafael J. Wysocki 		/*
185*99387b01SRafael J. Wysocki 		 * Reset performance_platform_limit in case there is a stale
186*99387b01SRafael J. Wysocki 		 * value in it, so as to make it match the "no limit" QoS value
187*99387b01SRafael J. Wysocki 		 * below.
188*99387b01SRafael J. Wysocki 		 */
189*99387b01SRafael J. Wysocki 		pr->performance_platform_limit = 0;
190*99387b01SRafael J. Wysocki 
191a1bb46c3SRafael J. Wysocki 		ret = freq_qos_add_request(&policy->constraints,
192*99387b01SRafael J. Wysocki 					   &pr->perflib_req, FREQ_QOS_MAX,
193*99387b01SRafael J. Wysocki 					   FREQ_QOS_MAX_DEFAULT_VALUE);
1942d8b39a6SRafael J. Wysocki 		if (ret < 0)
195a1bb46c3SRafael J. Wysocki 			pr_err("Failed to add freq constraint for CPU%d (%d)\n",
196a1bb46c3SRafael J. Wysocki 			       cpu, ret);
197a1bb46c3SRafael J. Wysocki 	}
198d15ce412SViresh Kumar }
199d15ce412SViresh Kumar 
acpi_processor_ppc_exit(struct cpufreq_policy * policy)2003000ce3cSRafael J. Wysocki void acpi_processor_ppc_exit(struct cpufreq_policy *policy)
201d15ce412SViresh Kumar {
202a1bb46c3SRafael J. Wysocki 	unsigned int cpu;
203a1bb46c3SRafael J. Wysocki 
204a1bb46c3SRafael J. Wysocki 	for_each_cpu(cpu, policy->related_cpus) {
205a1bb46c3SRafael J. Wysocki 		struct acpi_processor *pr = per_cpu(processors, cpu);
206d15ce412SViresh Kumar 
2072d8b39a6SRafael J. Wysocki 		if (pr)
2083000ce3cSRafael J. Wysocki 			freq_qos_remove_request(&pr->perflib_req);
2091da177e4SLinus Torvalds 	}
210a1bb46c3SRafael J. Wysocki }
2111da177e4SLinus Torvalds 
acpi_processor_get_performance_control(struct acpi_processor * pr)2124be44fcdSLen Brown static int acpi_processor_get_performance_control(struct acpi_processor *pr)
2131da177e4SLinus Torvalds {
2141da177e4SLinus Torvalds 	int result = 0;
2151da177e4SLinus Torvalds 	acpi_status status = 0;
2161da177e4SLinus Torvalds 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
2171da177e4SLinus Torvalds 	union acpi_object *pct = NULL;
2181da177e4SLinus Torvalds 	union acpi_object obj = { 0 };
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds 	status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer);
2211da177e4SLinus Torvalds 	if (ACPI_FAILURE(status)) {
2224c324548SRafael J. Wysocki 		acpi_evaluation_failure_warn(pr->handle, "_PCT", status);
223d550d98dSPatrick Mochel 		return -ENODEV;
2241da177e4SLinus Torvalds 	}
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 	pct = (union acpi_object *)buffer.pointer;
227d8f4ed07SRafael J. Wysocki 	if (!pct || pct->type != ACPI_TYPE_PACKAGE || pct->package.count != 2) {
2286183a684SHanjun Guo 		pr_err("Invalid _PCT data\n");
2291da177e4SLinus Torvalds 		result = -EFAULT;
2301da177e4SLinus Torvalds 		goto end;
2311da177e4SLinus Torvalds 	}
2321da177e4SLinus Torvalds 
2331da177e4SLinus Torvalds 	/*
2341da177e4SLinus Torvalds 	 * control_register
2351da177e4SLinus Torvalds 	 */
2361da177e4SLinus Torvalds 
2371da177e4SLinus Torvalds 	obj = pct->package.elements[0];
2381da177e4SLinus Torvalds 
239d8f4ed07SRafael J. Wysocki 	if (!obj.buffer.pointer || obj.type != ACPI_TYPE_BUFFER ||
240d8f4ed07SRafael J. Wysocki 	    obj.buffer.length < sizeof(struct acpi_pct_register)) {
2416183a684SHanjun Guo 		pr_err("Invalid _PCT data (control_register)\n");
2421da177e4SLinus Torvalds 		result = -EFAULT;
2431da177e4SLinus Torvalds 		goto end;
2441da177e4SLinus Torvalds 	}
2454be44fcdSLen Brown 	memcpy(&pr->performance->control_register, obj.buffer.pointer,
2464be44fcdSLen Brown 	       sizeof(struct acpi_pct_register));
2471da177e4SLinus Torvalds 
2481da177e4SLinus Torvalds 	/*
2491da177e4SLinus Torvalds 	 * status_register
2501da177e4SLinus Torvalds 	 */
2511da177e4SLinus Torvalds 
2521da177e4SLinus Torvalds 	obj = pct->package.elements[1];
2531da177e4SLinus Torvalds 
254d8f4ed07SRafael J. Wysocki 	if (!obj.buffer.pointer || obj.type != ACPI_TYPE_BUFFER ||
255d8f4ed07SRafael J. Wysocki 	    obj.buffer.length < sizeof(struct acpi_pct_register)) {
2566183a684SHanjun Guo 		pr_err("Invalid _PCT data (status_register)\n");
2571da177e4SLinus Torvalds 		result = -EFAULT;
2581da177e4SLinus Torvalds 		goto end;
2591da177e4SLinus Torvalds 	}
2601da177e4SLinus Torvalds 
2614be44fcdSLen Brown 	memcpy(&pr->performance->status_register, obj.buffer.pointer,
2624be44fcdSLen Brown 	       sizeof(struct acpi_pct_register));
2631da177e4SLinus Torvalds 
2641da177e4SLinus Torvalds end:
26502438d87SLen Brown 	kfree(buffer.pointer);
2661da177e4SLinus Torvalds 
267d550d98dSPatrick Mochel 	return result;
2681da177e4SLinus Torvalds }
2691da177e4SLinus Torvalds 
270f594065fSMatthew Garrett #ifdef CONFIG_X86
271f594065fSMatthew Garrett /*
272f594065fSMatthew Garrett  * Some AMDs have 50MHz frequency multiples, but only provide 100MHz rounding
273f594065fSMatthew Garrett  * in their ACPI data. Calculate the real values and fix up the _PSS data.
274f594065fSMatthew Garrett  */
amd_fixup_frequency(struct acpi_processor_px * px,int i)275f594065fSMatthew Garrett static void amd_fixup_frequency(struct acpi_processor_px *px, int i)
276f594065fSMatthew Garrett {
277f594065fSMatthew Garrett 	u32 hi, lo, fid, did;
278f594065fSMatthew Garrett 	int index = px->control & 0x00000007;
279f594065fSMatthew Garrett 
280f594065fSMatthew Garrett 	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
281f594065fSMatthew Garrett 		return;
282f594065fSMatthew Garrett 
2833d9e9a96SRafael J. Wysocki 	if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) ||
2843d9e9a96SRafael J. Wysocki 	    boot_cpu_data.x86 == 0x11) {
285f594065fSMatthew Garrett 		rdmsr(MSR_AMD_PSTATE_DEF_BASE + index, lo, hi);
2869855d8ceSStefan Bader 		/*
2879855d8ceSStefan Bader 		 * MSR C001_0064+:
2889855d8ceSStefan Bader 		 * Bit 63: PstateEn. Read-write. If set, the P-state is valid.
2899855d8ceSStefan Bader 		 */
2909855d8ceSStefan Bader 		if (!(hi & BIT(31)))
2919855d8ceSStefan Bader 			return;
2929855d8ceSStefan Bader 
293f594065fSMatthew Garrett 		fid = lo & 0x3f;
294f594065fSMatthew Garrett 		did = (lo >> 6) & 7;
295f594065fSMatthew Garrett 		if (boot_cpu_data.x86 == 0x10)
296f594065fSMatthew Garrett 			px->core_frequency = (100 * (fid + 0x10)) >> did;
297f594065fSMatthew Garrett 		else
298f594065fSMatthew Garrett 			px->core_frequency = (100 * (fid + 8)) >> did;
299f594065fSMatthew Garrett 	}
300f594065fSMatthew Garrett }
301f594065fSMatthew Garrett #else
amd_fixup_frequency(struct acpi_processor_px * px,int i)302f594065fSMatthew Garrett static void amd_fixup_frequency(struct acpi_processor_px *px, int i) {};
303f594065fSMatthew Garrett #endif
304f594065fSMatthew Garrett 
acpi_processor_get_performance_states(struct acpi_processor * pr)3054be44fcdSLen Brown static int acpi_processor_get_performance_states(struct acpi_processor *pr)
3061da177e4SLinus Torvalds {
3071da177e4SLinus Torvalds 	int result = 0;
3081da177e4SLinus Torvalds 	acpi_status status = AE_OK;
3091da177e4SLinus Torvalds 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
3101da177e4SLinus Torvalds 	struct acpi_buffer format = { sizeof("NNNNNN"), "NNNNNN" };
3111da177e4SLinus Torvalds 	struct acpi_buffer state = { 0, NULL };
3121da177e4SLinus Torvalds 	union acpi_object *pss = NULL;
3131da177e4SLinus Torvalds 	int i;
314d8e725f3SMarco Aurelio da Costa 	int last_invalid = -1;
3151da177e4SLinus Torvalds 
3161da177e4SLinus Torvalds 	status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer);
3171da177e4SLinus Torvalds 	if (ACPI_FAILURE(status)) {
3184c324548SRafael J. Wysocki 		acpi_evaluation_failure_warn(pr->handle, "_PSS", status);
319d550d98dSPatrick Mochel 		return -ENODEV;
3201da177e4SLinus Torvalds 	}
3211da177e4SLinus Torvalds 
32250dd0969SJan Engelhardt 	pss = buffer.pointer;
323d8f4ed07SRafael J. Wysocki 	if (!pss || pss->type != ACPI_TYPE_PACKAGE) {
3246183a684SHanjun Guo 		pr_err("Invalid _PSS data\n");
3251da177e4SLinus Torvalds 		result = -EFAULT;
3261da177e4SLinus Torvalds 		goto end;
3271da177e4SLinus Torvalds 	}
3281da177e4SLinus Torvalds 
32952af99c3SRafael J. Wysocki 	acpi_handle_debug(pr->handle, "Found %d performance states\n",
33052af99c3SRafael J. Wysocki 			  pss->package.count);
3311da177e4SLinus Torvalds 
3321da177e4SLinus Torvalds 	pr->performance->state_count = pss->package.count;
3334be44fcdSLen Brown 	pr->performance->states =
3346da2ec56SKees Cook 	    kmalloc_array(pss->package.count,
3356da2ec56SKees Cook 			  sizeof(struct acpi_processor_px),
3364be44fcdSLen Brown 			  GFP_KERNEL);
3371da177e4SLinus Torvalds 	if (!pr->performance->states) {
3381da177e4SLinus Torvalds 		result = -ENOMEM;
3391da177e4SLinus Torvalds 		goto end;
3401da177e4SLinus Torvalds 	}
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds 	for (i = 0; i < pr->performance->state_count; i++) {
3431da177e4SLinus Torvalds 
3441da177e4SLinus Torvalds 		struct acpi_processor_px *px = &(pr->performance->states[i]);
3451da177e4SLinus Torvalds 
3461da177e4SLinus Torvalds 		state.length = sizeof(struct acpi_processor_px);
3471da177e4SLinus Torvalds 		state.pointer = px;
3481da177e4SLinus Torvalds 
34952af99c3SRafael J. Wysocki 		acpi_handle_debug(pr->handle, "Extracting state %d\n", i);
3501da177e4SLinus Torvalds 
3511da177e4SLinus Torvalds 		status = acpi_extract_package(&(pss->package.elements[i]),
3521da177e4SLinus Torvalds 					      &format, &state);
3531da177e4SLinus Torvalds 		if (ACPI_FAILURE(status)) {
35452af99c3SRafael J. Wysocki 			acpi_handle_warn(pr->handle, "Invalid _PSS data: %s\n",
35552af99c3SRafael J. Wysocki 					 acpi_format_exception(status));
3561da177e4SLinus Torvalds 			result = -EFAULT;
3571da177e4SLinus Torvalds 			kfree(pr->performance->states);
3581da177e4SLinus Torvalds 			goto end;
3591da177e4SLinus Torvalds 		}
3601da177e4SLinus Torvalds 
361f594065fSMatthew Garrett 		amd_fixup_frequency(px, i);
362f594065fSMatthew Garrett 
36352af99c3SRafael J. Wysocki 		acpi_handle_debug(pr->handle,
3641da177e4SLinus Torvalds 				  "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n",
3651da177e4SLinus Torvalds 				  i,
3661da177e4SLinus Torvalds 				  (u32) px->core_frequency,
3671da177e4SLinus Torvalds 				  (u32) px->power,
3681da177e4SLinus Torvalds 				  (u32) px->transition_latency,
3691da177e4SLinus Torvalds 				  (u32) px->bus_master_latency,
37052af99c3SRafael J. Wysocki 				  (u32) px->control, (u32) px->status);
3711da177e4SLinus Torvalds 
37234d531e6SLen Brown 		/*
37334d531e6SLen Brown 		 * Check that ACPI's u64 MHz will be valid as u32 KHz in cpufreq
37434d531e6SLen Brown 		 */
37534d531e6SLen Brown 		if (!px->core_frequency ||
376d8f4ed07SRafael J. Wysocki 		    (u32)(px->core_frequency * 1000) != px->core_frequency * 1000) {
3776183a684SHanjun Guo 			pr_err(FW_BUG
378d8e725f3SMarco Aurelio da Costa 			       "Invalid BIOS _PSS frequency found for processor %d: 0x%llx MHz\n",
379d8e725f3SMarco Aurelio da Costa 			       pr->id, px->core_frequency);
380d8e725f3SMarco Aurelio da Costa 			if (last_invalid == -1)
381d8e725f3SMarco Aurelio da Costa 				last_invalid = i;
382d8e725f3SMarco Aurelio da Costa 		} else {
383d8e725f3SMarco Aurelio da Costa 			if (last_invalid != -1) {
384d8e725f3SMarco Aurelio da Costa 				/*
385d8e725f3SMarco Aurelio da Costa 				 * Copy this valid entry over last_invalid entry
386d8e725f3SMarco Aurelio da Costa 				 */
387d8e725f3SMarco Aurelio da Costa 				memcpy(&(pr->performance->states[last_invalid]),
388d8e725f3SMarco Aurelio da Costa 				       px, sizeof(struct acpi_processor_px));
389d8e725f3SMarco Aurelio da Costa 				++last_invalid;
390d8e725f3SMarco Aurelio da Costa 			}
391d8e725f3SMarco Aurelio da Costa 		}
392d8e725f3SMarco Aurelio da Costa 	}
393d8e725f3SMarco Aurelio da Costa 
394d8e725f3SMarco Aurelio da Costa 	if (last_invalid == 0) {
3956183a684SHanjun Guo 		pr_err(FW_BUG
396d8e725f3SMarco Aurelio da Costa 			   "No valid BIOS _PSS frequency found for processor %d\n", pr->id);
3971da177e4SLinus Torvalds 		result = -EFAULT;
3981da177e4SLinus Torvalds 		kfree(pr->performance->states);
399d8e725f3SMarco Aurelio da Costa 		pr->performance->states = NULL;
4001da177e4SLinus Torvalds 	}
401d8e725f3SMarco Aurelio da Costa 
402d8e725f3SMarco Aurelio da Costa 	if (last_invalid > 0)
403d8e725f3SMarco Aurelio da Costa 		pr->performance->state_count = last_invalid;
4041da177e4SLinus Torvalds 
4051da177e4SLinus Torvalds end:
40602438d87SLen Brown 	kfree(buffer.pointer);
4071da177e4SLinus Torvalds 
408d550d98dSPatrick Mochel 	return result;
4091da177e4SLinus Torvalds }
4101da177e4SLinus Torvalds 
acpi_processor_get_performance_info(struct acpi_processor * pr)411c705c78cSKonrad Rzeszutek Wilk int acpi_processor_get_performance_info(struct acpi_processor *pr)
4121da177e4SLinus Torvalds {
4131da177e4SLinus Torvalds 	int result = 0;
4141da177e4SLinus Torvalds 
4151da177e4SLinus Torvalds 	if (!pr || !pr->performance || !pr->handle)
416d550d98dSPatrick Mochel 		return -EINVAL;
4171da177e4SLinus Torvalds 
418952c63e9SJiang Liu 	if (!acpi_has_method(pr->handle, "_PCT")) {
41952af99c3SRafael J. Wysocki 		acpi_handle_debug(pr->handle,
42052af99c3SRafael J. Wysocki 				  "ACPI-based processor performance control unavailable\n");
421d550d98dSPatrick Mochel 		return -ENODEV;
4221da177e4SLinus Torvalds 	}
4231da177e4SLinus Torvalds 
4241da177e4SLinus Torvalds 	result = acpi_processor_get_performance_control(pr);
4251da177e4SLinus Torvalds 	if (result)
426910dfae2SThomas Renninger 		goto update_bios;
4271da177e4SLinus Torvalds 
4281da177e4SLinus Torvalds 	result = acpi_processor_get_performance_states(pr);
4291da177e4SLinus Torvalds 	if (result)
430910dfae2SThomas Renninger 		goto update_bios;
4311da177e4SLinus Torvalds 
432455c0d71SDarrick J. Wong 	/* We need to call _PPC once when cpufreq starts */
433455c0d71SDarrick J. Wong 	if (ignore_ppc != 1)
434455c0d71SDarrick J. Wong 		result = acpi_processor_get_platform_limit(pr);
435455c0d71SDarrick J. Wong 
436455c0d71SDarrick J. Wong 	return result;
437910dfae2SThomas Renninger 
438910dfae2SThomas Renninger 	/*
439910dfae2SThomas Renninger 	 * Having _PPC but missing frequencies (_PSS, _PCT) is a very good hint that
440910dfae2SThomas Renninger 	 * the BIOS is older than the CPU and does not know its frequencies
441910dfae2SThomas Renninger 	 */
442910dfae2SThomas Renninger  update_bios:
44316be87eaSMiao Xie #ifdef CONFIG_X86
444952c63e9SJiang Liu 	if (acpi_has_method(pr->handle, "_PPC")) {
445910dfae2SThomas Renninger 		if(boot_cpu_has(X86_FEATURE_EST))
4466183a684SHanjun Guo 			pr_warn(FW_BUG "BIOS needs update for CPU "
447910dfae2SThomas Renninger 			       "frequency support\n");
448910dfae2SThomas Renninger 	}
44916be87eaSMiao Xie #endif
450910dfae2SThomas Renninger 	return result;
4511da177e4SLinus Torvalds }
452c705c78cSKonrad Rzeszutek Wilk EXPORT_SYMBOL_GPL(acpi_processor_get_performance_info);
453d0ea59e1SRafael J. Wysocki 
acpi_processor_pstate_control(void)454d0ea59e1SRafael J. Wysocki int acpi_processor_pstate_control(void)
4554be44fcdSLen Brown {
4561da177e4SLinus Torvalds 	acpi_status status;
4571da177e4SLinus Torvalds 
458d0ea59e1SRafael J. Wysocki 	if (!acpi_gbl_FADT.smi_command || !acpi_gbl_FADT.pstate_control)
459d0ea59e1SRafael J. Wysocki 		return 0;
460d0ea59e1SRafael J. Wysocki 
46152af99c3SRafael J. Wysocki 	pr_debug("Writing pstate_control [0x%x] to smi_command [0x%x]\n",
46252af99c3SRafael J. Wysocki 		 acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command);
463d0ea59e1SRafael J. Wysocki 
464d0ea59e1SRafael J. Wysocki 	status = acpi_os_write_port(acpi_gbl_FADT.smi_command,
465d0ea59e1SRafael J. Wysocki 				    (u32)acpi_gbl_FADT.pstate_control, 8);
466d0ea59e1SRafael J. Wysocki 	if (ACPI_SUCCESS(status))
467d0ea59e1SRafael J. Wysocki 		return 1;
468d0ea59e1SRafael J. Wysocki 
46952af99c3SRafael J. Wysocki 	pr_warn("Failed to write pstate_control [0x%x] to smi_command [0x%x]: %s\n",
47052af99c3SRafael J. Wysocki 		acpi_gbl_FADT.pstate_control, acpi_gbl_FADT.smi_command,
47152af99c3SRafael J. Wysocki 		acpi_format_exception(status));
472d0ea59e1SRafael J. Wysocki 	return -EIO;
473d0ea59e1SRafael J. Wysocki }
474d0ea59e1SRafael J. Wysocki 
acpi_processor_notify_smm(struct module * calling_module)475d0ea59e1SRafael J. Wysocki int acpi_processor_notify_smm(struct module *calling_module)
476d0ea59e1SRafael J. Wysocki {
47741103b3bSTian Tao 	static int is_done;
478be5c8a04SRafael J. Wysocki 	int result = 0;
4791da177e4SLinus Torvalds 
480d15ce412SViresh Kumar 	if (!acpi_processor_cpufreq_init)
481d550d98dSPatrick Mochel 		return -EBUSY;
4821da177e4SLinus Torvalds 
4831da177e4SLinus Torvalds 	if (!try_module_get(calling_module))
484d550d98dSPatrick Mochel 		return -EINVAL;
4851da177e4SLinus Torvalds 
486be5c8a04SRafael J. Wysocki 	/*
487be5c8a04SRafael J. Wysocki 	 * is_done is set to negative if an error occurs and to 1 if no error
488be5c8a04SRafael J. Wysocki 	 * occurrs, but SMM has been notified already. This avoids repeated
489be5c8a04SRafael J. Wysocki 	 * notification which might lead to unexpected results.
4901da177e4SLinus Torvalds 	 */
491be5c8a04SRafael J. Wysocki 	if (is_done != 0) {
492be5c8a04SRafael J. Wysocki 		if (is_done < 0)
493be5c8a04SRafael J. Wysocki 			result = is_done;
4941da177e4SLinus Torvalds 
495be5c8a04SRafael J. Wysocki 		goto out_put;
496be5c8a04SRafael J. Wysocki 	}
4971da177e4SLinus Torvalds 
498d0ea59e1SRafael J. Wysocki 	result = acpi_processor_pstate_control();
499be5c8a04SRafael J. Wysocki 	if (result <= 0) {
500f1a70bacSRafael J. Wysocki 		if (result) {
501f1a70bacSRafael J. Wysocki 			is_done = result;
502f1a70bacSRafael J. Wysocki 		} else {
50352af99c3SRafael J. Wysocki 			pr_debug("No SMI port or pstate_control\n");
504f1a70bacSRafael J. Wysocki 			is_done = 1;
505f1a70bacSRafael J. Wysocki 		}
506be5c8a04SRafael J. Wysocki 		goto out_put;
5071da177e4SLinus Torvalds 	}
508be5c8a04SRafael J. Wysocki 
509be5c8a04SRafael J. Wysocki 	is_done = 1;
510be5c8a04SRafael J. Wysocki 	/*
511be5c8a04SRafael J. Wysocki 	 * Success. If there _PPC, unloading the cpufreq driver would be risky,
512be5c8a04SRafael J. Wysocki 	 * so disallow it in that case.
513be5c8a04SRafael J. Wysocki 	 */
514be5c8a04SRafael J. Wysocki 	if (acpi_processor_ppc_in_use)
515be5c8a04SRafael J. Wysocki 		return 0;
516be5c8a04SRafael J. Wysocki 
517be5c8a04SRafael J. Wysocki out_put:
5181da177e4SLinus Torvalds 	module_put(calling_module);
519d0ea59e1SRafael J. Wysocki 	return result;
5201da177e4SLinus Torvalds }
5214be44fcdSLen Brown EXPORT_SYMBOL(acpi_processor_notify_smm);
5221da177e4SLinus Torvalds 
acpi_processor_get_psd(acpi_handle handle,struct acpi_psd_package * pdomain)5234d0f1ce6SJoao Martins int acpi_processor_get_psd(acpi_handle handle, struct acpi_psd_package *pdomain)
5243b2d9942SVenkatesh Pallipadi {
5253b2d9942SVenkatesh Pallipadi 	int result = 0;
5263b2d9942SVenkatesh Pallipadi 	acpi_status status = AE_OK;
5273b2d9942SVenkatesh Pallipadi 	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
5283b2d9942SVenkatesh Pallipadi 	struct acpi_buffer format = {sizeof("NNNNN"), "NNNNN"};
5293b2d9942SVenkatesh Pallipadi 	struct acpi_buffer state = {0, NULL};
5303b2d9942SVenkatesh Pallipadi 	union acpi_object  *psd = NULL;
5313b2d9942SVenkatesh Pallipadi 
5324d0f1ce6SJoao Martins 	status = acpi_evaluate_object(handle, "_PSD", NULL, &buffer);
5333b2d9942SVenkatesh Pallipadi 	if (ACPI_FAILURE(status)) {
5349011bff4SLen Brown 		return -ENODEV;
5353b2d9942SVenkatesh Pallipadi 	}
5363b2d9942SVenkatesh Pallipadi 
53750dd0969SJan Engelhardt 	psd = buffer.pointer;
538d8f4ed07SRafael J. Wysocki 	if (!psd || psd->type != ACPI_TYPE_PACKAGE) {
5396183a684SHanjun Guo 		pr_err("Invalid _PSD data\n");
5403b2d9942SVenkatesh Pallipadi 		result = -EFAULT;
5413b2d9942SVenkatesh Pallipadi 		goto end;
5423b2d9942SVenkatesh Pallipadi 	}
5433b2d9942SVenkatesh Pallipadi 
5443b2d9942SVenkatesh Pallipadi 	if (psd->package.count != 1) {
5456183a684SHanjun Guo 		pr_err("Invalid _PSD data\n");
5463b2d9942SVenkatesh Pallipadi 		result = -EFAULT;
5473b2d9942SVenkatesh Pallipadi 		goto end;
5483b2d9942SVenkatesh Pallipadi 	}
5493b2d9942SVenkatesh Pallipadi 
5503b2d9942SVenkatesh Pallipadi 	state.length = sizeof(struct acpi_psd_package);
5513b2d9942SVenkatesh Pallipadi 	state.pointer = pdomain;
5523b2d9942SVenkatesh Pallipadi 
5533d9e9a96SRafael J. Wysocki 	status = acpi_extract_package(&(psd->package.elements[0]), &format, &state);
5543b2d9942SVenkatesh Pallipadi 	if (ACPI_FAILURE(status)) {
5556183a684SHanjun Guo 		pr_err("Invalid _PSD data\n");
5563b2d9942SVenkatesh Pallipadi 		result = -EFAULT;
5573b2d9942SVenkatesh Pallipadi 		goto end;
5583b2d9942SVenkatesh Pallipadi 	}
5593b2d9942SVenkatesh Pallipadi 
5603b2d9942SVenkatesh Pallipadi 	if (pdomain->num_entries != ACPI_PSD_REV0_ENTRIES) {
5616183a684SHanjun Guo 		pr_err("Unknown _PSD:num_entries\n");
5623b2d9942SVenkatesh Pallipadi 		result = -EFAULT;
5633b2d9942SVenkatesh Pallipadi 		goto end;
5643b2d9942SVenkatesh Pallipadi 	}
5653b2d9942SVenkatesh Pallipadi 
5663b2d9942SVenkatesh Pallipadi 	if (pdomain->revision != ACPI_PSD_REV0_REVISION) {
5676183a684SHanjun Guo 		pr_err("Unknown _PSD:revision\n");
5683b2d9942SVenkatesh Pallipadi 		result = -EFAULT;
5693b2d9942SVenkatesh Pallipadi 		goto end;
5703b2d9942SVenkatesh Pallipadi 	}
5713b2d9942SVenkatesh Pallipadi 
572e1eb4779SStanislaw Gruszka 	if (pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ALL &&
573e1eb4779SStanislaw Gruszka 	    pdomain->coord_type != DOMAIN_COORD_TYPE_SW_ANY &&
574e1eb4779SStanislaw Gruszka 	    pdomain->coord_type != DOMAIN_COORD_TYPE_HW_ALL) {
5756183a684SHanjun Guo 		pr_err("Invalid _PSD:coord_type\n");
576e1eb4779SStanislaw Gruszka 		result = -EFAULT;
577e1eb4779SStanislaw Gruszka 		goto end;
578e1eb4779SStanislaw Gruszka 	}
5793b2d9942SVenkatesh Pallipadi end:
58002438d87SLen Brown 	kfree(buffer.pointer);
5819011bff4SLen Brown 	return result;
5823b2d9942SVenkatesh Pallipadi }
5834d0f1ce6SJoao Martins EXPORT_SYMBOL(acpi_processor_get_psd);
5843b2d9942SVenkatesh Pallipadi 
acpi_processor_preregister_performance(struct acpi_processor_performance __percpu * performance)5853b2d9942SVenkatesh Pallipadi int acpi_processor_preregister_performance(
586a29d8b8eSTejun Heo 		struct acpi_processor_performance __percpu *performance)
5873b2d9942SVenkatesh Pallipadi {
58809d5ca80SLan Tianyu 	int count_target;
5893b2d9942SVenkatesh Pallipadi 	int retval = 0;
5903b2d9942SVenkatesh Pallipadi 	unsigned int i, j;
5912fdf66b4SRusty Russell 	cpumask_var_t covered_cpus;
5923b2d9942SVenkatesh Pallipadi 	struct acpi_processor *pr;
5933b2d9942SVenkatesh Pallipadi 	struct acpi_psd_package *pdomain;
5943b2d9942SVenkatesh Pallipadi 	struct acpi_processor *match_pr;
5953b2d9942SVenkatesh Pallipadi 	struct acpi_psd_package *match_pdomain;
5963b2d9942SVenkatesh Pallipadi 
59779f55997SLi Zefan 	if (!zalloc_cpumask_var(&covered_cpus, GFP_KERNEL))
5982fdf66b4SRusty Russell 		return -ENOMEM;
5992fdf66b4SRusty Russell 
600785fcccdSLen Brown 	mutex_lock(&performance_mutex);
6013b2d9942SVenkatesh Pallipadi 
602e1eb4779SStanislaw Gruszka 	/*
603e1eb4779SStanislaw Gruszka 	 * Check if another driver has already registered, and abort before
604e1eb4779SStanislaw Gruszka 	 * changing pr->performance if it has. Check input data as well.
605e1eb4779SStanislaw Gruszka 	 */
606193de0c7SKAMEZAWA Hiroyuki 	for_each_possible_cpu(i) {
607706546d0SMike Travis 		pr = per_cpu(processors, i);
6083b2d9942SVenkatesh Pallipadi 		if (!pr) {
6093b2d9942SVenkatesh Pallipadi 			/* Look only at processors in ACPI namespace */
6103b2d9942SVenkatesh Pallipadi 			continue;
6113b2d9942SVenkatesh Pallipadi 		}
6123b2d9942SVenkatesh Pallipadi 
6133b2d9942SVenkatesh Pallipadi 		if (pr->performance) {
6143b2d9942SVenkatesh Pallipadi 			retval = -EBUSY;
615e1eb4779SStanislaw Gruszka 			goto err_out;
6163b2d9942SVenkatesh Pallipadi 		}
6173b2d9942SVenkatesh Pallipadi 
618b36128c8SRusty Russell 		if (!performance || !per_cpu_ptr(performance, i)) {
6193b2d9942SVenkatesh Pallipadi 			retval = -EINVAL;
620e1eb4779SStanislaw Gruszka 			goto err_out;
6213b2d9942SVenkatesh Pallipadi 		}
622e1eb4779SStanislaw Gruszka 	}
623e1eb4779SStanislaw Gruszka 
624e1eb4779SStanislaw Gruszka 	/* Call _PSD for all CPUs */
625e1eb4779SStanislaw Gruszka 	for_each_possible_cpu(i) {
626e1eb4779SStanislaw Gruszka 		pr = per_cpu(processors, i);
627e1eb4779SStanislaw Gruszka 		if (!pr)
628e1eb4779SStanislaw Gruszka 			continue;
6293b2d9942SVenkatesh Pallipadi 
630b36128c8SRusty Russell 		pr->performance = per_cpu_ptr(performance, i);
6314d0f1ce6SJoao Martins 		pdomain = &(pr->performance->domain_info);
6324d0f1ce6SJoao Martins 		if (acpi_processor_get_psd(pr->handle, pdomain)) {
6333b2d9942SVenkatesh Pallipadi 			retval = -EINVAL;
6343b2d9942SVenkatesh Pallipadi 			continue;
6353b2d9942SVenkatesh Pallipadi 		}
6363b2d9942SVenkatesh Pallipadi 	}
6373b2d9942SVenkatesh Pallipadi 	if (retval)
6383b2d9942SVenkatesh Pallipadi 		goto err_ret;
6393b2d9942SVenkatesh Pallipadi 
6403b2d9942SVenkatesh Pallipadi 	/*
6413b2d9942SVenkatesh Pallipadi 	 * Now that we have _PSD data from all CPUs, lets setup P-state
6423b2d9942SVenkatesh Pallipadi 	 * domain info.
6433b2d9942SVenkatesh Pallipadi 	 */
644193de0c7SKAMEZAWA Hiroyuki 	for_each_possible_cpu(i) {
645706546d0SMike Travis 		pr = per_cpu(processors, i);
6463b2d9942SVenkatesh Pallipadi 		if (!pr)
6473b2d9942SVenkatesh Pallipadi 			continue;
6483b2d9942SVenkatesh Pallipadi 
6492fdf66b4SRusty Russell 		if (cpumask_test_cpu(i, covered_cpus))
6503b2d9942SVenkatesh Pallipadi 			continue;
6513b2d9942SVenkatesh Pallipadi 
6523b2d9942SVenkatesh Pallipadi 		pdomain = &(pr->performance->domain_info);
6532fdf66b4SRusty Russell 		cpumask_set_cpu(i, pr->performance->shared_cpu_map);
6542fdf66b4SRusty Russell 		cpumask_set_cpu(i, covered_cpus);
6553b2d9942SVenkatesh Pallipadi 		if (pdomain->num_processors <= 1)
6563b2d9942SVenkatesh Pallipadi 			continue;
6573b2d9942SVenkatesh Pallipadi 
6583b2d9942SVenkatesh Pallipadi 		/* Validate the Domain info */
6593b2d9942SVenkatesh Pallipadi 		count_target = pdomain->num_processors;
66046f18e3aSVenkatesh Pallipadi 		if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
6613b2d9942SVenkatesh Pallipadi 			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ALL;
66246f18e3aSVenkatesh Pallipadi 		else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
66346f18e3aSVenkatesh Pallipadi 			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_HW;
66446f18e3aSVenkatesh Pallipadi 		else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
6653b2d9942SVenkatesh Pallipadi 			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_ANY;
6663b2d9942SVenkatesh Pallipadi 
667193de0c7SKAMEZAWA Hiroyuki 		for_each_possible_cpu(j) {
6683b2d9942SVenkatesh Pallipadi 			if (i == j)
6693b2d9942SVenkatesh Pallipadi 				continue;
6703b2d9942SVenkatesh Pallipadi 
671706546d0SMike Travis 			match_pr = per_cpu(processors, j);
6723b2d9942SVenkatesh Pallipadi 			if (!match_pr)
6733b2d9942SVenkatesh Pallipadi 				continue;
6743b2d9942SVenkatesh Pallipadi 
6753b2d9942SVenkatesh Pallipadi 			match_pdomain = &(match_pr->performance->domain_info);
6763b2d9942SVenkatesh Pallipadi 			if (match_pdomain->domain != pdomain->domain)
6773b2d9942SVenkatesh Pallipadi 				continue;
6783b2d9942SVenkatesh Pallipadi 
6793b2d9942SVenkatesh Pallipadi 			/* Here i and j are in the same domain */
6803b2d9942SVenkatesh Pallipadi 
6813b2d9942SVenkatesh Pallipadi 			if (match_pdomain->num_processors != count_target) {
6823b2d9942SVenkatesh Pallipadi 				retval = -EINVAL;
6833b2d9942SVenkatesh Pallipadi 				goto err_ret;
6843b2d9942SVenkatesh Pallipadi 			}
6853b2d9942SVenkatesh Pallipadi 
6863b2d9942SVenkatesh Pallipadi 			if (pdomain->coord_type != match_pdomain->coord_type) {
6873b2d9942SVenkatesh Pallipadi 				retval = -EINVAL;
6883b2d9942SVenkatesh Pallipadi 				goto err_ret;
6893b2d9942SVenkatesh Pallipadi 			}
6903b2d9942SVenkatesh Pallipadi 
6912fdf66b4SRusty Russell 			cpumask_set_cpu(j, covered_cpus);
6922fdf66b4SRusty Russell 			cpumask_set_cpu(j, pr->performance->shared_cpu_map);
6933b2d9942SVenkatesh Pallipadi 		}
6943b2d9942SVenkatesh Pallipadi 
695193de0c7SKAMEZAWA Hiroyuki 		for_each_possible_cpu(j) {
6963b2d9942SVenkatesh Pallipadi 			if (i == j)
6973b2d9942SVenkatesh Pallipadi 				continue;
6983b2d9942SVenkatesh Pallipadi 
699706546d0SMike Travis 			match_pr = per_cpu(processors, j);
7003b2d9942SVenkatesh Pallipadi 			if (!match_pr)
7013b2d9942SVenkatesh Pallipadi 				continue;
7023b2d9942SVenkatesh Pallipadi 
7033b2d9942SVenkatesh Pallipadi 			match_pdomain = &(match_pr->performance->domain_info);
7043b2d9942SVenkatesh Pallipadi 			if (match_pdomain->domain != pdomain->domain)
7053b2d9942SVenkatesh Pallipadi 				continue;
7063b2d9942SVenkatesh Pallipadi 
7073b2d9942SVenkatesh Pallipadi 			match_pr->performance->shared_type =
7083b2d9942SVenkatesh Pallipadi 					pr->performance->shared_type;
7092fdf66b4SRusty Russell 			cpumask_copy(match_pr->performance->shared_cpu_map,
7102fdf66b4SRusty Russell 				     pr->performance->shared_cpu_map);
7113b2d9942SVenkatesh Pallipadi 		}
7123b2d9942SVenkatesh Pallipadi 	}
7133b2d9942SVenkatesh Pallipadi 
7143b2d9942SVenkatesh Pallipadi err_ret:
715193de0c7SKAMEZAWA Hiroyuki 	for_each_possible_cpu(i) {
716706546d0SMike Travis 		pr = per_cpu(processors, i);
7173b2d9942SVenkatesh Pallipadi 		if (!pr || !pr->performance)
7183b2d9942SVenkatesh Pallipadi 			continue;
7193b2d9942SVenkatesh Pallipadi 
7203b2d9942SVenkatesh Pallipadi 		/* Assume no coordination on any error parsing domain info */
7213b2d9942SVenkatesh Pallipadi 		if (retval) {
7222fdf66b4SRusty Russell 			cpumask_clear(pr->performance->shared_cpu_map);
7232fdf66b4SRusty Russell 			cpumask_set_cpu(i, pr->performance->shared_cpu_map);
724bca3e43cSIonela Voinescu 			pr->performance->shared_type = CPUFREQ_SHARED_TYPE_NONE;
7253b2d9942SVenkatesh Pallipadi 		}
7263b2d9942SVenkatesh Pallipadi 		pr->performance = NULL; /* Will be set for real in register */
7273b2d9942SVenkatesh Pallipadi 	}
7283b2d9942SVenkatesh Pallipadi 
729e1eb4779SStanislaw Gruszka err_out:
730785fcccdSLen Brown 	mutex_unlock(&performance_mutex);
7312fdf66b4SRusty Russell 	free_cpumask_var(covered_cpus);
7329011bff4SLen Brown 	return retval;
7333b2d9942SVenkatesh Pallipadi }
7343b2d9942SVenkatesh Pallipadi EXPORT_SYMBOL(acpi_processor_preregister_performance);
7353b2d9942SVenkatesh Pallipadi 
acpi_processor_register_performance(struct acpi_processor_performance * performance,unsigned int cpu)7363d9e9a96SRafael J. Wysocki int acpi_processor_register_performance(struct acpi_processor_performance
7374be44fcdSLen Brown 					*performance, unsigned int cpu)
7381da177e4SLinus Torvalds {
7391da177e4SLinus Torvalds 	struct acpi_processor *pr;
7401da177e4SLinus Torvalds 
741d15ce412SViresh Kumar 	if (!acpi_processor_cpufreq_init)
742d550d98dSPatrick Mochel 		return -EINVAL;
7431da177e4SLinus Torvalds 
74465c19bbdSArjan van de Ven 	mutex_lock(&performance_mutex);
7451da177e4SLinus Torvalds 
746706546d0SMike Travis 	pr = per_cpu(processors, cpu);
7471da177e4SLinus Torvalds 	if (!pr) {
74865c19bbdSArjan van de Ven 		mutex_unlock(&performance_mutex);
749d550d98dSPatrick Mochel 		return -ENODEV;
7501da177e4SLinus Torvalds 	}
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds 	if (pr->performance) {
75365c19bbdSArjan van de Ven 		mutex_unlock(&performance_mutex);
754d550d98dSPatrick Mochel 		return -EBUSY;
7551da177e4SLinus Torvalds 	}
7561da177e4SLinus Torvalds 
757a913f507SAndrew Morton 	WARN_ON(!performance);
758a913f507SAndrew Morton 
7591da177e4SLinus Torvalds 	pr->performance = performance;
7601da177e4SLinus Torvalds 
7611da177e4SLinus Torvalds 	if (acpi_processor_get_performance_info(pr)) {
7621da177e4SLinus Torvalds 		pr->performance = NULL;
76365c19bbdSArjan van de Ven 		mutex_unlock(&performance_mutex);
764d550d98dSPatrick Mochel 		return -EIO;
7651da177e4SLinus Torvalds 	}
7661da177e4SLinus Torvalds 
76765c19bbdSArjan van de Ven 	mutex_unlock(&performance_mutex);
768d550d98dSPatrick Mochel 	return 0;
7691da177e4SLinus Torvalds }
7701da177e4SLinus Torvalds EXPORT_SYMBOL(acpi_processor_register_performance);
7711da177e4SLinus Torvalds 
acpi_processor_unregister_performance(unsigned int cpu)772b2f8dc4cSRafael J. Wysocki void acpi_processor_unregister_performance(unsigned int cpu)
7731da177e4SLinus Torvalds {
7741da177e4SLinus Torvalds 	struct acpi_processor *pr;
7751da177e4SLinus Torvalds 
77665c19bbdSArjan van de Ven 	mutex_lock(&performance_mutex);
7771da177e4SLinus Torvalds 
778706546d0SMike Travis 	pr = per_cpu(processors, cpu);
7795be583c6SRafael J. Wysocki 	if (!pr)
7805be583c6SRafael J. Wysocki 		goto unlock;
7811da177e4SLinus Torvalds 
782a913f507SAndrew Morton 	if (pr->performance)
7831da177e4SLinus Torvalds 		kfree(pr->performance->states);
7843d9e9a96SRafael J. Wysocki 
7851da177e4SLinus Torvalds 	pr->performance = NULL;
7861da177e4SLinus Torvalds 
7875be583c6SRafael J. Wysocki unlock:
78865c19bbdSArjan van de Ven 	mutex_unlock(&performance_mutex);
7891da177e4SLinus Torvalds }
7901da177e4SLinus Torvalds EXPORT_SYMBOL(acpi_processor_unregister_performance);
791