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;
532dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	struct list_head node;
54023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap};
55023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic DEFINE_IDR(cpufreq_idr);
56160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhangstatic DEFINE_MUTEX(cooling_cpufreq_lock);
57023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
58160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhangstatic unsigned int cpufreq_dev_count;
59023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
602dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brarstatic LIST_HEAD(cpufreq_dev_list);
61023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
62023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
63023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * get_idr - function to get a unique id.
64023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @idr: struct idr * handle used to create a id.
65023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @id: int * value generated by this function.
6679491e53dcd73175473e3293a574f5e006b468beEduardo Valentin *
6779491e53dcd73175473e3293a574f5e006b468beEduardo Valentin * This function will populate @id with an unique
6879491e53dcd73175473e3293a574f5e006b468beEduardo Valentin * id, using the idr API.
6979491e53dcd73175473e3293a574f5e006b468beEduardo Valentin *
7079491e53dcd73175473e3293a574f5e006b468beEduardo Valentin * Return: 0 on success, an error code on failure.
71023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
72023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int get_idr(struct idr *idr, int *id)
73023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
746deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo	int ret;
75023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
76023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_lock(&cooling_cpufreq_lock);
776deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo	ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
78023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_unlock(&cooling_cpufreq_lock);
796deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo	if (unlikely(ret < 0))
806deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo		return ret;
816deb69facebb2f9a2b15a8e5e33ab00ebc7c44cbTejun Heo	*id = ret;
8279491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
83023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return 0;
84023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
85023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
86023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
87023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * release_idr - function to free the unique id.
88023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @idr: struct idr * handle used for creating the id.
89023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @id: int value representing the unique id.
90023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
91023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic void release_idr(struct idr *idr, int id)
92023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
93023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_lock(&cooling_cpufreq_lock);
94023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	idr_remove(idr, id);
95023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_unlock(&cooling_cpufreq_lock);
96023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
97023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
98023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/* Below code defines functions to be used for cpufreq as cooling device */
99023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
100023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
10182b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * is_cpufreq_valid - function to check frequency transitioning capability.
102023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cpu: cpu for which check is needed.
10382b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin *
10482b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * This function will check the current state of the system if
10582b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * it is capable of changing the frequency for a given @cpu.
10682b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin *
10782b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * Return: 0 if the system is not currently capable of changing
10882b9ee402fa9867edde8dbf17a55f615f80bc3baEduardo Valentin * the frequency of given cpu. !0 in case the frequency is changeable.
109023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
110023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int is_cpufreq_valid(int cpu)
111023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
112023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_policy policy;
11379491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
114023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return !cpufreq_get_policy(&policy, cpu);
115023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
116023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
117fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Ruienum cpufreq_cooling_property {
118fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	GET_LEVEL,
119fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	GET_FREQ,
120fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	GET_MAXL,
121fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui};
122fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
1232d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin/**
1242d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * get_property - fetch a property of interest for a give cpu.
1252d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * @cpu: cpu for which the property is required
1262d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * @input: query parameter
1272d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * @output: query return
1282d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * @property: type of query (frequency, level, max level)
1292d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin *
1302d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * This is the common function to
131fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * 1. get maximum cpu cooling states
132fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * 2. translate frequency to cooling state
133fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * 3. translate cooling state to frequency
134fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * Note that the code may be not in good shape
135fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * but it is written in this way in order to:
136fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * a) reduce duplicate code as most of the code can be shared.
137fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * b) make sure the logic is consistent when translating between
138fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui *    cooling states and frequencies.
1392d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin *
1402d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin * Return: 0 on success, -EINVAL when invalid parameters are passed.
1412d6f28fedcd2842635a02b29e3823ba9881d5086Eduardo Valentin */
142fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Ruistatic int get_property(unsigned int cpu, unsigned long input,
143d8892a01b13c8ea3ad74fb0c56beb96e92a2c65eEduardo Valentin			unsigned int *output,
144d8892a01b13c8ea3ad74fb0c56beb96e92a2c65eEduardo Valentin			enum cpufreq_cooling_property property)
145023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
1463c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis	int i;
1474469b99743d296e24aefc5f8ed7df1bc9cfbbac8Eduardo Valentin	unsigned long max_level = 0, level = 0;
148fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	unsigned int freq = CPUFREQ_ENTRY_INVALID;
149fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	int descend = -1;
1503c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis	struct cpufreq_frequency_table *pos, *table =
151023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap					cpufreq_frequency_get_table(cpu);
15279d264016ac011b74497b553022d2fc45bf9d9f2Eduardo Valentin
153fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (!output)
154fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		return -EINVAL;
155fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
156023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (!table)
157fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		return -EINVAL;
158023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
1593c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis	cpufreq_for_each_valid_entry(pos, table) {
160fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* ignore duplicate entry */
1613c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis		if (freq == pos->frequency)
162fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			continue;
163fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
164fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* get the frequency order */
16524c7a381720843f17efb42de81f7e85aefd6f616Shawn Guo		if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
1663c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis			descend = freq > pos->frequency;
167023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
1683c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis		freq = pos->frequency;
169fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		max_level++;
170023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
171a116776f7b6052599df0c67db29c30ea9d69d7eeZhang Rui
172a116776f7b6052599df0c67db29c30ea9d69d7eeZhang Rui	/* No valid cpu frequency entry */
173a116776f7b6052599df0c67db29c30ea9d69d7eeZhang Rui	if (max_level == 0)
174a116776f7b6052599df0c67db29c30ea9d69d7eeZhang Rui		return -EINVAL;
175a116776f7b6052599df0c67db29c30ea9d69d7eeZhang Rui
1761c9573a40c1d34494419f32560f28c763c504d79Eduardo Valentin	/* max_level is an index, not a counter */
1771c9573a40c1d34494419f32560f28c763c504d79Eduardo Valentin	max_level--;
178023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
179fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	/* get max level */
180fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (property == GET_MAXL) {
181fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		*output = (unsigned int)max_level;
182fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		return 0;
183fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	}
184023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
185fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (property == GET_FREQ)
1861c9573a40c1d34494419f32560f28c763c504d79Eduardo Valentin		level = descend ? input : (max_level - input);
187fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
1883c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis	i = 0;
1893c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis	cpufreq_for_each_valid_entry(pos, table) {
190fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* ignore duplicate entry */
1913c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis		if (freq == pos->frequency)
192fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			continue;
193fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
194fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		/* now we have a valid frequency entry */
1953c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis		freq = pos->frequency;
196fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
197fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		if (property == GET_LEVEL && (unsigned int)input == freq) {
198fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			/* get level by frequency */
1993c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis			*output = descend ? i : (max_level - i);
200fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			return 0;
201fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		}
2023c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis		if (property == GET_FREQ && level == i) {
203fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			/* get frequency by level */
204fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			*output = freq;
205fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui			return 0;
206fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		}
2073c84ef3af7c5778e25145a1fef29a816730a830cStratos Karafotis		i++;
208023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
20979491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
210fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	return -EINVAL;
211fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui}
212fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
21344952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin/**
21444952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * cpufreq_cooling_get_level - for a give cpu, return the cooling level.
21544952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * @cpu: cpu for which the level is required
21644952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * @freq: the frequency of interest
21744952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin *
21844952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * This function will match the cooling level corresponding to the
21944952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * requested @freq and return it.
22044952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin *
22144952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
22244952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin * otherwise.
22344952d338ad73439b993c7a09a93a3a688e49768Eduardo Valentin */
22457df8106932b57427df1eaaa13871857f75b1194Zhang Ruiunsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq)
22557df8106932b57427df1eaaa13871857f75b1194Zhang Rui{
22657df8106932b57427df1eaaa13871857f75b1194Zhang Rui	unsigned int val;
22757df8106932b57427df1eaaa13871857f75b1194Zhang Rui
22857df8106932b57427df1eaaa13871857f75b1194Zhang Rui	if (get_property(cpu, (unsigned long)freq, &val, GET_LEVEL))
22957df8106932b57427df1eaaa13871857f75b1194Zhang Rui		return THERMAL_CSTATE_INVALID;
23079491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
23157df8106932b57427df1eaaa13871857f75b1194Zhang Rui	return (unsigned long)val;
23257df8106932b57427df1eaaa13871857f75b1194Zhang Rui}
233243dbd9c606ff4f925496022762c8cf5b6e4a85eEduardo ValentinEXPORT_SYMBOL_GPL(cpufreq_cooling_get_level);
23457df8106932b57427df1eaaa13871857f75b1194Zhang Rui
235fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui/**
236fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * get_cpu_frequency - get the absolute value of frequency from level.
237fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui * @cpu: cpu for which frequency is fetched.
23841518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * @level: cooling level
23941518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin *
24041518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * This function matches cooling level with frequency. Based on a cooling level
24141518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * of frequency, equals cooling state of cpu cooling device, it will return
24241518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * the corresponding frequency.
243fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui *	e.g level=0 --> 1st MAX FREQ, level=1 ---> 2nd MAX FREQ, .... etc
24441518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin *
24541518c41dd6f36a0a951d1850e286a44773f75caEduardo Valentin * Return: 0 on error, the corresponding frequency otherwise.
246fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui */
247fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Ruistatic unsigned int get_cpu_frequency(unsigned int cpu, unsigned long level)
248fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui{
249fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	int ret = 0;
250fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	unsigned int freq;
251fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui
252fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	ret = get_property(cpu, level, &freq, GET_FREQ);
253fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (ret)
254fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		return 0;
25579491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
256fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	return freq;
257023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
258023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
259023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
260023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_apply_cooling - function to apply frequency clipping.
261023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cpufreq_device: cpufreq_cooling_device pointer containing frequency
262023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap *	clipping data.
263023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cooling_state: value of the cooling state.
2644b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin *
2654b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin * Function used to make sure the cpufreq layer is aware of current thermal
2664b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin * limits. The limits are applied by updating the cpufreq policy.
2674b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin *
2684b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin * Return: 0 on success, an error code otherwise (-EINVAL in case wrong
2694b33deb5470cda8268d684da6d448c081b6bf641Eduardo Valentin * cooling state).
270023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
271023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_apply_cooling(struct cpufreq_cooling_device *cpufreq_device,
2725fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin				 unsigned long cooling_state)
273023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
274023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	unsigned int cpuid, clip_freq;
275bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	struct cpumask *mask = &cpufreq_device->allowed_cpus;
276bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	unsigned int cpu = cpumask_any(mask);
277023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
278023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
279023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	/* Check if the old cooling action is same as new cooling action */
280023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (cpufreq_device->cpufreq_state == cooling_state)
281023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return 0;
282023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
283023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	clip_freq = get_cpu_frequency(cpu, cooling_state);
284023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (!clip_freq)
285023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return -EINVAL;
286023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
287023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_device->cpufreq_state = cooling_state;
288023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_device->cpufreq_val = clip_freq;
289023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
290bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	for_each_cpu(cpuid, mask) {
291023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		if (is_cpufreq_valid(cpuid))
292023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			cpufreq_update_policy(cpuid);
293023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
294023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
295023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return 0;
296023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
297023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
298023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
299023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_thermal_notifier - notifier callback for cpufreq policy change.
300023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @nb:	struct notifier_block * with callback info.
301023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @event: value showing cpufreq event for which this function invoked.
302023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @data: callback-specific data
303bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin *
3049746b6e726c82f7beb902596aad263b1539e8f5aJavi Merino * Callback to hijack the notification on cpufreq policy transition.
305bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin * Every time there is a change in policy, we will intercept and
306bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin * update the cpufreq policy with thermal constraints.
307bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin *
308bab3055472b44f0897fff88f9c8d3fc55e5d7685Eduardo Valentin * Return: 0 (success)
309023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
310023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_thermal_notifier(struct notifier_block *nb,
3115fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin				    unsigned long event, void *data)
312023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
313023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_policy *policy = data;
314023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	unsigned long max_freq = 0;
3152dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	struct cpufreq_cooling_device *cpufreq_dev;
316023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3172dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	if (event != CPUFREQ_ADJUST)
318023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return 0;
319023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3202dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	mutex_lock(&cooling_cpufreq_lock);
3212dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) {
3222dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar		if (!cpumask_test_cpu(policy->cpu,
3232dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar					&cpufreq_dev->allowed_cpus))
3242dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar			continue;
3252dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar
3262dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar		if (!cpufreq_dev->cpufreq_val)
3272dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar			cpufreq_dev->cpufreq_val = get_cpu_frequency(
3282dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar					cpumask_any(&cpufreq_dev->allowed_cpus),
3292dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar					cpufreq_dev->cpufreq_state);
330023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3312dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar		max_freq = cpufreq_dev->cpufreq_val;
332023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3332dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar		if (policy->max != max_freq)
3342dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar			cpufreq_verify_within_limits(policy, 0, max_freq);
3352dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	}
3362dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	mutex_unlock(&cooling_cpufreq_lock);
337023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
338023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return 0;
339023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
340023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3411b9e35265903c2e0e484dc224e8f7de506e3353fEduardo Valentin/* cpufreq cooling device callback functions are defined below */
342023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
343023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
344023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_get_max_state - callback function to get the max cooling state.
345023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cdev: thermal cooling device pointer.
346023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @state: fill this variable with the max cooling state.
34762c00421b31424489435619545a53802eab07f3eEduardo Valentin *
34862c00421b31424489435619545a53802eab07f3eEduardo Valentin * Callback for the thermal cooling device to return the cpufreq
34962c00421b31424489435619545a53802eab07f3eEduardo Valentin * max cooling state.
35062c00421b31424489435619545a53802eab07f3eEduardo Valentin *
35162c00421b31424489435619545a53802eab07f3eEduardo Valentin * Return: 0 on success, an error code otherwise.
352023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
353023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
354023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap				 unsigned long *state)
355023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
356160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
357bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	struct cpumask *mask = &cpufreq_device->allowed_cpus;
358023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	unsigned int cpu;
3594f89038f177462dbd2fd911297fd004226176db7Dan Carpenter	unsigned int count = 0;
360fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	int ret;
361023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
362bde00663098db4d6a25681351ffa4a87eff3d0b4Laurent Navet [Mali]	cpu = cpumask_any(mask);
363023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
3644f89038f177462dbd2fd911297fd004226176db7Dan Carpenter	ret = get_property(cpu, 0, &count, GET_MAXL);
3659c51b05a7852183ba9654ca850bee97d38e948d5hongbo.zhang
366fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	if (count > 0)
367fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui		*state = count;
368023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
369fc35b35cbe24ef021ea9acfba21e54da958df747Zhang Rui	return ret;
370023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
371023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
372023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
373023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_get_cur_state - callback function to get the current cooling state.
374023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cdev: thermal cooling device pointer.
375023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @state: fill this variable with the current cooling state.
3763672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin *
3773672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin * Callback for the thermal cooling device to return the cpufreq
3783672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin * current cooling state.
3793672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin *
3803672552dc0c5f6ffacb611a7ddb4ddbd8b3adb68Eduardo Valentin * Return: 0 on success, an error code otherwise.
381023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
382023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
383023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap				 unsigned long *state)
384023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
385160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
386023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
387160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	*state = cpufreq_device->cpufreq_state;
38879491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
389160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	return 0;
390023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
391023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
392023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
393023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_set_cur_state - callback function to set the current cooling state.
394023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cdev: thermal cooling device pointer.
395023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @state: set this variable to the current cooling state.
39656e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin *
39756e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin * Callback for the thermal cooling device to change the cpufreq
39856e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin * current cooling state.
39956e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin *
40056e05fdb6d1fe02b7d28391332cf65a77bffd691Eduardo Valentin * Return: 0 on success, an error code otherwise.
401023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
402023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
403023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap				 unsigned long state)
404023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
405160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
406023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
407160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	return cpufreq_apply_cooling(cpufreq_device, state);
408023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
409023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
410023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/* Bind cpufreq callbacks to thermal cooling device ops */
411023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic struct thermal_cooling_device_ops const cpufreq_cooling_ops = {
412023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	.get_max_state = cpufreq_get_max_state,
413023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	.get_cur_state = cpufreq_get_cur_state,
414023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	.set_cur_state = cpufreq_set_cur_state,
415023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap};
416023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
417023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/* Notifier for cpufreq policy change */
418023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapstatic struct notifier_block thermal_cpufreq_notifier_block = {
419023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	.notifier_call = cpufreq_thermal_notifier,
420023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap};
421023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
422023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
42339d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * __cpufreq_cooling_register - helper function to create cpufreq cooling device
42439d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * @np: a valid struct device_node to the cooling device device tree node
425023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @clip_cpus: cpumask of cpus where the frequency constraints will happen.
42612cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin *
42712cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * This interface function registers the cpufreq cooling device with the name
42812cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
42939d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * cooling devices. It also gives the opportunity to link the cooling device
43039d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * with a device tree node, in order to bind it via the thermal DT code.
43112cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin *
43212cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * Return: a valid struct thermal_cooling_device pointer on success,
43312cb08ba50b73be97e555bcdf84e8f21a196794bEduardo Valentin * on failure, it returns a corresponding ERR_PTR().
434023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
43539d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentinstatic struct thermal_cooling_device *
43639d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin__cpufreq_cooling_register(struct device_node *np,
43739d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin			   const struct cpumask *clip_cpus)
438023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
439023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct thermal_cooling_device *cool_dev;
440023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_cooling_device *cpufreq_dev = NULL;
441160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	unsigned int min = 0, max = 0;
442023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	char dev_name[THERMAL_NAME_LENGTH];
443a4b6fec977020a508ff04b05f0fa01221a4ecf29Jonghwa Lee	int ret = 0, i;
444023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	struct cpufreq_policy policy;
445023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
4461b9e35265903c2e0e484dc224e8f7de506e3353fEduardo Valentin	/* Verify that all the clip cpus have same freq_min, freq_max limit */
447023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	for_each_cpu(i, clip_cpus) {
4481b9e35265903c2e0e484dc224e8f7de506e3353fEduardo Valentin		/* continue if cpufreq policy not found and not return error */
449023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		if (!cpufreq_get_policy(&policy, i))
450023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			continue;
451023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		if (min == 0 && max == 0) {
452023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			min = policy.cpuinfo.min_freq;
453023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			max = policy.cpuinfo.max_freq;
454023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		} else {
455023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap			if (min != policy.cpuinfo.min_freq ||
4565fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin			    max != policy.cpuinfo.max_freq)
457023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap				return ERR_PTR(-EINVAL);
4586b6519df84afcda294c487bccb83a8caecb48acdhongbo.zhang		}
459023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
460023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_dev = kzalloc(sizeof(struct cpufreq_cooling_device),
4615fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin			      GFP_KERNEL);
462023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (!cpufreq_dev)
463023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return ERR_PTR(-ENOMEM);
464023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
465023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
466023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
467023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	ret = get_idr(&cpufreq_idr, &cpufreq_dev->id);
468023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (ret) {
469023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		kfree(cpufreq_dev);
470023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		return ERR_PTR(-EINVAL);
471023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
472023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
47399871a714b0a9429e960629fbc2c6c20c57ea484Eduardo Valentin	snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
47499871a714b0a9429e960629fbc2c6c20c57ea484Eduardo Valentin		 cpufreq_dev->id);
475023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
47639d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin	cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
47739d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin						      &cpufreq_cooling_ops);
47873b9bcd76d13716cc0e0ab053f8c1ae41f47636eWei Yongjun	if (IS_ERR(cool_dev)) {
479023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		release_idr(&cpufreq_idr, cpufreq_dev->id);
480023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		kfree(cpufreq_dev);
48173b9bcd76d13716cc0e0ab053f8c1ae41f47636eWei Yongjun		return cool_dev;
482023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	}
483023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_dev->cool_dev = cool_dev;
484023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	cpufreq_dev->cpufreq_state = 0;
485023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_lock(&cooling_cpufreq_lock);
486023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
487023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	/* Register the notifier for first cpufreq cooling device */
488023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	if (cpufreq_dev_count == 0)
489023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
4905fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin					  CPUFREQ_POLICY_NOTIFIER);
491160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	cpufreq_dev_count++;
4922dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	list_add(&cpufreq_dev->node, &cpufreq_dev_list);
493023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
494023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_unlock(&cooling_cpufreq_lock);
49579491e53dcd73175473e3293a574f5e006b468beEduardo Valentin
496023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	return cool_dev;
497023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
49839d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin
49939d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin/**
50039d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * cpufreq_cooling_register - function to create cpufreq cooling device.
50139d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * @clip_cpus: cpumask of cpus where the frequency constraints will happen.
50239d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin *
50339d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * This interface function registers the cpufreq cooling device with the name
50439d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
50539d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * cooling devices.
50639d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin *
50739d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * Return: a valid struct thermal_cooling_device pointer on success,
50839d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * on failure, it returns a corresponding ERR_PTR().
50939d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin */
51039d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentinstruct thermal_cooling_device *
51139d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentincpufreq_cooling_register(const struct cpumask *clip_cpus)
51239d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin{
51339d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin	return __cpufreq_cooling_register(NULL, clip_cpus);
51439d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin}
515243dbd9c606ff4f925496022762c8cf5b6e4a85eEduardo ValentinEXPORT_SYMBOL_GPL(cpufreq_cooling_register);
516023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
517023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap/**
51839d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * of_cpufreq_cooling_register - function to create cpufreq cooling device.
51939d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * @np: a valid struct device_node to the cooling device device tree node
52039d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * @clip_cpus: cpumask of cpus where the frequency constraints will happen.
52139d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin *
52239d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * This interface function registers the cpufreq cooling device with the name
52339d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * "thermal-cpufreq-%x". This api can support multiple instances of cpufreq
52439d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * cooling devices. Using this API, the cpufreq cooling device will be
52539d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * linked to the device tree node provided.
52639d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin *
52739d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * Return: a valid struct thermal_cooling_device pointer on success,
52839d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin * on failure, it returns a corresponding ERR_PTR().
52939d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin */
53039d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentinstruct thermal_cooling_device *
53139d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentinof_cpufreq_cooling_register(struct device_node *np,
53239d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin			    const struct cpumask *clip_cpus)
53339d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin{
53439d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin	if (!np)
53539d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin		return ERR_PTR(-EINVAL);
53639d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin
53739d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin	return __cpufreq_cooling_register(np, clip_cpus);
53839d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin}
53939d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo ValentinEXPORT_SYMBOL_GPL(of_cpufreq_cooling_register);
54039d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin
54139d99cff76bf2992fd6dd4b1fc62da139e62e90cEduardo Valentin/**
542023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * cpufreq_cooling_unregister - function to remove cpufreq cooling device.
543023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap * @cdev: thermal cooling device pointer.
544135266b4ea4567419c475354437b7aaa96023d9eEduardo Valentin *
545135266b4ea4567419c475354437b7aaa96023d9eEduardo Valentin * This interface function unregisters the "thermal-cpufreq-%x" cooling device.
546023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap */
547023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhapvoid cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
548023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap{
54950e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin	struct cpufreq_cooling_device *cpufreq_dev;
550023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
55150e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin	if (!cdev)
55250e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin		return;
55350e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin
55450e66c7ed8a1cd7e933628f9f5cf2617394adf5aEduardo Valentin	cpufreq_dev = cdev->devdata;
555023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_lock(&cooling_cpufreq_lock);
5562dcd851fe4b4fe60c2f8520bf7668d7e9b2dd76bYadwinder Singh Brar	list_del(&cpufreq_dev->node);
557160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang	cpufreq_dev_count--;
558023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap
559023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	/* Unregister the notifier for the last cpufreq cooling device */
5602d9bf71c12be9caadd9a8276a78f3a3df8e9e852Eduardo Valentin	if (cpufreq_dev_count == 0)
561023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap		cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
5625fda7f680a44c65ce44fbe37f254b8a82f49d241Eduardo Valentin					    CPUFREQ_POLICY_NOTIFIER);
563023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	mutex_unlock(&cooling_cpufreq_lock);
564160b7d8048b87cb594e1a22b5345b468b6c2c40ehongbo.zhang
565023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
566023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	release_idr(&cpufreq_idr, cpufreq_dev->id);
567023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap	kfree(cpufreq_dev);
568023614183768a7ac62898bded5ec6c0c9fecbdd9Amit Daniel Kachhap}
569243dbd9c606ff4f925496022762c8cf5b6e4a85eEduardo ValentinEXPORT_SYMBOL_GPL(cpufreq_cooling_unregister);
570