tegra-bpmp-thermal.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. /*
  2. * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved.
  3. *
  4. * Author:
  5. * Mikko Perttunen <mperttunen@nvidia.com>
  6. * Aapo Vienamo <avienamo@nvidia.com>
  7. *
  8. * This software is licensed under the terms of the GNU General Public
  9. * License version 2, as published by the Free Software Foundation, and
  10. * may be copied, distributed, and modified under those terms.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. */
  18. #include <linux/err.h>
  19. #include <linux/module.h>
  20. #include <linux/platform_device.h>
  21. #include <linux/thermal.h>
  22. #include <linux/workqueue.h>
  23. #include <soc/tegra/bpmp.h>
  24. #include <soc/tegra/bpmp-abi.h>
  25. struct tegra_bpmp_thermal_zone {
  26. struct tegra_bpmp_thermal *tegra;
  27. struct thermal_zone_device *tzd;
  28. struct work_struct tz_device_update_work;
  29. unsigned int idx;
  30. };
  31. struct tegra_bpmp_thermal {
  32. struct device *dev;
  33. struct tegra_bpmp *bpmp;
  34. unsigned int num_zones;
  35. struct tegra_bpmp_thermal_zone **zones;
  36. };
  37. static int tegra_bpmp_thermal_get_temp(void *data, int *out_temp)
  38. {
  39. struct tegra_bpmp_thermal_zone *zone = data;
  40. struct mrq_thermal_host_to_bpmp_request req;
  41. union mrq_thermal_bpmp_to_host_response reply;
  42. struct tegra_bpmp_message msg;
  43. int err;
  44. memset(&req, 0, sizeof(req));
  45. req.type = CMD_THERMAL_GET_TEMP;
  46. req.get_temp.zone = zone->idx;
  47. memset(&msg, 0, sizeof(msg));
  48. msg.mrq = MRQ_THERMAL;
  49. msg.tx.data = &req;
  50. msg.tx.size = sizeof(req);
  51. msg.rx.data = &reply;
  52. msg.rx.size = sizeof(reply);
  53. err = tegra_bpmp_transfer(zone->tegra->bpmp, &msg);
  54. if (err)
  55. return err;
  56. *out_temp = reply.get_temp.temp;
  57. return 0;
  58. }
  59. static int tegra_bpmp_thermal_set_trips(void *data, int low, int high)
  60. {
  61. struct tegra_bpmp_thermal_zone *zone = data;
  62. struct mrq_thermal_host_to_bpmp_request req;
  63. struct tegra_bpmp_message msg;
  64. memset(&req, 0, sizeof(req));
  65. req.type = CMD_THERMAL_SET_TRIP;
  66. req.set_trip.zone = zone->idx;
  67. req.set_trip.enabled = true;
  68. req.set_trip.low = low;
  69. req.set_trip.high = high;
  70. memset(&msg, 0, sizeof(msg));
  71. msg.mrq = MRQ_THERMAL;
  72. msg.tx.data = &req;
  73. msg.tx.size = sizeof(req);
  74. return tegra_bpmp_transfer(zone->tegra->bpmp, &msg);
  75. }
  76. static void tz_device_update_work_fn(struct work_struct *work)
  77. {
  78. struct tegra_bpmp_thermal_zone *zone;
  79. zone = container_of(work, struct tegra_bpmp_thermal_zone,
  80. tz_device_update_work);
  81. thermal_zone_device_update(zone->tzd, THERMAL_TRIP_VIOLATED);
  82. }
  83. static void bpmp_mrq_thermal(unsigned int mrq, struct tegra_bpmp_channel *ch,
  84. void *data)
  85. {
  86. struct mrq_thermal_bpmp_to_host_request *req;
  87. struct tegra_bpmp_thermal *tegra = data;
  88. int i;
  89. req = (struct mrq_thermal_bpmp_to_host_request *)ch->ib->data;
  90. if (req->type != CMD_THERMAL_HOST_TRIP_REACHED) {
  91. dev_err(tegra->dev, "%s: invalid request type: %d\n",
  92. __func__, req->type);
  93. tegra_bpmp_mrq_return(ch, -EINVAL, NULL, 0);
  94. return;
  95. }
  96. for (i = 0; i < tegra->num_zones; ++i) {
  97. if (tegra->zones[i]->idx != req->host_trip_reached.zone)
  98. continue;
  99. schedule_work(&tegra->zones[i]->tz_device_update_work);
  100. tegra_bpmp_mrq_return(ch, 0, NULL, 0);
  101. return;
  102. }
  103. dev_err(tegra->dev, "%s: invalid thermal zone: %d\n", __func__,
  104. req->host_trip_reached.zone);
  105. tegra_bpmp_mrq_return(ch, -EINVAL, NULL, 0);
  106. }
  107. static int tegra_bpmp_thermal_get_num_zones(struct tegra_bpmp *bpmp,
  108. int *num_zones)
  109. {
  110. struct mrq_thermal_host_to_bpmp_request req;
  111. union mrq_thermal_bpmp_to_host_response reply;
  112. struct tegra_bpmp_message msg;
  113. int err;
  114. memset(&req, 0, sizeof(req));
  115. req.type = CMD_THERMAL_GET_NUM_ZONES;
  116. memset(&msg, 0, sizeof(msg));
  117. msg.mrq = MRQ_THERMAL;
  118. msg.tx.data = &req;
  119. msg.tx.size = sizeof(req);
  120. msg.rx.data = &reply;
  121. msg.rx.size = sizeof(reply);
  122. err = tegra_bpmp_transfer(bpmp, &msg);
  123. if (err)
  124. return err;
  125. *num_zones = reply.get_num_zones.num;
  126. return 0;
  127. }
  128. static const struct thermal_zone_of_device_ops tegra_bpmp_of_thermal_ops = {
  129. .get_temp = tegra_bpmp_thermal_get_temp,
  130. .set_trips = tegra_bpmp_thermal_set_trips,
  131. };
  132. static int tegra_bpmp_thermal_probe(struct platform_device *pdev)
  133. {
  134. struct tegra_bpmp *bpmp = dev_get_drvdata(pdev->dev.parent);
  135. struct tegra_bpmp_thermal *tegra;
  136. struct thermal_zone_device *tzd;
  137. unsigned int i, max_num_zones;
  138. int err;
  139. tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
  140. if (!tegra)
  141. return -ENOMEM;
  142. tegra->dev = &pdev->dev;
  143. tegra->bpmp = bpmp;
  144. err = tegra_bpmp_thermal_get_num_zones(bpmp, &max_num_zones);
  145. if (err) {
  146. dev_err(&pdev->dev, "failed to get the number of zones: %d\n",
  147. err);
  148. return err;
  149. }
  150. tegra->zones = devm_kcalloc(&pdev->dev, max_num_zones,
  151. sizeof(*tegra->zones), GFP_KERNEL);
  152. if (!tegra->zones)
  153. return -ENOMEM;
  154. for (i = 0; i < max_num_zones; ++i) {
  155. struct tegra_bpmp_thermal_zone *zone;
  156. int temp;
  157. zone = devm_kzalloc(&pdev->dev, sizeof(*zone), GFP_KERNEL);
  158. if (!zone)
  159. return -ENOMEM;
  160. zone->idx = i;
  161. zone->tegra = tegra;
  162. err = tegra_bpmp_thermal_get_temp(zone, &temp);
  163. if (err < 0) {
  164. devm_kfree(&pdev->dev, zone);
  165. continue;
  166. }
  167. tzd = devm_thermal_zone_of_sensor_register(
  168. &pdev->dev, i, zone, &tegra_bpmp_of_thermal_ops);
  169. if (IS_ERR(tzd)) {
  170. if (PTR_ERR(tzd) == -EPROBE_DEFER)
  171. return -EPROBE_DEFER;
  172. devm_kfree(&pdev->dev, zone);
  173. continue;
  174. }
  175. zone->tzd = tzd;
  176. INIT_WORK(&zone->tz_device_update_work,
  177. tz_device_update_work_fn);
  178. tegra->zones[tegra->num_zones++] = zone;
  179. }
  180. err = tegra_bpmp_request_mrq(bpmp, MRQ_THERMAL, bpmp_mrq_thermal,
  181. tegra);
  182. if (err) {
  183. dev_err(&pdev->dev, "failed to register mrq handler: %d\n",
  184. err);
  185. return err;
  186. }
  187. platform_set_drvdata(pdev, tegra);
  188. return 0;
  189. }
  190. static int tegra_bpmp_thermal_remove(struct platform_device *pdev)
  191. {
  192. struct tegra_bpmp_thermal *tegra = platform_get_drvdata(pdev);
  193. tegra_bpmp_free_mrq(tegra->bpmp, MRQ_THERMAL, tegra);
  194. return 0;
  195. }
  196. static const struct of_device_id tegra_bpmp_thermal_of_match[] = {
  197. { .compatible = "nvidia,tegra186-bpmp-thermal" },
  198. { },
  199. };
  200. MODULE_DEVICE_TABLE(of, tegra_bpmp_thermal_of_match);
  201. static struct platform_driver tegra_bpmp_thermal_driver = {
  202. .probe = tegra_bpmp_thermal_probe,
  203. .remove = tegra_bpmp_thermal_remove,
  204. .driver = {
  205. .name = "tegra-bpmp-thermal",
  206. .of_match_table = tegra_bpmp_thermal_of_match,
  207. },
  208. };
  209. module_platform_driver(tegra_bpmp_thermal_driver);
  210. MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
  211. MODULE_DESCRIPTION("NVIDIA Tegra BPMP thermal sensor driver");
  212. MODULE_LICENSE("GPL v2");