imx_thermal.c 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. /*
  2. * Copyright 2013 Freescale Semiconductor, Inc.
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 as
  6. * published by the Free Software Foundation.
  7. *
  8. */
  9. #include <linux/cpu_cooling.h>
  10. #include <linux/cpufreq.h>
  11. #include <linux/delay.h>
  12. #include <linux/device.h>
  13. #include <linux/init.h>
  14. #include <linux/io.h>
  15. #include <linux/kernel.h>
  16. #include <linux/mfd/syscon.h>
  17. #include <linux/module.h>
  18. #include <linux/of.h>
  19. #include <linux/platform_device.h>
  20. #include <linux/regmap.h>
  21. #include <linux/slab.h>
  22. #include <linux/thermal.h>
  23. #include <linux/types.h>
  24. #define REG_SET 0x4
  25. #define REG_CLR 0x8
  26. #define REG_TOG 0xc
  27. #define MISC0 0x0150
  28. #define MISC0_REFTOP_SELBIASOFF (1 << 3)
  29. #define TEMPSENSE0 0x0180
  30. #define TEMPSENSE0_TEMP_CNT_SHIFT 8
  31. #define TEMPSENSE0_TEMP_CNT_MASK (0xfff << TEMPSENSE0_TEMP_CNT_SHIFT)
  32. #define TEMPSENSE0_FINISHED (1 << 2)
  33. #define TEMPSENSE0_MEASURE_TEMP (1 << 1)
  34. #define TEMPSENSE0_POWER_DOWN (1 << 0)
  35. #define TEMPSENSE1 0x0190
  36. #define TEMPSENSE1_MEASURE_FREQ 0xffff
  37. #define OCOTP_ANA1 0x04e0
  38. /* The driver supports 1 passive trip point and 1 critical trip point */
  39. enum imx_thermal_trip {
  40. IMX_TRIP_PASSIVE,
  41. IMX_TRIP_CRITICAL,
  42. IMX_TRIP_NUM,
  43. };
  44. /*
  45. * It defines the temperature in millicelsius for passive trip point
  46. * that will trigger cooling action when crossed.
  47. */
  48. #define IMX_TEMP_PASSIVE 85000
  49. /*
  50. * The maximum die temperature on imx parts is 105C, let's give some cushion
  51. * for noise and possible temperature rise between measurements.
  52. */
  53. #define IMX_TEMP_CRITICAL 100000
  54. #define IMX_POLLING_DELAY 2000 /* millisecond */
  55. #define IMX_PASSIVE_DELAY 1000
  56. struct imx_thermal_data {
  57. struct thermal_zone_device *tz;
  58. struct thermal_cooling_device *cdev;
  59. enum thermal_device_mode mode;
  60. struct regmap *tempmon;
  61. int c1, c2; /* See formula in imx_get_sensor_data() */
  62. };
  63. static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp)
  64. {
  65. struct imx_thermal_data *data = tz->devdata;
  66. struct regmap *map = data->tempmon;
  67. static unsigned long last_temp;
  68. unsigned int n_meas;
  69. u32 val;
  70. /*
  71. * Every time we measure the temperature, we will power on the
  72. * temperature sensor, enable measurements, take a reading,
  73. * disable measurements, power off the temperature sensor.
  74. */
  75. regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
  76. regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_MEASURE_TEMP);
  77. /*
  78. * According to the temp sensor designers, it may require up to ~17us
  79. * to complete a measurement.
  80. */
  81. usleep_range(20, 50);
  82. regmap_read(map, TEMPSENSE0, &val);
  83. regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
  84. regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
  85. if ((val & TEMPSENSE0_FINISHED) == 0) {
  86. dev_dbg(&tz->device, "temp measurement never finished\n");
  87. return -EAGAIN;
  88. }
  89. n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT;
  90. /* See imx_get_sensor_data() for formula derivation */
  91. *temp = data->c2 + data->c1 * n_meas;
  92. if (*temp != last_temp) {
  93. dev_dbg(&tz->device, "millicelsius: %ld\n", *temp);
  94. last_temp = *temp;
  95. }
  96. return 0;
  97. }
  98. static int imx_get_mode(struct thermal_zone_device *tz,
  99. enum thermal_device_mode *mode)
  100. {
  101. struct imx_thermal_data *data = tz->devdata;
  102. *mode = data->mode;
  103. return 0;
  104. }
  105. static int imx_set_mode(struct thermal_zone_device *tz,
  106. enum thermal_device_mode mode)
  107. {
  108. struct imx_thermal_data *data = tz->devdata;
  109. if (mode == THERMAL_DEVICE_ENABLED) {
  110. tz->polling_delay = IMX_POLLING_DELAY;
  111. tz->passive_delay = IMX_PASSIVE_DELAY;
  112. } else {
  113. tz->polling_delay = 0;
  114. tz->passive_delay = 0;
  115. }
  116. data->mode = mode;
  117. thermal_zone_device_update(tz);
  118. return 0;
  119. }
  120. static int imx_get_trip_type(struct thermal_zone_device *tz, int trip,
  121. enum thermal_trip_type *type)
  122. {
  123. *type = (trip == IMX_TRIP_PASSIVE) ? THERMAL_TRIP_PASSIVE :
  124. THERMAL_TRIP_CRITICAL;
  125. return 0;
  126. }
  127. static int imx_get_crit_temp(struct thermal_zone_device *tz,
  128. unsigned long *temp)
  129. {
  130. *temp = IMX_TEMP_CRITICAL;
  131. return 0;
  132. }
  133. static int imx_get_trip_temp(struct thermal_zone_device *tz, int trip,
  134. unsigned long *temp)
  135. {
  136. *temp = (trip == IMX_TRIP_PASSIVE) ? IMX_TEMP_PASSIVE :
  137. IMX_TEMP_CRITICAL;
  138. return 0;
  139. }
  140. static int imx_bind(struct thermal_zone_device *tz,
  141. struct thermal_cooling_device *cdev)
  142. {
  143. int ret;
  144. ret = thermal_zone_bind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev,
  145. THERMAL_NO_LIMIT,
  146. THERMAL_NO_LIMIT);
  147. if (ret) {
  148. dev_err(&tz->device,
  149. "binding zone %s with cdev %s failed:%d\n",
  150. tz->type, cdev->type, ret);
  151. return ret;
  152. }
  153. return 0;
  154. }
  155. static int imx_unbind(struct thermal_zone_device *tz,
  156. struct thermal_cooling_device *cdev)
  157. {
  158. int ret;
  159. ret = thermal_zone_unbind_cooling_device(tz, IMX_TRIP_PASSIVE, cdev);
  160. if (ret) {
  161. dev_err(&tz->device,
  162. "unbinding zone %s with cdev %s failed:%d\n",
  163. tz->type, cdev->type, ret);
  164. return ret;
  165. }
  166. return 0;
  167. }
  168. static const struct thermal_zone_device_ops imx_tz_ops = {
  169. .bind = imx_bind,
  170. .unbind = imx_unbind,
  171. .get_temp = imx_get_temp,
  172. .get_mode = imx_get_mode,
  173. .set_mode = imx_set_mode,
  174. .get_trip_type = imx_get_trip_type,
  175. .get_trip_temp = imx_get_trip_temp,
  176. .get_crit_temp = imx_get_crit_temp,
  177. };
  178. static int imx_get_sensor_data(struct platform_device *pdev)
  179. {
  180. struct imx_thermal_data *data = platform_get_drvdata(pdev);
  181. struct regmap *map;
  182. int t1, t2, n1, n2;
  183. int ret;
  184. u32 val;
  185. map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
  186. "fsl,tempmon-data");
  187. if (IS_ERR(map)) {
  188. ret = PTR_ERR(map);
  189. dev_err(&pdev->dev, "failed to get sensor regmap: %d\n", ret);
  190. return ret;
  191. }
  192. ret = regmap_read(map, OCOTP_ANA1, &val);
  193. if (ret) {
  194. dev_err(&pdev->dev, "failed to read sensor data: %d\n", ret);
  195. return ret;
  196. }
  197. if (val == 0 || val == ~0) {
  198. dev_err(&pdev->dev, "invalid sensor calibration data\n");
  199. return -EINVAL;
  200. }
  201. /*
  202. * Sensor data layout:
  203. * [31:20] - sensor value @ 25C
  204. * [19:8] - sensor value of hot
  205. * [7:0] - hot temperature value
  206. */
  207. n1 = val >> 20;
  208. n2 = (val & 0xfff00) >> 8;
  209. t2 = val & 0xff;
  210. t1 = 25; /* t1 always 25C */
  211. /*
  212. * Derived from linear interpolation,
  213. * Tmeas = T2 + (Nmeas - N2) * (T1 - T2) / (N1 - N2)
  214. * We want to reduce this down to the minimum computation necessary
  215. * for each temperature read. Also, we want Tmeas in millicelsius
  216. * and we don't want to lose precision from integer division. So...
  217. * milli_Tmeas = 1000 * T2 + 1000 * (Nmeas - N2) * (T1 - T2) / (N1 - N2)
  218. * Let constant c1 = 1000 * (T1 - T2) / (N1 - N2)
  219. * milli_Tmeas = (1000 * T2) + c1 * (Nmeas - N2)
  220. * milli_Tmeas = (1000 * T2) + (c1 * Nmeas) - (c1 * N2)
  221. * Let constant c2 = (1000 * T2) - (c1 * N2)
  222. * milli_Tmeas = c2 + (c1 * Nmeas)
  223. */
  224. data->c1 = 1000 * (t1 - t2) / (n1 - n2);
  225. data->c2 = 1000 * t2 - data->c1 * n2;
  226. return 0;
  227. }
  228. static int imx_thermal_probe(struct platform_device *pdev)
  229. {
  230. struct imx_thermal_data *data;
  231. struct cpumask clip_cpus;
  232. struct regmap *map;
  233. int ret;
  234. data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
  235. if (!data)
  236. return -ENOMEM;
  237. map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,tempmon");
  238. if (IS_ERR(map)) {
  239. ret = PTR_ERR(map);
  240. dev_err(&pdev->dev, "failed to get tempmon regmap: %d\n", ret);
  241. return ret;
  242. }
  243. data->tempmon = map;
  244. platform_set_drvdata(pdev, data);
  245. ret = imx_get_sensor_data(pdev);
  246. if (ret) {
  247. dev_err(&pdev->dev, "failed to get sensor data\n");
  248. return ret;
  249. }
  250. /* Make sure sensor is in known good state for measurements */
  251. regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_POWER_DOWN);
  252. regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_MEASURE_TEMP);
  253. regmap_write(map, TEMPSENSE1 + REG_CLR, TEMPSENSE1_MEASURE_FREQ);
  254. regmap_write(map, MISC0 + REG_SET, MISC0_REFTOP_SELBIASOFF);
  255. regmap_write(map, TEMPSENSE0 + REG_SET, TEMPSENSE0_POWER_DOWN);
  256. cpumask_set_cpu(0, &clip_cpus);
  257. data->cdev = cpufreq_cooling_register(&clip_cpus);
  258. if (IS_ERR(data->cdev)) {
  259. ret = PTR_ERR(data->cdev);
  260. dev_err(&pdev->dev,
  261. "failed to register cpufreq cooling device: %d\n", ret);
  262. return ret;
  263. }
  264. data->tz = thermal_zone_device_register("imx_thermal_zone",
  265. IMX_TRIP_NUM, 0, data,
  266. &imx_tz_ops, NULL,
  267. IMX_PASSIVE_DELAY,
  268. IMX_POLLING_DELAY);
  269. if (IS_ERR(data->tz)) {
  270. ret = PTR_ERR(data->tz);
  271. dev_err(&pdev->dev,
  272. "failed to register thermal zone device %d\n", ret);
  273. cpufreq_cooling_unregister(data->cdev);
  274. return ret;
  275. }
  276. data->mode = THERMAL_DEVICE_ENABLED;
  277. return 0;
  278. }
  279. static int imx_thermal_remove(struct platform_device *pdev)
  280. {
  281. struct imx_thermal_data *data = platform_get_drvdata(pdev);
  282. thermal_zone_device_unregister(data->tz);
  283. cpufreq_cooling_unregister(data->cdev);
  284. return 0;
  285. }
  286. #ifdef CONFIG_PM_SLEEP
  287. static int imx_thermal_suspend(struct device *dev)
  288. {
  289. struct imx_thermal_data *data = dev_get_drvdata(dev);
  290. struct regmap *map = data->tempmon;
  291. u32 val;
  292. regmap_read(map, TEMPSENSE0, &val);
  293. if ((val & TEMPSENSE0_POWER_DOWN) == 0) {
  294. /*
  295. * If a measurement is taking place, wait for a long enough
  296. * time for it to finish, and then check again. If it still
  297. * does not finish, something must go wrong.
  298. */
  299. udelay(50);
  300. regmap_read(map, TEMPSENSE0, &val);
  301. if ((val & TEMPSENSE0_POWER_DOWN) == 0)
  302. return -ETIMEDOUT;
  303. }
  304. return 0;
  305. }
  306. static int imx_thermal_resume(struct device *dev)
  307. {
  308. /* Nothing to do for now */
  309. return 0;
  310. }
  311. #endif
  312. static SIMPLE_DEV_PM_OPS(imx_thermal_pm_ops,
  313. imx_thermal_suspend, imx_thermal_resume);
  314. static const struct of_device_id of_imx_thermal_match[] = {
  315. { .compatible = "fsl,imx6q-tempmon", },
  316. { /* end */ }
  317. };
  318. static struct platform_driver imx_thermal = {
  319. .driver = {
  320. .name = "imx_thermal",
  321. .owner = THIS_MODULE,
  322. .pm = &imx_thermal_pm_ops,
  323. .of_match_table = of_imx_thermal_match,
  324. },
  325. .probe = imx_thermal_probe,
  326. .remove = imx_thermal_remove,
  327. };
  328. module_platform_driver(imx_thermal);
  329. MODULE_AUTHOR("Freescale Semiconductor, Inc.");
  330. MODULE_DESCRIPTION("Thermal driver for Freescale i.MX SoCs");
  331. MODULE_LICENSE("GPL v2");
  332. MODULE_ALIAS("platform:imx-thermal");