PageRenderTime 36ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/drivers/thermal/samsung/gpu_cooling.c

https://bitbucket.org/corsicanu/hadeskernel-o
C | 460 lines | 213 code | 66 blank | 181 comment | 36 complexity | 134dfd86a31313210f4f839ce3054058 MD5 | raw file
Possible License(s): GPL-2.0
  1. /*
  2. * linux/drivers/thermal/gpu_cooling.c
  3. *
  4. * Copyright (C) 2012 Samsung Electronics Co., Ltd(http://www.samsung.com)
  5. * Copyright (C) 2012 Amit Daniel <amit.kachhap@linaro.org>
  6. *
  7. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; version 2 of the License.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  20. *
  21. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  22. */
  23. #include <linux/module.h>
  24. #include <linux/thermal.h>
  25. #include <linux/cpufreq.h>
  26. #include <linux/err.h>
  27. #include <linux/slab.h>
  28. #include <linux/cpu.h>
  29. #include <linux/gpu_cooling.h>
  30. #include <soc/samsung/tmu.h>
  31. #include "exynos_tmu_data.h"
  32. /**
  33. * struct gpufreq_cooling_device - data for cooling device with gpufreq
  34. * @id: unique integer value corresponding to each gpufreq_cooling_device
  35. * registered.
  36. * @cool_dev: thermal_cooling_device pointer to keep track of the
  37. * registered cooling device.
  38. * @gpufreq_state: integer value representing the current state of gpufreq
  39. * cooling devices.
  40. * @gpufreq_val: integer value representing the absolute value of the clipped
  41. * frequency.
  42. * @allowed_gpus: all the gpus involved for this gpufreq_cooling_device.
  43. *
  44. * This structure is required for keeping information of each
  45. * gpufreq_cooling_device registered. In order to prevent corruption of this a
  46. * mutex lock cooling_gpu_lock is used.
  47. */
  48. struct gpufreq_cooling_device {
  49. int id;
  50. struct thermal_cooling_device *cool_dev;
  51. unsigned long gpufreq_state;
  52. unsigned int gpufreq_val;
  53. };
  54. static DEFINE_IDR(gpufreq_idr);
  55. static DEFINE_MUTEX(cooling_gpu_lock);
  56. static BLOCKING_NOTIFIER_HEAD(gpu_notifier);
  57. static unsigned int gpufreq_dev_count;
  58. extern struct cpufreq_frequency_table gpu_freq_table[];
  59. /**
  60. * get_idr - function to get a unique id.
  61. * @idr: struct idr * handle used to create a id.
  62. * @id: int * value generated by this function.
  63. *
  64. * This function will populate @id with an unique
  65. * id, using the idr API.
  66. *
  67. * Return: 0 on success, an error code on failure.
  68. */
  69. static int get_idr(struct idr *idr, int *id)
  70. {
  71. int ret;
  72. mutex_lock(&cooling_gpu_lock);
  73. ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
  74. mutex_unlock(&cooling_gpu_lock);
  75. if (unlikely(ret < 0))
  76. return ret;
  77. *id = ret;
  78. return 0;
  79. }
  80. /**
  81. * release_idr - function to free the unique id.
  82. * @idr: struct idr * handle used for creating the id.
  83. * @id: int value representing the unique id.
  84. */
  85. static void release_idr(struct idr *idr, int id)
  86. {
  87. mutex_lock(&cooling_gpu_lock);
  88. idr_remove(idr, id);
  89. mutex_unlock(&cooling_gpu_lock);
  90. }
  91. /* Below code defines functions to be used for gpufreq as cooling device */
  92. enum gpufreq_cooling_property {
  93. GET_LEVEL,
  94. GET_FREQ,
  95. GET_MAXL,
  96. };
  97. /**
  98. * get_property - fetch a property of interest for a give gpu.
  99. * @gpu: gpu for which the property is required
  100. * @input: query parameter
  101. * @output: query return
  102. * @property: type of query (frequency, level, max level)
  103. *
  104. * This is the common function to
  105. * 1. get maximum gpu cooling states
  106. * 2. translate frequency to cooling state
  107. * 3. translate cooling state to frequency
  108. * Note that the code may be not in good shape
  109. * but it is written in this way in order to:
  110. * a) reduce duplicate code as most of the code can be shared.
  111. * b) make sure the logic is consistent when translating between
  112. * cooling states and frequencies.
  113. *
  114. * Return: 0 on success, -EINVAL when invalid parameters are passed.
  115. */
  116. static int get_property(unsigned int gpu, unsigned long input,
  117. unsigned int *output,
  118. enum gpufreq_cooling_property property)
  119. {
  120. int i;
  121. unsigned long max_level = 0, level = 0;
  122. unsigned int freq = CPUFREQ_ENTRY_INVALID;
  123. int descend = -1;
  124. struct cpufreq_frequency_table *pos, *table =
  125. gpu_freq_table;
  126. if (!output)
  127. return -EINVAL;
  128. cpufreq_for_each_valid_entry(pos, table) {
  129. /* ignore duplicate entry */
  130. if (freq == pos->frequency)
  131. continue;
  132. /* get the frequency order */
  133. if (freq != CPUFREQ_ENTRY_INVALID && descend == -1)
  134. descend = freq > pos->frequency;
  135. freq = pos->frequency;
  136. max_level++;
  137. }
  138. /* No valid cpu frequency entry */
  139. if (max_level == 0)
  140. return -EINVAL;
  141. /* max_level is an index, not a counter */
  142. max_level--;
  143. /* get max level */
  144. if (property == GET_MAXL) {
  145. *output = (unsigned int)max_level;
  146. return 0;
  147. }
  148. if (property == GET_FREQ)
  149. level = descend ? input : (max_level - input);
  150. i = 0;
  151. cpufreq_for_each_valid_entry(pos, table) {
  152. /* ignore duplicate entry */
  153. if (freq == pos->frequency)
  154. continue;
  155. /* now we have a valid frequency entry */
  156. freq = pos->frequency;
  157. if (property == GET_LEVEL && (unsigned int)input == freq) {
  158. /* get level by frequency */
  159. *output = descend ? i : (max_level - i);
  160. return 0;
  161. }
  162. if (property == GET_FREQ && level == i) {
  163. /* get frequency by level */
  164. *output = freq;
  165. return 0;
  166. }
  167. i++;
  168. }
  169. return -EINVAL;
  170. }
  171. /**
  172. * gpufreq_cooling_get_level - for a give gpu, return the cooling level.
  173. * @gpu: gpu for which the level is required
  174. * @freq: the frequency of interest
  175. *
  176. * This function will match the cooling level corresponding to the
  177. * requested @freq and return it.
  178. *
  179. * Return: The matched cooling level on success or THERMAL_CSTATE_INVALID
  180. * otherwise.
  181. */
  182. unsigned long gpufreq_cooling_get_level(unsigned int gpu, unsigned int freq)
  183. {
  184. unsigned int val;
  185. if (get_property(gpu, (unsigned long)freq, &val, GET_LEVEL))
  186. return THERMAL_CSTATE_INVALID;
  187. return (unsigned long)val;
  188. }
  189. EXPORT_SYMBOL_GPL(gpufreq_cooling_get_level);
  190. /**
  191. * gpufreq_apply_cooling - function to apply frequency clipping.
  192. * @gpufreq_device: gpufreq_cooling_device pointer containing frequency
  193. * clipping data.
  194. * @cooling_state: value of the cooling state.
  195. *
  196. * Function used to make sure the gpufreq layer is aware of current thermal
  197. * limits. The limits are applied by updating the gpufreq policy.
  198. *
  199. * Return: 0 on success, an error code otherwise (-EINVAL in case wrong
  200. * cooling state).
  201. */
  202. static int gpufreq_apply_cooling(struct gpufreq_cooling_device *gpufreq_device,
  203. unsigned long cooling_state)
  204. {
  205. /* Check if the old cooling action is same as new cooling action */
  206. if (gpufreq_device->gpufreq_state == cooling_state)
  207. return 0;
  208. gpufreq_device->gpufreq_state = cooling_state;
  209. blocking_notifier_call_chain(&gpu_notifier, GPU_THROTTLING, &cooling_state);
  210. return 0;
  211. }
  212. /* gpufreq cooling device callback functions are defined below */
  213. /**
  214. * gpufreq_get_max_state - callback function to get the max cooling state.
  215. * @cdev: thermal cooling device pointer.
  216. * @state: fill this variable with the max cooling state.
  217. *
  218. * Callback for the thermal cooling device to return the gpufreq
  219. * max cooling state.
  220. *
  221. * Return: 0 on success, an error code otherwise.
  222. */
  223. static int gpufreq_get_max_state(struct thermal_cooling_device *cdev,
  224. unsigned long *state)
  225. {
  226. unsigned int count = 0;
  227. int ret;
  228. ret = get_property(0, 0, &count, GET_MAXL);
  229. if (count > 0)
  230. *state = count;
  231. return ret;
  232. }
  233. /**
  234. * gpufreq_get_cur_state - callback function to get the current cooling state.
  235. * @cdev: thermal cooling device pointer.
  236. * @state: fill this variable with the current cooling state.
  237. *
  238. * Callback for the thermal cooling device to return the gpufreq
  239. * current cooling state.
  240. *
  241. * Return: 0 on success, an error code otherwise.
  242. */
  243. static int gpufreq_get_cur_state(struct thermal_cooling_device *cdev,
  244. unsigned long *state)
  245. {
  246. struct gpufreq_cooling_device *gpufreq_device = cdev->devdata;
  247. *state = gpufreq_device->gpufreq_state;
  248. return 0;
  249. }
  250. /**
  251. * gpufreq_set_cur_state - callback function to set the current cooling state.
  252. * @cdev: thermal cooling device pointer.
  253. * @state: set this variable to the current cooling state.
  254. *
  255. * Callback for the thermal cooling device to change the gpufreq
  256. * current cooling state.
  257. *
  258. * Return: 0 on success, an error code otherwise.
  259. */
  260. static int gpufreq_set_cur_state(struct thermal_cooling_device *cdev,
  261. unsigned long state)
  262. {
  263. struct gpufreq_cooling_device *gpufreq_device = cdev->devdata;
  264. return gpufreq_apply_cooling(gpufreq_device, state);
  265. }
  266. static enum tmu_noti_state_t gpu_tstate = GPU_COLD;
  267. int gpufreq_set_cur_temp(bool suspended, unsigned long temp)
  268. {
  269. enum tmu_noti_state_t tstate;
  270. if (temp < EXYNOS_COLD_TEMP)
  271. tstate = GPU_COLD;
  272. else
  273. tstate = GPU_NORMAL;
  274. if (gpu_tstate == tstate)
  275. return 0;
  276. gpu_tstate = tstate;
  277. blocking_notifier_call_chain(&gpu_notifier, tstate, &tstate);
  278. return 0;
  279. }
  280. /* Bind gpufreq callbacks to thermal cooling device ops */
  281. static struct thermal_cooling_device_ops const gpufreq_cooling_ops = {
  282. .get_max_state = gpufreq_get_max_state,
  283. .get_cur_state = gpufreq_get_cur_state,
  284. .set_cur_state = gpufreq_set_cur_state,
  285. };
  286. int exynos_gpu_add_notifier(struct notifier_block *n)
  287. {
  288. return blocking_notifier_chain_register(&gpu_notifier, n);
  289. }
  290. /**
  291. * __gpufreq_cooling_register - helper function to create gpufreq cooling device
  292. * @np: a valid struct device_node to the cooling device device tree node
  293. * @clip_gpus: gpumask of gpus where the frequency constraints will happen.
  294. *
  295. * This interface function registers the gpufreq cooling device with the name
  296. * "thermal-gpufreq-%x". This api can support multiple instances of gpufreq
  297. * cooling devices. It also gives the opportunity to link the cooling device
  298. * with a device tree node, in order to bind it via the thermal DT code.
  299. *
  300. * Return: a valid struct thermal_cooling_device pointer on success,
  301. * on failure, it returns a corresponding ERR_PTR().
  302. */
  303. static struct thermal_cooling_device *
  304. __gpufreq_cooling_register(struct device_node *np,
  305. const struct cpumask *clip_gpus)
  306. {
  307. struct thermal_cooling_device *cool_dev;
  308. struct gpufreq_cooling_device *gpufreq_dev = NULL;
  309. char dev_name[THERMAL_NAME_LENGTH];
  310. int ret = 0;
  311. gpufreq_dev = kzalloc(sizeof(struct gpufreq_cooling_device),
  312. GFP_KERNEL);
  313. if (!gpufreq_dev)
  314. return ERR_PTR(-ENOMEM);
  315. ret = get_idr(&gpufreq_idr, &gpufreq_dev->id);
  316. if (ret) {
  317. kfree(gpufreq_dev);
  318. return ERR_PTR(-EINVAL);
  319. }
  320. snprintf(dev_name, sizeof(dev_name), "thermal-gpufreq-%d",
  321. gpufreq_dev->id);
  322. cool_dev = thermal_of_cooling_device_register(np, dev_name, gpufreq_dev,
  323. &gpufreq_cooling_ops);
  324. if (IS_ERR(cool_dev)) {
  325. release_idr(&gpufreq_idr, gpufreq_dev->id);
  326. kfree(gpufreq_dev);
  327. return cool_dev;
  328. }
  329. gpufreq_dev->cool_dev = cool_dev;
  330. gpufreq_dev->gpufreq_state = 0;
  331. mutex_lock(&cooling_gpu_lock);
  332. gpufreq_dev_count++;
  333. mutex_unlock(&cooling_gpu_lock);
  334. return cool_dev;
  335. }
  336. /**
  337. * gpufreq_cooling_register - function to create gpufreq cooling device.
  338. * @clip_gpus: cpumask of gpus where the frequency constraints will happen.
  339. *
  340. * This interface function registers the gpufreq cooling device with the name
  341. * "thermal-gpufreq-%x". This api can support multiple instances of gpufreq
  342. * cooling devices.
  343. *
  344. * Return: a valid struct thermal_cooling_device pointer on success,
  345. * on failure, it returns a corresponding ERR_PTR().
  346. */
  347. struct thermal_cooling_device *
  348. gpufreq_cooling_register(const struct cpumask *clip_gpus)
  349. {
  350. return __gpufreq_cooling_register(NULL, clip_gpus);
  351. }
  352. EXPORT_SYMBOL_GPL(gpufreq_cooling_register);
  353. /**
  354. * of_gpufreq_cooling_register - function to create gpufreq cooling device.
  355. * @np: a valid struct device_node to the cooling device device tree node
  356. * @clip_gpus: cpumask of gpus where the frequency constraints will happen.
  357. *
  358. * This interface function registers the gpufreq cooling device with the name
  359. * "thermal-gpufreq-%x". This api can support multiple instances of gpufreq
  360. * cooling devices. Using this API, the gpufreq cooling device will be
  361. * linked to the device tree node provided.
  362. *
  363. * Return: a valid struct thermal_cooling_device pointer on success,
  364. * on failure, it returns a corresponding ERR_PTR().
  365. */
  366. struct thermal_cooling_device *
  367. of_gpufreq_cooling_register(struct device_node *np,
  368. const struct cpumask *clip_gpus)
  369. {
  370. if (!np)
  371. return ERR_PTR(-EINVAL);
  372. return __gpufreq_cooling_register(np, clip_gpus);
  373. }
  374. EXPORT_SYMBOL_GPL(of_gpufreq_cooling_register);
  375. /**
  376. * gpufreq_cooling_unregister - function to remove gpufreq cooling device.
  377. * @cdev: thermal cooling device pointer.
  378. *
  379. * This interface function unregisters the "thermal-gpufreq-%x" cooling device.
  380. */
  381. void gpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
  382. {
  383. struct gpufreq_cooling_device *gpufreq_dev;
  384. if (!cdev)
  385. return;
  386. gpufreq_dev = cdev->devdata;
  387. mutex_lock(&cooling_gpu_lock);
  388. gpufreq_dev_count--;
  389. mutex_unlock(&cooling_gpu_lock);
  390. thermal_cooling_device_unregister(gpufreq_dev->cool_dev);
  391. release_idr(&gpufreq_idr, gpufreq_dev->id);
  392. kfree(gpufreq_dev);
  393. }
  394. EXPORT_SYMBOL_GPL(gpufreq_cooling_unregister);