tegra-bpmp-thermal.c 6.4 KB

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