int3403_thermal.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  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. if (obj->tzone)
  246. thermal_zone_device_unregister(obj->tzone);
  247. return result;
  248. }
  249. static int int3403_sensor_remove(struct int3403_priv *priv)
  250. {
  251. struct int3403_sensor *obj = priv->priv;
  252. thermal_zone_device_unregister(obj->tzone);
  253. return 0;
  254. }
  255. /* INT3403 Cooling devices */
  256. static int int3403_get_max_state(struct thermal_cooling_device *cdev,
  257. unsigned long *state)
  258. {
  259. struct int3403_priv *priv = cdev->devdata;
  260. struct int3403_cdev *obj = priv->priv;
  261. *state = obj->max_state;
  262. return 0;
  263. }
  264. static int int3403_get_cur_state(struct thermal_cooling_device *cdev,
  265. unsigned long *state)
  266. {
  267. struct int3403_priv *priv = cdev->devdata;
  268. unsigned long long level;
  269. acpi_status status;
  270. status = acpi_evaluate_integer(priv->adev->handle, "PPPC", NULL, &level);
  271. if (ACPI_SUCCESS(status)) {
  272. *state = level;
  273. return 0;
  274. } else
  275. return -EINVAL;
  276. }
  277. static int
  278. int3403_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
  279. {
  280. struct int3403_priv *priv = cdev->devdata;
  281. acpi_status status;
  282. status = acpi_execute_simple_method(priv->adev->handle, "SPPC", state);
  283. if (ACPI_SUCCESS(status))
  284. return 0;
  285. else
  286. return -EINVAL;
  287. }
  288. static const struct thermal_cooling_device_ops int3403_cooling_ops = {
  289. .get_max_state = int3403_get_max_state,
  290. .get_cur_state = int3403_get_cur_state,
  291. .set_cur_state = int3403_set_cur_state,
  292. };
  293. static int int3403_cdev_add(struct int3403_priv *priv)
  294. {
  295. int result = 0;
  296. acpi_status status;
  297. struct int3403_cdev *obj;
  298. struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
  299. union acpi_object *p;
  300. obj = devm_kzalloc(&priv->pdev->dev, sizeof(*obj), GFP_KERNEL);
  301. if (!obj)
  302. return -ENOMEM;
  303. status = acpi_evaluate_object(priv->adev->handle, "PPSS", NULL, &buf);
  304. if (ACPI_FAILURE(status))
  305. return -ENODEV;
  306. p = buf.pointer;
  307. if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
  308. printk(KERN_WARNING "Invalid PPSS data\n");
  309. return -EFAULT;
  310. }
  311. obj->max_state = p->package.count - 1;
  312. obj->cdev =
  313. thermal_cooling_device_register(acpi_device_bid(priv->adev),
  314. priv, &int3403_cooling_ops);
  315. if (IS_ERR(obj->cdev))
  316. result = PTR_ERR(obj->cdev);
  317. priv->priv = obj;
  318. /* TODO: add ACPI notification support */
  319. return result;
  320. }
  321. static int int3403_cdev_remove(struct int3403_priv *priv)
  322. {
  323. struct int3403_cdev *obj = priv->priv;
  324. thermal_cooling_device_unregister(obj->cdev);
  325. return 0;
  326. }
  327. static int int3403_add(struct platform_device *pdev)
  328. {
  329. struct int3403_priv *priv;
  330. int result = 0;
  331. acpi_status status;
  332. priv = devm_kzalloc(&pdev->dev, sizeof(struct int3403_priv),
  333. GFP_KERNEL);
  334. if (!priv)
  335. return -ENOMEM;
  336. priv->pdev = pdev;
  337. priv->adev = ACPI_COMPANION(&(pdev->dev));
  338. if (!priv->adev) {
  339. result = -EINVAL;
  340. goto err;
  341. }
  342. status = acpi_evaluate_integer(priv->adev->handle, "PTYP",
  343. NULL, &priv->type);
  344. if (ACPI_FAILURE(status)) {
  345. result = -EINVAL;
  346. goto err;
  347. }
  348. platform_set_drvdata(pdev, priv);
  349. switch (priv->type) {
  350. case INT3403_TYPE_SENSOR:
  351. result = int3403_sensor_add(priv);
  352. break;
  353. case INT3403_TYPE_CHARGER:
  354. case INT3403_TYPE_BATTERY:
  355. result = int3403_cdev_add(priv);
  356. break;
  357. default:
  358. result = -EINVAL;
  359. }
  360. if (result)
  361. goto err;
  362. return result;
  363. err:
  364. return result;
  365. }
  366. static int int3403_remove(struct platform_device *pdev)
  367. {
  368. struct int3403_priv *priv = platform_get_drvdata(pdev);
  369. switch (priv->type) {
  370. case INT3403_TYPE_SENSOR:
  371. int3403_sensor_remove(priv);
  372. break;
  373. case INT3403_TYPE_CHARGER:
  374. case INT3403_TYPE_BATTERY:
  375. int3403_cdev_remove(priv);
  376. break;
  377. default:
  378. break;
  379. }
  380. return 0;
  381. }
  382. static const struct acpi_device_id int3403_device_ids[] = {
  383. {"INT3403", 0},
  384. {"", 0},
  385. };
  386. MODULE_DEVICE_TABLE(acpi, int3403_device_ids);
  387. static struct platform_driver int3403_driver = {
  388. .probe = int3403_add,
  389. .remove = int3403_remove,
  390. .driver = {
  391. .name = "int3403 thermal",
  392. .owner = THIS_MODULE,
  393. .acpi_match_table = int3403_device_ids,
  394. },
  395. };
  396. module_platform_driver(int3403_driver);
  397. MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
  398. MODULE_LICENSE("GPL v2");
  399. MODULE_DESCRIPTION("ACPI INT3403 thermal driver");