cpu_cooling.c revision 50e66c7ed8a1cd7e933628f9f5cf2617394adf5a
1023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/*
2023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  linux/drivers/thermal/cpu_cooling.c
3023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *
4023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  Copyright (C) 2012	Samsung Electronics Co., Ltd(http://www.samsung.com)
5023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  Copyright (C) 2012  Amit Daniel <amit.kachhap@linaro.org>
6023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *
7023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  This program is free software; you can redistribute it and/or modify
9023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  it under the terms of the GNU General Public License as published by
10023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  the Free Software Foundation; version 2 of the License.
11023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *
12023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  This program is distributed in the hope that it will be useful, but
13023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  WITHOUT ANY WARRANTY; without even the implied warranty of
14023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  General Public License for more details.
16023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *
17023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  You should have received a copy of the GNU General Public License along
18023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  with this program; if not, write to the Free Software Foundation, Inc.,
19023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *
21023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
23023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap#include <linux/module.h>
24023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap#include <linux/thermal.h>
25023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap#include <linux/cpufreq.h>
26023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap#include <linux/err.h>
27023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap#include <linux/slab.h>
28023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap#include <linux/cpu.h>
29023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap#include <linux/cpu_cooling.h>
30023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
31023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
323b3c07485579b9a4ecaee718667c87f59c603686Eduardo Valentin * struct cpufreq_cooling_device - data for cooling device with cpufreq
33023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @id: unique integer value corresponding to each cpufreq_cooling_device
34023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *	registered.
353b3c07485579b9a4ecaee718667c87f59c603686Eduardo Valentin * @cool_dev: thermal_cooling_device pointer to keep track of the
363b3c07485579b9a4ecaee718667c87f59c603686Eduardo Valentin *	registered cooling device.
37023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cpufreq_state: integer value representing the current state of cpufreq
38023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *	cooling	devices.
39023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cpufreq_val: integer value representing the absolute value of the clipped
40023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *	frequency.
41023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @allowed_cpus: all the cpus involved for this cpufreq_cooling_device.
42023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *
43023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * This structure is required for keeping information of each
4467d0b2a8267caa4e653714bec79013fa6b7dd508Eduardo Valentin * cpufreq_cooling_device registered. In order to prevent corruption of this a
45023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * mutex lock cooling_cpufreq_lock is used.
46023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
47023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstruct cpufreq_cooling_device {
48023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	int id;
49023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct thermal_cooling_device *cool_dev;
50023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	unsigned int cpufreq_state;
51023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	unsigned int cpufreq_val;
52023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpumask allowed_cpus;
53023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap};
54023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic DEFINE_IDR(cpufreq_idr);
55160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhangstatic DEFINE_MUTEX(cooling_cpufreq_lock);
56023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
57160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhangstatic unsigned int cpufreq_dev_count;
58023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
59023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/* notify_table passes value to the CPUFREQ_ADJUST callback function. */
60023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap#define NOTIFY_INVALID NULL
61a0f846c23cf8e52da054abb52294d54e4b1986f9Sachin Kamatstatic struct cpufreq_cooling_device *notify_device;
62023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
63023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
64023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * get_idr - function to get a unique id.
65023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @idr: struct idr * handle used to create a id.
66023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @id: int * value generated by this function.
6779491e53dcd73175473e3293a574f5e006b468beEduardo Valentin *
6879491e53dcd73175473e3293a574f5e006b468beEduardo Valentin * This function will populate @id with an unique
6979491e53dcd73175473e3293a574f5e006b468beEduardo Valentin * id, using the idr API.
7079491e53dcd73175473e3293a574f5e006b468beEduardo Valentin *
7179491e53dcd73175473e3293a574f5e006b468beEduardo Valentin * Return: 0 on success, an error code on failure.
72023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
73023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int get_idr(struct idr *idr, int *id)
74023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
756deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo	int ret;
76023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
77023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_lock(&cooling_cpufreq_lock);
786deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo	ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
79023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_unlock(&cooling_cpufreq_lock);
806deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo	if (unlikely(ret < 0))
816deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo		return ret;
826deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo	*id = ret;
8379491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
84023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return 0;
85023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
86023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
87023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
88023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * release_idr - function to free the unique id.
89023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @idr: struct idr * handle used for creating the id.
90023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @id: int value representing the unique id.
91023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
92023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic void release_idr(struct idr *idr, int id)
93023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
94023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_lock(&cooling_cpufreq_lock);
95023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	idr_remove(idr, id);
96023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_unlock(&cooling_cpufreq_lock);
97023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
98023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
99023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/* Below code defines functions to be used for cpufreq as cooling device */
100023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
101023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
10282b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * is_cpufreq_valid - function to check frequency transitioning capability.
103023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cpu: cpu for which check is needed.
10482b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin *
10582b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * This function will check the current state of the system if
10682b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * it is capable of changing the frequency for a given @cpu.
10782b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin *
10882b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * Return: 0 if the system is not currently capable of changing
10982b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * the frequency of given cpu. !0 in case the frequency is changeable.
110023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
111023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int is_cpufreq_valid(int cpu)
112023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
113023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_policy policy;
11479491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
115023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return !cpufreq_get_policy(&policy, cpu);
116023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
117023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
118fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Ruienum cpufreq_cooling_property {
119fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	GET_LEVEL,
120fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	GET_FREQ,
121fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	GET_MAXL,
122fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui};
123fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
1242d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin/**
1252d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * get_property - fetch a property of interest for a give cpu.
1262d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * @cpu: cpu for which the property is required
1272d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * @input: query parameter
1282d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * @output: query return
1292d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * @property: type of query (frequency, level, max level)
1302d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin *
1312d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * This is the common function to
132fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * 1. get maximum cpu cooling states
133fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * 2. translate frequency to cooling state
134fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * 3. translate cooling state to frequency
135fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * Note that the code may be not in good shape
136fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * but it is written in this way in order to:
137fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * a) reduce duplicate code as most of the code can be shared.
138fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * b) make sure the logic is consistent when translating between
139fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui *    cooling states and frequencies.
1402d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin *
1412d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * Return: 0 on success, -EINVAL when invalid parameters are passed.
1422d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin */
143fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Ruistatic int get_property(unsigned int cpu, unsigned long input,
144d8892a01b13c8ea3ad74fb0c56beb96e92a2c65eEduardo Valentin			unsigned int *output,
145d8892a01b13c8ea3ad74fb0c56beb96e92a2c65eEduardo Valentin			enum cpufreq_cooling_property property)
146023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
147fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	int i, j;
1484469b99743d296e24aefc5f8ed7df1bc9cfbbac8Eduardo Valentin	unsigned long max_level = 0, level = 0;
149fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	unsigned int freq = CPUFREQ_ENTRY_INVALID;
150fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	int descend = -1;
151023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_frequency_table *table =
152023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap					cpufreq_frequency_get_table(cpu);
15379d264016ac011b74497b553022d2fc45bf9d9f2Eduardo Valentin
154fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (!output)
155fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		return -EINVAL;
156fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
157023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (!table)
158fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		return -EINVAL;
159023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
160fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
161fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* ignore invalid entries */
162023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
163023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			continue;
164023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
165fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* ignore duplicate entry */
166fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		if (freq == table[i].frequency)
167fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			continue;
168fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
169fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* get the frequency order */
17024c7a381720843f17efb42de81f7e85aefd6f616Shawn Guo		if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
171fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			descend = !!(freq > table[i].frequency);
172023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
173fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		freq = table[i].frequency;
174fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		max_level++;
175023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
176023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
177fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	/* get max level */
178fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (property == GET_MAXL) {
179fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		*output = (unsigned int)max_level;
180fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		return 0;
181fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	}
182023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
183fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (property == GET_FREQ)
184ef5e2124ec3d54182bd826411d864e0e28fc00d4Eduardo Valentin		level = descend ? input : (max_level - input - 1);
185fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
186fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	for (i = 0, j = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
187fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* ignore invalid entry */
188023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		if (table[i].frequency == CPUFREQ_ENTRY_INVALID)
189023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			continue;
190fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
191fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* ignore duplicate entry */
192fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		if (freq == table[i].frequency)
193fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			continue;
194fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
195fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* now we have a valid frequency entry */
196fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		freq = table[i].frequency;
197fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
198fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		if (property == GET_LEVEL && (unsigned int)input == freq) {
199fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			/* get level by frequency */
200fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			*output = descend ? j : (max_level - j - 1);
201fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			return 0;
202fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		}
203fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		if (property == GET_FREQ && level == j) {
204fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			/* get frequency by level */
205fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			*output = freq;
206fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			return 0;
207fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		}
208fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		j++;
209023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
21079491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
211fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	return -EINVAL;
212fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui}
213fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
21444952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin/**
21544952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * cpufreq_cooling_get_level - for a give cpu, return the cooling level.
21644952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * @cpu: cpu for which the level is required
21744952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * @freq: the frequency of interest
21844952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin *
21944952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * This function will match the cooling level corresponding to the
22044952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * requested @freq and return it.
22144952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin *
22244952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
22344952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * otherwise.
22444952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin */
22557df8106932b57427df1eaaa13871857f75b1194Zhang Ruiunsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq)
22657df8106932b57427df1eaaa13871857f75b1194Zhang Rui{
22757df8106932b57427df1eaaa13871857f75b1194Zhang Rui	unsigned int val;
22857df8106932b57427df1eaaa13871857f75b1194Zhang Rui
22957df8106932b57427df1eaaa13871857f75b1194Zhang Rui	if (get_property(cpu, (unsigned long)freq, &val, GET_LEVEL))
23057df8106932b57427df1eaaa13871857f75b1194Zhang Rui		return THERMAL_CSTATE_INVALID;
23179491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
23257df8106932b57427df1eaaa13871857f75b1194Zhang Rui	return (unsigned long)val;
23357df8106932b57427df1eaaa13871857f75b1194Zhang Rui}
234243dbd9c606ff4f925496022762c8cf5b6e4a85eEduardo ValentinEXPORT_SYMBOL_GPL(cpufreq_cooling_get_level);
23557df8106932b57427df1eaaa13871857f75b1194Zhang Rui
236fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui/**
237fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * get_cpu_frequency - get the absolute value of frequency from level.
238fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * @cpu: cpu for which frequency is fetched.
23941518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * @level: cooling level
24041518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin *
24141518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * This function matches cooling level with frequency. Based on a cooling level
24241518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * of frequency, equals cooling state of cpu cooling device, it will return
24341518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * the corresponding frequency.
244fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui *	e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
24541518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin *
24641518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * Return: 0 on error, the corresponding frequency otherwise.
247fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui */
248fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Ruistatic unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
249fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui{
250fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	int ret = 0;
251fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	unsigned int freq;
252fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
253fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	ret = get_property(cpu, level, &freq, GET_FREQ);
254fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (ret)
255fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		return 0;
25679491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
257fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	return freq;
258023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
259023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
260023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
261023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_apply_cooling - function to apply frequency clipping.
262023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cpufreq_device: cpufreq_cooling_device pointer containing frequency
263023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *	clipping data.
264023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cooling_state: value of the cooling state.
2654b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin *
2664b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin * Function used to make sure the cpufreq layer is aware of current thermal
2674b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin * limits. The limits are applied by updating the cpufreq policy.
2684b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin *
2694b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin * Return: 0 on success, an error code otherwise (-EINVAL in case wrong
2704b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin * cooling state).
271023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
272023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device,
2735fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin				 unsigned long cooling_state)
274023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
275023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	unsigned int cpuid, clip_freq;
276bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	struct cpumask *mask = &cpufreq_device->allowed_cpus;
277bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	unsigned int cpu = cpumask_any(mask);
278023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
279023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
280023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	/* Check if the old cooling action is same as new cooling action */
281023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (cpufreq_device->cpufreq_state == cooling_state)
282023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return 0;
283023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
284023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	clip_freq = get_cpu_frequency(cpu, cooling_state);
285023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (!clip_freq)
286023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return -EINVAL;
287023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
288023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_device->cpufreq_state = cooling_state;
289023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_device->cpufreq_val = clip_freq;
290023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	notify_device = cpufreq_device;
291023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
292bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	for_each_cpu(cpuid, mask) {
293023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		if (is_cpufreq_valid(cpuid))
294023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			cpufreq_update_policy(cpuid);
295023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
296023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
297023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	notify_device = NOTIFY_INVALID;
298023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
299023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return 0;
300023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
301023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
302023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
303023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_thermal_notifier - notifier callback for cpufreq policy change.
304023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @nb:	struct notifier_block * with callback info.
305023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @event: value showing cpufreq event for which this function invoked.
306023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @data: callback-specific data
307bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin *
308bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin * Callback to highjack the notification on cpufreq policy transition.
309bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin * Every time there is a change in policy, we will intercept and
310bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin * update the cpufreq policy with thermal constraints.
311bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin *
312bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin * Return: 0 (success)
313023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
314023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_thermal_notifier(struct notifier_block *nb,
3155fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin				    unsigned long event, void *data)
316023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
317023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_policy *policy = data;
318023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	unsigned long max_freq = 0;
319023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
320023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (event != CPUFREQ_ADJUST || notify_device == NOTIFY_INVALID)
321023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return 0;
322023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
323023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (cpumask_test_cpu(policy->cpu, &notify_device->allowed_cpus))
324023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		max_freq = notify_device->cpufreq_val;
325044d5c26da262fa433dacbe1c6962459050d6b06Lan Tianyu	else
326044d5c26da262fa433dacbe1c6962459050d6b06Lan Tianyu		return 0;
327023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3281b9e35265903c2e0e484dc224e8f7de506e3353fEduardo Valentin	/* Never exceed user_policy.max */
329023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (max_freq > policy->user_policy.max)
330023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		max_freq = policy->user_policy.max;
331023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
332023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (policy->max != max_freq)
333023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		cpufreq_verify_within_limits(policy, 0, max_freq);
334023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
335023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return 0;
336023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
337023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3381b9e35265903c2e0e484dc224e8f7de506e3353fEduardo Valentin/* cpufreq cooling device callback functions are defined below */
339023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
340023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
341023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_get_max_state - callback function to get the max cooling state.
342023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cdev: thermal cooling device pointer.
343023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @state: fill this variable with the max cooling state.
34462c00421b31424489435619545a53802eab07f3eEduardo Valentin *
34562c00421b31424489435619545a53802eab07f3eEduardo Valentin * Callback for the thermal cooling device to return the cpufreq
34662c00421b31424489435619545a53802eab07f3eEduardo Valentin * max cooling state.
34762c00421b31424489435619545a53802eab07f3eEduardo Valentin *
34862c00421b31424489435619545a53802eab07f3eEduardo Valentin * Return: 0 on success, an error code otherwise.
349023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
350023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
351023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap				 unsigned long *state)
352023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
353160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
354bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	struct cpumask *mask = &cpufreq_device->allowed_cpus;
355023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	unsigned int cpu;
3564f89038f177462dbd2fd911297fd004226176db7Dan Carpenter	unsigned int count = 0;
357fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	int ret;
358023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
359bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	cpu = cpumask_any(mask);
360023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3614f89038f177462dbd2fd911297fd004226176db7Dan Carpenter	ret = get_property(cpu, 0, &count, GET_MAXL);
3629c51b05a7852183ba9654ca850bee97d38e948d5hongbo.zhang
363fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (count > 0)
364fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		*state = count;
365023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
366fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	return ret;
367023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
368023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
369023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
370023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_get_cur_state - callback function to get the current cooling state.
371023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cdev: thermal cooling device pointer.
372023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @state: fill this variable with the current cooling state.
3733672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin *
3743672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin * Callback for the thermal cooling device to return the cpufreq
3753672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin * current cooling state.
3763672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin *
3773672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin * Return: 0 on success, an error code otherwise.
378023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
379023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
380023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap				 unsigned long *state)
381023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
382160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
383023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
384160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	*state = cpufreq_device->cpufreq_state;
38579491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
386160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	return 0;
387023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
388023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
389023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
390023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_set_cur_state - callback function to set the current cooling state.
391023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cdev: thermal cooling device pointer.
392023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @state: set this variable to the current cooling state.
39356e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin *
39456e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin * Callback for the thermal cooling device to change the cpufreq
39556e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin * current cooling state.
39656e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin *
39756e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin * Return: 0 on success, an error code otherwise.
398023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
399023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
400023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap				 unsigned long state)
401023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
402160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
403023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
404160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	return cpufreq_apply_cooling(cpufreq_device, state);
405023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
406023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
407023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/* Bind cpufreq callbacks to thermal cooling device ops */
408023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic struct thermal_cooling_device_ops const cpufreq_cooling_ops = {
409023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	.get_max_state = cpufreq_get_max_state,
410023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	.get_cur_state = cpufreq_get_cur_state,
411023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	.set_cur_state = cpufreq_set_cur_state,
412023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap};
413023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
414023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/* Notifier for cpufreq policy change */
415023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic struct notifier_block thermal_cpufreq_notifier_block = {
416023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	.notifier_call = cpufreq_thermal_notifier,
417023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap};
418023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
419023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
420023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_cooling_register - function to create cpufreq cooling device.
421023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @clip_cpus: cpumask of cpus where the frequency constraints will happen.
42212cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin *
42312cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * This interface function registers the cpufreq cooling device with the name
42412cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
42512cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * cooling devices.
42612cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin *
42712cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * Return: a valid struct thermal_cooling_device pointer on success,
42812cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * on failure, it returns a corresponding ERR_PTR().
429023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
4305fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentinstruct thermal_cooling_device *
4315fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentincpufreq_cooling_register(const struct cpumask *clip_cpus)
432023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
433023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct thermal_cooling_device *cool_dev;
434023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_cooling_device *cpufreq_dev = NULL;
435160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	unsigned int min = 0, max = 0;
436023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	char dev_name[THERMAL_NAME_LENGTH];
437a4b6fec977020a508ff04b05f0fa01221a4ecf29Jonghwa Lee	int ret = 0, i;
438023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_policy policy;
439023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
4401b9e35265903c2e0e484dc224e8f7de506e3353fEduardo Valentin	/* Verify that all the clip cpus have same freq_min, freq_max limit */
441023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	for_each_cpu(i, clip_cpus) {
4421b9e35265903c2e0e484dc224e8f7de506e3353fEduardo Valentin		/* continue if cpufreq policy not found and not return error */
443023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		if (!cpufreq_get_policy(&policy, i))
444023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			continue;
445023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		if (min == 0 && max == 0) {
446023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			min = policy.cpuinfo.min_freq;
447023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			max = policy.cpuinfo.max_freq;
448023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		} else {
449023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			if (min != policy.cpuinfo.min_freq ||
4505fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin			    max != policy.cpuinfo.max_freq)
451023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap				return ERR_PTR(-EINVAL);
4526b6519df84afcda294c487bccb83a8caecb48acdhongbo.zhang		}
453023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
454023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device),
4555fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin			      GFP_KERNEL);
456023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (!cpufreq_dev)
457023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return ERR_PTR(-ENOMEM);
458023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
459023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
460023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
461023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
462023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (ret) {
463023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		kfree(cpufreq_dev);
464023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return ERR_PTR(-EINVAL);
465023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
466023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
46799871a714b0a9429e960629fbc2c6c20c57ea484Eduardo Valentin	snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
46899871a714b0a9429e960629fbc2c6c20c57ea484Eduardo Valentin		 cpufreq_dev->id);
469023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
470023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cool_dev = thermal_cooling_device_register(dev_name, cpufreq_dev,
4715fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin						   &cpufreq_cooling_ops);
472023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (!cool_dev) {
473023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		release_idr(&cpufreq_idr, cpufreq_dev->id);
474023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		kfree(cpufreq_dev);
475023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return ERR_PTR(-EINVAL);
476023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
477023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_dev->cool_dev = cool_dev;
478023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_dev->cpufreq_state = 0;
479023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_lock(&cooling_cpufreq_lock);
480023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
481023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	/* Register the notifier for first cpufreq cooling device */
482023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (cpufreq_dev_count == 0)
483023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
4845fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin					  CPUFREQ_POLICY_NOTIFIER);
485160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	cpufreq_dev_count++;
486023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
487023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_unlock(&cooling_cpufreq_lock);
48879491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
489023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return cool_dev;
490023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
491243dbd9c606ff4f925496022762c8cf5b6e4a85eEduardo ValentinEXPORT_SYMBOL_GPL(cpufreq_cooling_register);
492023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
493023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
494023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_cooling_unregister - function to remove cpufreq cooling device.
495023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cdev: thermal cooling device pointer.
496135266b4ea4567419c475354437b7aaa96023d9eEduardo Valentin *
497135266b4ea4567419c475354437b7aaa96023d9eEduardo Valentin * This interface function unregisters the "thermal-cpufreq-%x" cooling device.
498023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
499023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapvoid cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
500023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
50150e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin	struct cpufreq_cooling_device *cpufreq_dev;
502023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
50350e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin	if (!cdev)
50450e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin		return;
50550e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin
50650e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin	cpufreq_dev = cdev->devdata;
507023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_lock(&cooling_cpufreq_lock);
508160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	cpufreq_dev_count--;
509023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
510023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	/* Unregister the notifier for the last cpufreq cooling device */
5112d9bf71c12be9caadd9a8276a78f3a3df8e9e852Eduardo Valentin	if (cpufreq_dev_count == 0)
512023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
5135fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin					    CPUFREQ_POLICY_NOTIFIER);
514023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_unlock(&cooling_cpufreq_lock);
515160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang
516023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
517023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	release_idr(&cpufreq_idr, cpufreq_dev->id);
518023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	kfree(cpufreq_dev);
519023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
520243dbd9c606ff4f925496022762c8cf5b6e4a85eEduardo ValentinEXPORT_SYMBOL_GPL(cpufreq_cooling_unregister);
521