10f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar/*
20f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  pcc-cpufreq.c - Processor Clocking Control firmware cpufreq interface
30f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *
40f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  Copyright (C) 2009 Red Hat, Matthew Garrett <mjg@redhat.com>
50f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
60f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *	Nagananda Chumbalkar <nagananda.chumbalkar@hp.com>
70f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *
80f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
90f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *
100f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  This program is free software; you can redistribute it and/or modify
110f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  it under the terms of the GNU General Public License as published by
120f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  the Free Software Foundation; version 2 of the License.
130f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *
140f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  This program is distributed in the hope that it will be useful, but
150f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  WITHOUT ANY WARRANTY; without even the implied warranty of
160f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or NON
170f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  INFRINGEMENT. See the GNU General Public License for more details.
180f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *
190f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  You should have received a copy of the GNU General Public License along
200f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  with this program; if not, write to the Free Software Foundation, Inc.,
210f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *  675 Mass Ave, Cambridge, MA 02139, USA.
220f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar *
230f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
240f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar */
250f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
260f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/kernel.h>
270f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/module.h>
280f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/init.h>
290f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/smp.h>
300f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/sched.h>
310f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/cpufreq.h>
320f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/compiler.h>
335a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
340f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
350f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/acpi.h>
360f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/io.h>
370f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/spinlock.h>
380f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <linux/uaccess.h>
390f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
400f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#include <acpi/processor.h>
410f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
42904cc1e637a00dba1b58e7752f485f90ebf2a568Naga Chumbalkar#define PCC_VERSION	"1.10.00"
430f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#define POLL_LOOPS 	300
440f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
450f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#define CMD_COMPLETE 	0x1
460f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#define CMD_GET_FREQ 	0x0
470f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#define CMD_SET_FREQ 	0x1
480f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
490f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar#define BUF_SZ		4
500f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
510f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstruct pcc_register_resource {
520f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 descriptor;
530f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u16 length;
540f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 space_id;
550f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 bit_width;
560f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 bit_offset;
570f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 access_size;
580f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u64 address;
590f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar} __attribute__ ((packed));
600f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
610f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstruct pcc_memory_resource {
620f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 descriptor;
630f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u16 length;
640f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 space_id;
650f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 resource_usage;
660f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 type_specific;
670f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u64 granularity;
680f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u64 minimum;
690f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u64 maximum;
700f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u64 translation_offset;
710f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u64 address_length;
720f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar} __attribute__ ((packed));
730f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
740f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic struct cpufreq_driver pcc_cpufreq_driver;
750f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
760f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstruct pcc_header {
770f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 signature;
780f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u16 length;
790f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 major;
800f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u8 minor;
810f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 features;
820f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u16 command;
830f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u16 status;
840f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 latency;
850f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 minimum_time;
860f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 maximum_time;
870f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 nominal;
880f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 throttled_frequency;
890f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 minimum_frequency;
900f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar};
910f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
920f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic void __iomem *pcch_virt_addr;
930f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic struct pcc_header __iomem *pcch_hdr;
940f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
950f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic DEFINE_SPINLOCK(pcc_lock);
960f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
970f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic struct acpi_generic_address doorbell;
980f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
990f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic u64 doorbell_preserve;
1000f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic u64 doorbell_write;
1010f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
102904cc1e637a00dba1b58e7752f485f90ebf2a568Naga Chumbalkarstatic u8 OSC_UUID[16] = {0x9F, 0x2C, 0x9B, 0x63, 0x91, 0x70, 0x1f, 0x49,
1030f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			  0xBB, 0x4F, 0xA5, 0x98, 0x2F, 0xA1, 0xB5, 0x46};
1040f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1050f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstruct pcc_cpu {
1060f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 input_offset;
1070f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 output_offset;
1080f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar};
1090f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
110a3da323420d5aa6f7bd15efc7bf34cd6d19e1f1aNamhyung Kimstatic struct pcc_cpu __percpu *pcc_cpu_info;
1110f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1120f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic int pcc_cpufreq_verify(struct cpufreq_policy *policy)
1130f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
1140f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
1150f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar				     policy->cpuinfo.max_freq);
1160f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return 0;
1170f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
1180f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1190f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic inline void pcc_cmd(void)
1200f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
1210f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u64 doorbell_value;
1220f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	int i;
1230f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1240f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	acpi_read(&doorbell_value, &doorbell);
1250f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	acpi_write((doorbell_value & doorbell_preserve) | doorbell_write,
1260f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		   &doorbell);
1270f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1280f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	for (i = 0; i < POLL_LOOPS; i++) {
1290f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		if (ioread16(&pcch_hdr->status) & CMD_COMPLETE)
1300f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			break;
1310f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
1320f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
1330f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1340f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic inline void pcc_clear_mapping(void)
1350f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
1360f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (pcch_virt_addr)
1370f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		iounmap(pcch_virt_addr);
1380f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcch_virt_addr = NULL;
1390f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
1400f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1410f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic unsigned int pcc_get_freq(unsigned int cpu)
1420f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
1430f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct pcc_cpu *pcc_cpu_data;
1440f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	unsigned int curr_freq;
1450f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	unsigned int freq_limit;
1460f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u16 status;
1470f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 input_buffer;
1480f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 output_buffer;
1490f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1500f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	spin_lock(&pcc_lock);
1510f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1522d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("get: get_freq for CPU %d\n", cpu);
1530f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
1540f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1550f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	input_buffer = 0x1;
1560f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	iowrite32(input_buffer,
1570f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			(pcch_virt_addr + pcc_cpu_data->input_offset));
1580f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	iowrite16(CMD_GET_FREQ, &pcch_hdr->command);
1590f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1600f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_cmd();
1610f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1620f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	output_buffer =
1630f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(pcch_virt_addr + pcc_cpu_data->output_offset);
1640f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1650f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	/* Clear the input buffer - we are done with the current command */
1660f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
1670f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1680f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	status = ioread16(&pcch_hdr->status);
1690f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (status != CMD_COMPLETE) {
1702d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("get: FAILED: for CPU %d, status is %d\n",
1710f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			cpu, status);
1720f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto cmd_incomplete;
1730f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
1740f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	iowrite16(0, &pcch_hdr->status);
1750f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	curr_freq = (((ioread32(&pcch_hdr->nominal) * (output_buffer & 0xff))
1760f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			/ 100) * 1000);
1770f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1782d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("get: SUCCESS: (virtual) output_offset for cpu %d is "
1792d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		"0x%p, contains a value of: 0x%x. Speed is: %d MHz\n",
1800f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		cpu, (pcch_virt_addr + pcc_cpu_data->output_offset),
1810f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		output_buffer, curr_freq);
1820f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1830f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	freq_limit = (output_buffer >> 8) & 0xff;
1840f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (freq_limit != 0xff) {
1852d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("get: frequency for cpu %d is being temporarily"
1860f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			" capped at %d\n", cpu, curr_freq);
1870f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
1880f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1890f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	spin_unlock(&pcc_lock);
1900f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return curr_freq;
1910f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1920f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarcmd_incomplete:
1930f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	iowrite16(0, &pcch_hdr->status);
1940f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	spin_unlock(&pcc_lock);
1951f858ef2fbabdc5e645644010a31a40c32e397c9Naga Chumbalkar	return 0;
1960f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
1970f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
1980f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic int pcc_cpufreq_target(struct cpufreq_policy *policy,
1990f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			      unsigned int target_freq,
2000f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			      unsigned int relation)
2010f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
2020f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct pcc_cpu *pcc_cpu_data;
2030f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct cpufreq_freqs freqs;
2040f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u16 status;
2050f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 input_buffer;
2060f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	int cpu;
2070f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2080f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	spin_lock(&pcc_lock);
2090f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	cpu = policy->cpu;
2100f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
2110f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2122d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("target: CPU %d should go to target freq: %d "
2132d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		"(virtual) input_offset is 0x%p\n",
2140f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		cpu, target_freq,
2150f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		(pcch_virt_addr + pcc_cpu_data->input_offset));
2160f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2170f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	freqs.new = target_freq;
2180f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	freqs.cpu = cpu;
2190f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
2200f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2210f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	input_buffer = 0x1 | (((target_freq * 100)
2220f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			       / (ioread32(&pcch_hdr->nominal) * 1000)) << 8);
2230f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	iowrite32(input_buffer,
2240f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			(pcch_virt_addr + pcc_cpu_data->input_offset));
2250f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	iowrite16(CMD_SET_FREQ, &pcch_hdr->command);
2260f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2270f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_cmd();
2280f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2290f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	/* Clear the input buffer - we are done with the current command */
2300f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
2310f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2320f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	status = ioread16(&pcch_hdr->status);
2330f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (status != CMD_COMPLETE) {
2342d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("target: FAILED for cpu %d, with status: 0x%x\n",
2350f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar			cpu, status);
2360f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto cmd_incomplete;
2370f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
2380f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	iowrite16(0, &pcch_hdr->status);
2390f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2400f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
2412d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu);
2420f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	spin_unlock(&pcc_lock);
2430f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2440f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return 0;
2450f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2460f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarcmd_incomplete:
2470f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	iowrite16(0, &pcch_hdr->status);
2480f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	spin_unlock(&pcc_lock);
2490f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return -EINVAL;
2500f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
2510f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2520f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic int pcc_get_offset(int cpu)
2530f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
2540f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	acpi_status status;
2550f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
2560f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	union acpi_object *pccp, *offset;
2570f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct pcc_cpu *pcc_cpu_data;
2580f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct acpi_processor *pr;
2590f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	int ret = 0;
2600f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2610f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pr = per_cpu(processors, cpu);
2620f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_cpu_data = per_cpu_ptr(pcc_cpu_info, cpu);
2630f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
264e71f5cc402ecb42b407ae52add7b173bf1c53daaNaga Chumbalkar	if (!pr)
265e71f5cc402ecb42b407ae52add7b173bf1c53daaNaga Chumbalkar		return -ENODEV;
266e71f5cc402ecb42b407ae52add7b173bf1c53daaNaga Chumbalkar
2670f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	status = acpi_evaluate_object(pr->handle, "PCCP", NULL, &buffer);
2680f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (ACPI_FAILURE(status))
2690f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return -ENODEV;
2700f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2710f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pccp = buffer.pointer;
2720f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (!pccp || pccp->type != ACPI_TYPE_PACKAGE) {
2730f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
2740f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
2750f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	};
2760f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2770f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	offset = &(pccp->package.elements[0]);
2780f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (!offset || offset->type != ACPI_TYPE_INTEGER) {
2790f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
2800f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
2810f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
2820f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2830f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_cpu_data->input_offset = offset->integer.value;
2840f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2850f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	offset = &(pccp->package.elements[1]);
2860f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (!offset || offset->type != ACPI_TYPE_INTEGER) {
2870f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
2880f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
2890f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
2900f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2910f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_cpu_data->output_offset = offset->integer.value;
2920f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2930f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
2940f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	memset_io((pcch_virt_addr + pcc_cpu_data->output_offset), 0, BUF_SZ);
2950f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
2962d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("pcc_get_offset: for CPU %d: pcc_cpu_data "
2970f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		"input_offset: 0x%x, pcc_cpu_data output_offset: 0x%x\n",
2980f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		cpu, pcc_cpu_data->input_offset, pcc_cpu_data->output_offset);
2990f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarout_free:
3000f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	kfree(buffer.pointer);
3010f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return ret;
3020f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
3030f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3040f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic int __init pcc_cpufreq_do_osc(acpi_handle *handle)
3050f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
3060f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	acpi_status status;
3070f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct acpi_object_list input;
3080f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
3090f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	union acpi_object in_params[4];
3100f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	union acpi_object *out_obj;
3110f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 capabilities[2];
3120f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 errors;
3130f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	u32 supported;
3140f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	int ret = 0;
3150f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3160f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	input.count = 4;
3170f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	input.pointer = in_params;
3180f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[0].type               = ACPI_TYPE_BUFFER;
3190f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[0].buffer.length      = 16;
3200f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[0].buffer.pointer     = OSC_UUID;
3210f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[1].type               = ACPI_TYPE_INTEGER;
3220f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[1].integer.value      = 1;
3230f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[2].type               = ACPI_TYPE_INTEGER;
3240f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[2].integer.value      = 2;
3250f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[3].type               = ACPI_TYPE_BUFFER;
3260f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[3].buffer.length      = 8;
3270f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	in_params[3].buffer.pointer     = (u8 *)&capabilities;
3280f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3290f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	capabilities[0] = OSC_QUERY_ENABLE;
3300f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	capabilities[1] = 0x1;
3310f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3320f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	status = acpi_evaluate_object(*handle, "_OSC", &input, &output);
3330f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (ACPI_FAILURE(status))
3340f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return -ENODEV;
3350f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3360f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (!output.length)
3370f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return -ENODEV;
3380f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3390f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	out_obj = output.pointer;
3400f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (out_obj->type != ACPI_TYPE_BUFFER) {
3410f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
3420f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
3430f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
3440f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3450f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
3460f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (errors) {
3470f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
3480f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
3490f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
3500f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3510f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	supported = *((u32 *)(out_obj->buffer.pointer + 4));
3520f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (!(supported & 0x1)) {
3530f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
3540f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
3550f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
3560f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3570f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	kfree(output.pointer);
3580f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	capabilities[0] = 0x0;
3590f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	capabilities[1] = 0x1;
3600f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3610f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	status = acpi_evaluate_object(*handle, "_OSC", &input, &output);
3620f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (ACPI_FAILURE(status))
3630f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return -ENODEV;
3640f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3650f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (!output.length)
3660f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return -ENODEV;
3670f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3680f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	out_obj = output.pointer;
3693682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg	if (out_obj->type != ACPI_TYPE_BUFFER) {
3703682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg		ret = -ENODEV;
3713682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg		goto out_free;
3723682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg	}
3730f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3740f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0);
3753682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg	if (errors) {
3763682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg		ret = -ENODEV;
3773682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg		goto out_free;
3783682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg	}
3790f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3800f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	supported = *((u32 *)(out_obj->buffer.pointer + 4));
3813682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg	if (!(supported & 0x1)) {
3823682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg		ret = -ENODEV;
3833682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg		goto out_free;
3843682930623f63c693845d9620c6bcdf5598c9bbbPekka Enberg	}
3850f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3860f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarout_free:
3870f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	kfree(output.pointer);
3880f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return ret;
3890f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
3900f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
3910f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic int __init pcc_cpufreq_probe(void)
3920f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
3930f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	acpi_status status;
3940f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL};
3950f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct pcc_memory_resource *mem_resource;
3960f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	struct pcc_register_resource *reg_resource;
3970f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	union acpi_object *out_obj, *member;
39847f8bcf362410b631a4d99ff5c79ec6b9dd3ace6Matthew Garrett	acpi_handle handle, osc_handle, pcch_handle;
3990f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	int ret = 0;
4000f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4010f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	status = acpi_get_handle(NULL, "\\_SB", &handle);
4020f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (ACPI_FAILURE(status))
4030f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return -ENODEV;
4040f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
40547f8bcf362410b631a4d99ff5c79ec6b9dd3ace6Matthew Garrett	status = acpi_get_handle(handle, "PCCH", &pcch_handle);
40647f8bcf362410b631a4d99ff5c79ec6b9dd3ace6Matthew Garrett	if (ACPI_FAILURE(status))
40747f8bcf362410b631a4d99ff5c79ec6b9dd3ace6Matthew Garrett		return -ENODEV;
40847f8bcf362410b631a4d99ff5c79ec6b9dd3ace6Matthew Garrett
4090f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	status = acpi_get_handle(handle, "_OSC", &osc_handle);
4100f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (ACPI_SUCCESS(status)) {
4110f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = pcc_cpufreq_do_osc(&osc_handle);
4120f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		if (ret)
4132d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski			pr_debug("probe: _OSC evaluation did not succeed\n");
4140f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		/* Firmware's use of _OSC is optional */
4150f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = 0;
4160f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
4170f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4180f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	status = acpi_evaluate_object(handle, "PCCH", NULL, &output);
4190f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (ACPI_FAILURE(status))
4200f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return -ENODEV;
4210f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4220f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	out_obj = output.pointer;
4230f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (out_obj->type != ACPI_TYPE_PACKAGE) {
4240f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
4250f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
4260f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
4270f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4280f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	member = &out_obj->package.elements[0];
4290f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (member->type != ACPI_TYPE_BUFFER) {
4300f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
4310f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
4320f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
4330f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4340f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	mem_resource = (struct pcc_memory_resource *)member->buffer.pointer;
4350f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4362d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("probe: mem_resource descriptor: 0x%x,"
4370f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" length: %d, space_id: %d, resource_usage: %d,"
4380f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" type_specific: %d, granularity: 0x%llx,"
4390f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" minimum: 0x%llx, maximum: 0x%llx,"
4400f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" translation_offset: 0x%llx, address_length: 0x%llx\n",
4410f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		mem_resource->descriptor, mem_resource->length,
4420f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		mem_resource->space_id, mem_resource->resource_usage,
4430f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		mem_resource->type_specific, mem_resource->granularity,
4440f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		mem_resource->minimum, mem_resource->maximum,
4450f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		mem_resource->translation_offset,
4460f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		mem_resource->address_length);
4470f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4480f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (mem_resource->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
4490f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
4500f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
4510f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
4520f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4530f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcch_virt_addr = ioremap_nocache(mem_resource->minimum,
4540f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar					mem_resource->address_length);
4550f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (pcch_virt_addr == NULL) {
4562d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("probe: could not map shared mem region\n");
4570f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto out_free;
4580f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
4590f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcch_hdr = pcch_virt_addr;
4600f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4612d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("probe: PCCH header (virtual) addr: 0x%p\n", pcch_hdr);
4622d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("probe: PCCH header is at physical address: 0x%llx,"
4630f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" signature: 0x%x, length: %d bytes, major: %d, minor: %d,"
4640f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" supported features: 0x%x, command field: 0x%x,"
4650f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" status field: 0x%x, nominal latency: %d us\n",
4660f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		mem_resource->minimum, ioread32(&pcch_hdr->signature),
4670f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread16(&pcch_hdr->length), ioread8(&pcch_hdr->major),
4680f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread8(&pcch_hdr->minor), ioread32(&pcch_hdr->features),
4690f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread16(&pcch_hdr->command), ioread16(&pcch_hdr->status),
4700f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(&pcch_hdr->latency));
4710f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4722d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("probe: min time between commands: %d us,"
4730f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" max time between commands: %d us,"
4740f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" nominal CPU frequency: %d MHz,"
4750f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" minimum CPU frequency: %d MHz,"
4760f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" minimum CPU frequency without throttling: %d MHz\n",
4770f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(&pcch_hdr->minimum_time),
4780f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(&pcch_hdr->maximum_time),
4790f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(&pcch_hdr->nominal),
4800f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(&pcch_hdr->throttled_frequency),
4810f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(&pcch_hdr->minimum_frequency));
4820f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4830f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	member = &out_obj->package.elements[1];
4840f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (member->type != ACPI_TYPE_BUFFER) {
4850f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
4860f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto pcch_free;
4870f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
4880f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4890f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	reg_resource = (struct pcc_register_resource *)member->buffer.pointer;
4900f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4910f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	doorbell.space_id = reg_resource->space_id;
4920f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	doorbell.bit_width = reg_resource->bit_width;
4930f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	doorbell.bit_offset = reg_resource->bit_offset;
4940f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	doorbell.access_width = 64;
4950f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	doorbell.address = reg_resource->address;
4960f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
4972d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("probe: doorbell: space_id is %d, bit_width is %d, "
4980f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		"bit_offset is %d, access_width is %d, address is 0x%llx\n",
4990f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		doorbell.space_id, doorbell.bit_width, doorbell.bit_offset,
5000f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		doorbell.access_width, reg_resource->address);
5010f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5020f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	member = &out_obj->package.elements[2];
5030f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (member->type != ACPI_TYPE_INTEGER) {
5040f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
5050f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto pcch_free;
5060f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
5070f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5080f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	doorbell_preserve = member->integer.value;
5090f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5100f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	member = &out_obj->package.elements[3];
5110f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (member->type != ACPI_TYPE_INTEGER) {
5120f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENODEV;
5130f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto pcch_free;
5140f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
5150f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5160f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	doorbell_write = member->integer.value;
5170f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5182d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("probe: doorbell_preserve: 0x%llx,"
5190f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		" doorbell_write: 0x%llx\n",
5200f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		doorbell_preserve, doorbell_write);
5210f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5220f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_cpu_info = alloc_percpu(struct pcc_cpu);
5230f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (!pcc_cpu_info) {
5240f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ret = -ENOMEM;
5250f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		goto pcch_free;
5260f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
5270f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5280f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	printk(KERN_DEBUG "pcc-cpufreq: (v%s) driver loaded with frequency"
5290f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	       " limits: %d MHz, %d MHz\n", PCC_VERSION,
5300f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	       ioread32(&pcch_hdr->minimum_frequency),
5310f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	       ioread32(&pcch_hdr->nominal));
5320f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	kfree(output.pointer);
5330f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return ret;
5340f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarpcch_free:
5350f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_clear_mapping();
5360f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarout_free:
5370f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	kfree(output.pointer);
5380f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return ret;
5390f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
5400f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5410f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy)
5420f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
5430f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	unsigned int cpu = policy->cpu;
5440f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	unsigned int result = 0;
5450f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5460f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (!pcch_virt_addr) {
5470f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		result = -1;
548179ee43465343d1f8f2a4af25ead4ae15e43fa6eMatthew Garrett		goto out;
5490f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
5500f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5510f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	result = pcc_get_offset(cpu);
5520f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (result) {
5532d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("init: PCCP evaluation failed\n");
554179ee43465343d1f8f2a4af25ead4ae15e43fa6eMatthew Garrett		goto out;
5550f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
5560f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5570f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	policy->max = policy->cpuinfo.max_freq =
5580f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(&pcch_hdr->nominal) * 1000;
5590f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	policy->min = policy->cpuinfo.min_freq =
5600f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		ioread32(&pcch_hdr->minimum_frequency) * 1000;
5610f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	policy->cur = pcc_get_freq(cpu);
5620f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
563179ee43465343d1f8f2a4af25ead4ae15e43fa6eMatthew Garrett	if (!policy->cur) {
5642d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("init: Unable to get current CPU frequency\n");
565179ee43465343d1f8f2a4af25ead4ae15e43fa6eMatthew Garrett		result = -EINVAL;
566179ee43465343d1f8f2a4af25ead4ae15e43fa6eMatthew Garrett		goto out;
567179ee43465343d1f8f2a4af25ead4ae15e43fa6eMatthew Garrett	}
568179ee43465343d1f8f2a4af25ead4ae15e43fa6eMatthew Garrett
5692d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski	pr_debug("init: policy->max is %d, policy->min is %d\n",
5700f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		policy->max, policy->min);
571179ee43465343d1f8f2a4af25ead4ae15e43fa6eMatthew Garrettout:
5720f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return result;
5730f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
5740f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5750f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic int pcc_cpufreq_cpu_exit(struct cpufreq_policy *policy)
5760f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
5770f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return 0;
5780f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
5790f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5800f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic struct cpufreq_driver pcc_cpufreq_driver = {
5810f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	.flags = CPUFREQ_CONST_LOOPS,
5820f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	.get = pcc_get_freq,
5830f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	.verify = pcc_cpufreq_verify,
5840f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	.target = pcc_cpufreq_target,
5850f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	.init = pcc_cpufreq_cpu_init,
5860f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	.exit = pcc_cpufreq_cpu_exit,
5870f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	.name = "pcc-cpufreq",
5880f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	.owner = THIS_MODULE,
5890f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar};
5900f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5910f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic int __init pcc_cpufreq_init(void)
5920f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
5930f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	int ret;
5940f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5950f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (acpi_disabled)
5960f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return 0;
5970f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
5980f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	ret = pcc_cpufreq_probe();
5990f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	if (ret) {
6002d06d8c49afdcc9bb35a85039fa50f0fe35bd40eDominik Brodowski		pr_debug("pcc_cpufreq_init: PCCH evaluation failed\n");
6010f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar		return ret;
6020f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	}
6030f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
6040f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	ret = cpufreq_register_driver(&pcc_cpufreq_driver);
6050f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
6060f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	return ret;
6070f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
6080f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
6090f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarstatic void __exit pcc_cpufreq_exit(void)
6100f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar{
6110f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	cpufreq_unregister_driver(&pcc_cpufreq_driver);
6120f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
6130f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	pcc_clear_mapping();
6140f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
6150f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar	free_percpu(pcc_cpu_info);
6160f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar}
6170f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
6180f1d683fb35d6c6f49ef696c95757f3970682a0eNaga ChumbalkarMODULE_AUTHOR("Matthew Garrett, Naga Chumbalkar");
6190f1d683fb35d6c6f49ef696c95757f3970682a0eNaga ChumbalkarMODULE_VERSION(PCC_VERSION);
6200f1d683fb35d6c6f49ef696c95757f3970682a0eNaga ChumbalkarMODULE_DESCRIPTION("Processor Clocking Control interface driver");
6210f1d683fb35d6c6f49ef696c95757f3970682a0eNaga ChumbalkarMODULE_LICENSE("GPL");
6220f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkar
6230f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarlate_initcall(pcc_cpufreq_init);
6240f1d683fb35d6c6f49ef696c95757f3970682a0eNaga Chumbalkarmodule_exit(pcc_cpufreq_exit);
625