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