int3403_thermal.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /*
  2. * ACPI INT3403 thermal driver
  3. * Copyright (c) 2013, Intel Corporation.
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms and conditions of the GNU General Public License,
  7. * version 2, as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope it will be useful, but WITHOUT
  10. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  12. * more details.
  13. */
  14. #include <linux/kernel.h>
  15. #include <linux/module.h>
  16. #include <linux/init.h>
  17. #include <linux/types.h>
  18. #include <linux/acpi.h>
  19. #include <linux/thermal.h>
  20. #include <linux/platform_device.h>
  21. #define INT3403_TYPE_SENSOR 0x03
  22. #define INT3403_TYPE_CHARGER 0x0B
  23. #define INT3403_TYPE_BATTERY 0x0C
  24. #define INT3403_PERF_CHANGED_EVENT 0x80
  25. #define INT3403_THERMAL_EVENT 0x90
  26. #define DECI_KELVIN_TO_MILLI_CELSIUS(t, off) (((t) - (off)) * 100)
  27. #define KELVIN_OFFSET 2732
  28. #define MILLI_CELSIUS_TO_DECI_KELVIN(t, off) (((t) / 100) + (off))
  29. struct int3403_sensor {
  30. struct thermal_zone_device *tzone;
  31. unsigned long *thresholds;
  32. unsigned long crit_temp;
  33. int crit_trip_id;
  34. unsigned long psv_temp;
  35. int psv_trip_id;
  36. };
  37. struct int3403_performance_state {
  38. u64 performance;
  39. u64 power;
  40. u64 latency;
  41. u64 linear;
  42. u64 control;
  43. u64 raw_performace;
  44. char *raw_unit;
  45. int reserved;
  46. };
  47. struct int3403_cdev {
  48. struct thermal_cooling_device *cdev;
  49. unsigned long max_state;
  50. };
  51. struct int3403_priv {
  52. struct platform_device *pdev;
  53. struct acpi_device *adev;
  54. unsigned long long type;
  55. void *priv;
  56. };
  57. static int sys_get_curr_temp(struct thermal_zone_device *tzone,
  58. unsigned long *temp)
  59. {
  60. struct int3403_priv *priv = tzone->devdata;
  61. struct acpi_device *device = priv->adev;
  62. unsigned long long tmp;
  63. acpi_status status;
  64. status = acpi_evaluate_integer(device->handle, "_TMP", NULL, &tmp);
  65. if (ACPI_FAILURE(status))
  66. return -EIO;
  67. *temp = DECI_KELVIN_TO_MILLI_CELSIUS(tmp, KELVIN_OFFSET);
  68. return 0;
  69. }
  70. static int sys_get_trip_hyst(struct thermal_zone_device *tzone,
  71. int trip, unsigned long *temp)
  72. {
  73. struct int3403_priv *priv = tzone->devdata;
  74. struct acpi_device *device = priv->adev;
  75. unsigned long long hyst;
  76. acpi_status status;
  77. status = acpi_evaluate_integer(device->handle, "GTSH", NULL, &hyst);
  78. if (ACPI_FAILURE(status))
  79. return -EIO;
  80. /*
  81. * Thermal hysteresis represents a temperature difference.
  82. * Kelvin and Celsius have same degree size. So the
  83. * conversion here between tenths of degree Kelvin unit
  84. * and Milli-Celsius unit is just to multiply 100.
  85. */
  86. *temp = hyst * 100;
  87. return 0;
  88. }
  89. static int sys_get_trip_temp(struct thermal_zone_device *tzone,
  90. int trip, unsigned long *temp)
  91. {
  92. struct int3403_priv *priv = tzone->devdata;
  93. struct int3403_sensor *obj = priv->priv;
  94. if (priv->type != INT3403_TYPE_SENSOR || !obj)
  95. return -EINVAL;
  96. if (trip == obj->crit_trip_id)
  97. *temp = obj->crit_temp;
  98. else if (trip == obj->psv_trip_id)
  99. *temp = obj->psv_temp;
  100. else {
  101. /*
  102. * get_trip_temp is a mandatory callback but
  103. * PATx method doesn't return any value, so return
  104. * cached value, which was last set from user space
  105. */
  106. *temp = obj->thresholds[trip];
  107. }
  108. return 0;
  109. }
  110. static int sys_get_trip_type(struct thermal_zone_device *thermal,
  111. int trip, enum thermal_trip_type *type)
  112. {
  113. struct int3403_priv *priv = thermal->devdata;
  114. struct int3403_sensor *obj = priv->priv;
  115. /* Mandatory callback, may not mean much here */
  116. if (trip == obj->crit_trip_id)
  117. *type = THERMAL_TRIP_CRITICAL;
  118. else
  119. *type = THERMAL_TRIP_PASSIVE;
  120. return 0;
  121. }
  122. int sys_set_trip_temp(struct thermal_zone_device *tzone, int trip,
  123. unsigned long temp)
  124. {
  125. struct int3403_priv *priv = tzone->devdata;
  126. struct acpi_device *device = priv->adev;
  127. struct int3403_sensor *obj = priv->priv;
  128. acpi_status status;
  129. char name[10];
  130. int ret = 0;
  131. snprintf(name, sizeof(name), "PAT%d", trip);
  132. if (acpi_has_method(device->handle, name)) {
  133. status = acpi_execute_simple_method(device->handle, name,
  134. MILLI_CELSIUS_TO_DECI_KELVIN(temp,
  135. KELVIN_OFFSET));
  136. if (ACPI_FAILURE(status))
  137. ret = -EIO;
  138. else
  139. obj->thresholds[trip] = temp;
  140. } else {
  141. ret = -EIO;
  142. dev_err(&device->dev, "sys_set_trip_temp: method not found\n");
  143. }
  144. return ret;
  145. }
  146. static struct thermal_zone_device_ops tzone_ops = {
  147. .get_temp = sys_get_curr_temp,
  148. .get_trip_temp = sys_get_trip_temp,
  149. .get_trip_type = sys_get_trip_type,
  150. .set_trip_temp = sys_set_trip_temp,
  151. .get_trip_hyst = sys_get_trip_hyst,
  152. };
  153. static struct thermal_zone_params int3403_thermal_params = {
  154. .governor_name = "user_space",
  155. .no_hwmon = true,
  156. };
  157. static void int3403_notify(acpi_handle handle,
  158. u32 event, void *data)
  159. {
  160. struct int3403_priv *priv = data;
  161. struct int3403_sensor *obj;
  162. if (!priv)
  163. return;
  164. obj = priv->priv;
  165. if (priv->type != INT3403_TYPE_SENSOR || !obj)
  166. return;
  167. switch (event) {
  168. case INT3403_PERF_CHANGED_EVENT:
  169. break;
  170. case INT3403_THERMAL_EVENT:
  171. thermal_zone_device_update(obj->tzone);
  172. break;
  173. default:
  174. dev_err(&priv->pdev->dev, "Unsupported event [0x%x]\n", event);
  175. break;
  176. }
  177. }
  178. static int sys_get_trip_crt(struct acpi_device *device, unsigned long *temp)
  179. {
  180. unsigned long long crt;
  181. acpi_status status;
  182. status = acpi_evaluate_integer(device->handle, "_CRT", NULL, &crt);
  183. if (ACPI_FAILURE(status))
  184. return -EIO;
  185. *temp = DECI_KELVIN_TO_MILLI_CELSIUS(crt, KELVIN_OFFSET);
  186. return 0;
  187. }
  188. static int sys_get_trip_psv(struct acpi_device *device, unsigned long *temp)
  189. {
  190. unsigned long long psv;
  191. acpi_status status;
  192. status = acpi_evaluate_integer(device->handle, "_PSV", NULL, &psv);
  193. if (ACPI_FAILURE(status))
  194. return -EIO;
  195. *temp = DECI_KELVIN_TO_MILLI_CELSIUS(psv, KELVIN_OFFSET);
  196. return 0;
  197. }
  198. static int int3403_sensor_add(struct int3403_priv *priv)
  199. {
  200. int result = 0;
  201. acpi_status status;
  202. struct int3403_sensor *obj;
  203. unsigned long long trip_cnt;
  204. int trip_mask = 0;
  205. obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
  206. if (!obj)
  207. return -ENOMEM;
  208. priv->priv = obj;
  209. status = acpi_evaluate_integer(priv->adev->handle, "PATC", NULL,
  210. &trip_cnt);
  211. if (ACPI_FAILURE(status))
  212. trip_cnt = 0;
  213. if (trip_cnt) {
  214. /* We have to cache, thresholds can't be readback */
  215. obj->thresholds = devm_kzalloc(&priv->pdev->dev,
  216. sizeof(*obj->thresholds) * trip_cnt,
  217. GFP_KERNEL);
  218. if (!obj->thresholds) {
  219. result = -ENOMEM;
  220. goto err_free_obj;
  221. }
  222. trip_mask = BIT(trip_cnt) - 1;
  223. }
  224. obj->psv_trip_id = -1;
  225. if (!sys_get_trip_psv(priv->adev, &obj->psv_temp))
  226. obj->psv_trip_id = trip_cnt++;
  227. obj->crit_trip_id = -1;
  228. if (!sys_get_trip_crt(priv->adev, &obj->crit_temp))
  229. obj->crit_trip_id = trip_cnt++;
  230. obj->tzone = thermal_zone_device_register(acpi_device_bid(priv->adev),
  231. trip_cnt, trip_mask, priv, &tzone_ops,
  232. &int3403_thermal_params, 0, 0);
  233. if (IS_ERR(obj->tzone)) {
  234. result = PTR_ERR(obj->tzone);
  235. obj->tzone = NULL;
  236. goto err_free_obj;
  237. }
  238. result = acpi_install_notify_handler(priv->adev->handle,
  239. ACPI_DEVICE_NOTIFY, int3403_notify,
  240. (void *)priv);
  241. if (result)
  242. goto err_free_obj;
  243. return 0;
  244. err_free_obj:
  245. thermal_zone_device_unregister(obj->tzone);
  246. return result;
  247. }
  248. static int int3403_sensor_remove(struct int3403_priv *priv)
  249. {
  250. struct int3403_sensor *obj = priv->priv;
  251. acpi_remove_notify_handler(priv->adev->handle,
  252. ACPI_DEVICE_NOTIFY, int3403_notify);
  253. thermal_zone_device_unregister(obj->tzone);
  254. return 0;
  255. }
  256. /* INT3403 Cooling devices */
  257. static int int3403_get_max_state(struct thermal_cooling_device *cdev,
  258. unsigned long *state)
  259. {
  260. struct int3403_priv *priv = cdev->devdata;
  261. struct int3403_cdev *obj = priv->priv;
  262. *state = obj->max_state;
  263. return 0;
  264. }
  265. static int int3403_get_cur_state(struct thermal_cooling_device *cdev,
  266. unsigned long *state)
  267. {
  268. struct int3403_priv *priv = cdev->devdata;
  269. unsigned long long level;
  270. acpi_status status;
  271. status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level);
  272. if (ACPI_SUCCESS(status)) {
  273. *state = level;
  274. return 0;
  275. } else
  276. return -EINVAL;
  277. }
  278. static int
  279. int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
  280. {
  281. struct int3403_priv *priv = cdev->devdata;
  282. acpi_status status;
  283. status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state);
  284. if (ACPI_SUCCESS(status))
  285. return 0;
  286. else
  287. return -EINVAL;
  288. }
  289. static const struct thermal_cooling_device_ops int3403_cooling_ops = {
  290. .get_max_state = int3403_get_max_state,
  291. .get_cur_state = int3403_get_cur_state,
  292. .set_cur_state = int3403_set_cur_state,
  293. };
  294. static int int3403_cdev_add(struct int3403_priv *priv)
  295. {
  296. int result = 0;
  297. acpi_status status;
  298. struct int3403_cdev *obj;
  299. struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
  300. union acpi_object *p;
  301. obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
  302. if (!obj)
  303. return -ENOMEM;
  304. status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf);
  305. if (ACPI_FAILURE(status))
  306. return -ENODEV;
  307. p = buf.pointer;
  308. if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
  309. printk(KERN_WARNING "Invalid PPSS data\n");
  310. kfree(buf.pointer);
  311. return -EFAULT;
  312. }
  313. obj->max_state = p->package.count - 1;
  314. obj->cdev =
  315. thermal_cooling_device_register(acpi_device_bid(priv->adev),
  316. priv, &int3403_cooling_ops);
  317. if (IS_ERR(obj->cdev))
  318. result = PTR_ERR(obj->cdev);
  319. priv->priv = obj;
  320. kfree(buf.pointer);
  321. /* TODO: add ACPI notification support */
  322. return result;
  323. }
  324. static int int3403_cdev_remove(struct int3403_priv *priv)
  325. {
  326. struct int3403_cdev *obj = priv->priv;
  327. thermal_cooling_device_unregister(obj->cdev);
  328. return 0;
  329. }
  330. static int int3403_add(struct platform_device *pdev)
  331. {
  332. struct int3403_priv *priv;
  333. int result = 0;
  334. acpi_status status;
  335. priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv),
  336. GFP_KERNEL);
  337. if (!priv)
  338. return -ENOMEM;
  339. priv->pdev = pdev;
  340. priv->adev = ACPI_COMPANION(&(pdev->dev));
  341. if (!priv->adev) {
  342. result = -EINVAL;
  343. goto err;
  344. }
  345. status = acpi_evaluate_integer(priv->adev->handle, "PTYP",
  346. NULL, &priv->type);
  347. if (ACPI_FAILURE(status)) {
  348. result = -EINVAL;
  349. goto err;
  350. }
  351. platform_set_drvdata(pdev, priv);
  352. switch (priv->type) {
  353. case INT3403_TYPE_SENSOR:
  354. result = int3403_sensor_add(priv);
  355. break;
  356. case INT3403_TYPE_CHARGER:
  357. case INT3403_TYPE_BATTERY:
  358. result = int3403_cdev_add(priv);
  359. break;
  360. default:
  361. result = -EINVAL;
  362. }
  363. if (result)
  364. goto err;
  365. return result;
  366. err:
  367. return result;
  368. }
  369. static int int3403_remove(struct platform_device *pdev)
  370. {
  371. struct int3403_priv *priv = platform_get_drvdata(pdev);
  372. switch (priv->type) {
  373. case INT3403_TYPE_SENSOR:
  374. int3403_sensor_remove(priv);
  375. break;
  376. case INT3403_TYPE_CHARGER:
  377. case INT3403_TYPE_BATTERY:
  378. int3403_cdev_remove(priv);
  379. break;
  380. default:
  381. break;
  382. }
  383. return 0;
  384. }
  385. static const struct acpi_device_id int3403_device_ids[] = {
  386. {"INT3403", 0},
  387. {"", 0},
  388. };
  389. MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
  390. static struct platform_driver int3403_driver = {
  391. .probe = int3403_add,
  392. .remove = int3403_remove,
  393. .driver = {
  394. .name = "int3403 thermal",
  395. .acpi_match_table = int3403_device_ids,
  396. },
  397. };
  398. module_platform_driver(int3403_driver);
  399. MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
  400. MODULE_LICENSE("GPL v2");
  401. MODULE_DESCRIPTION("ACPI INT3403 thermal driver");