1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2015-2017, NVIDIA CORPORATION. All rights reserved.
6 * Mikko Perttunen <mperttunen@nvidia.com>
7 * Aapo Vienamo <avienamo@nvidia.com>
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>
16 #include <soc/tegra/bpmp.h>
17 #include <soc/tegra/bpmp-abi.h>
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
;
26 struct tegra_bpmp_thermal
{
28 struct tegra_bpmp
*bpmp
;
29 unsigned int num_zones
;
30 struct tegra_bpmp_thermal_zone
**zones
;
33 static int tegra_bpmp_thermal_get_temp(void *data
, int *out_temp
)
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
;
41 memset(&req
, 0, sizeof(req
));
42 req
.type
= CMD_THERMAL_GET_TEMP
;
43 req
.get_temp
.zone
= zone
->idx
;
45 memset(&msg
, 0, sizeof(msg
));
46 msg
.mrq
= MRQ_THERMAL
;
48 msg
.tx
.size
= sizeof(req
);
50 msg
.rx
.size
= sizeof(reply
);
52 err
= tegra_bpmp_transfer(zone
->tegra
->bpmp
, &msg
);
56 *out_temp
= reply
.get_temp
.temp
;
61 static int tegra_bpmp_thermal_set_trips(void *data
, int low
, int high
)
63 struct tegra_bpmp_thermal_zone
*zone
= data
;
64 struct mrq_thermal_host_to_bpmp_request req
;
65 struct tegra_bpmp_message msg
;
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
;
74 memset(&msg
, 0, sizeof(msg
));
75 msg
.mrq
= MRQ_THERMAL
;
77 msg
.tx
.size
= sizeof(req
);
79 return tegra_bpmp_transfer(zone
->tegra
->bpmp
, &msg
);
82 static void tz_device_update_work_fn(struct work_struct
*work
)
84 struct tegra_bpmp_thermal_zone
*zone
;
86 zone
= container_of(work
, struct tegra_bpmp_thermal_zone
,
87 tz_device_update_work
);
89 thermal_zone_device_update(zone
->tzd
, THERMAL_TRIP_VIOLATED
);
92 static void bpmp_mrq_thermal(unsigned int mrq
, struct tegra_bpmp_channel
*ch
,
95 struct mrq_thermal_bpmp_to_host_request
*req
;
96 struct tegra_bpmp_thermal
*tegra
= data
;
99 req
= (struct mrq_thermal_bpmp_to_host_request
*)ch
->ib
->data
;
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);
108 for (i
= 0; i
< tegra
->num_zones
; ++i
) {
109 if (tegra
->zones
[i
]->idx
!= req
->host_trip_reached
.zone
)
112 schedule_work(&tegra
->zones
[i
]->tz_device_update_work
);
113 tegra_bpmp_mrq_return(ch
, 0, NULL
, 0);
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);
122 static int tegra_bpmp_thermal_get_num_zones(struct tegra_bpmp
*bpmp
,
125 struct mrq_thermal_host_to_bpmp_request req
;
126 union mrq_thermal_bpmp_to_host_response reply
;
127 struct tegra_bpmp_message msg
;
130 memset(&req
, 0, sizeof(req
));
131 req
.type
= CMD_THERMAL_GET_NUM_ZONES
;
133 memset(&msg
, 0, sizeof(msg
));
134 msg
.mrq
= MRQ_THERMAL
;
136 msg
.tx
.size
= sizeof(req
);
137 msg
.rx
.data
= &reply
;
138 msg
.rx
.size
= sizeof(reply
);
140 err
= tegra_bpmp_transfer(bpmp
, &msg
);
144 *num_zones
= reply
.get_num_zones
.num
;
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
,
154 static int tegra_bpmp_thermal_probe(struct platform_device
*pdev
)
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
;
162 tegra
= devm_kzalloc(&pdev
->dev
, sizeof(*tegra
), GFP_KERNEL
);
166 tegra
->dev
= &pdev
->dev
;
169 err
= tegra_bpmp_thermal_get_num_zones(bpmp
, &max_num_zones
);
171 dev_err(&pdev
->dev
, "failed to get the number of zones: %d\n",
176 tegra
->zones
= devm_kcalloc(&pdev
->dev
, max_num_zones
,
177 sizeof(*tegra
->zones
), GFP_KERNEL
);
181 for (i
= 0; i
< max_num_zones
; ++i
) {
182 struct tegra_bpmp_thermal_zone
*zone
;
185 zone
= devm_kzalloc(&pdev
->dev
, sizeof(*zone
), GFP_KERNEL
);
192 err
= tegra_bpmp_thermal_get_temp(zone
, &temp
);
194 devm_kfree(&pdev
->dev
, zone
);
198 tzd
= devm_thermal_zone_of_sensor_register(
199 &pdev
->dev
, i
, zone
, &tegra_bpmp_of_thermal_ops
);
201 if (PTR_ERR(tzd
) == -EPROBE_DEFER
)
202 return -EPROBE_DEFER
;
203 devm_kfree(&pdev
->dev
, zone
);
208 INIT_WORK(&zone
->tz_device_update_work
,
209 tz_device_update_work_fn
);
211 tegra
->zones
[tegra
->num_zones
++] = zone
;
214 err
= tegra_bpmp_request_mrq(bpmp
, MRQ_THERMAL
, bpmp_mrq_thermal
,
217 dev_err(&pdev
->dev
, "failed to register mrq handler: %d\n",
222 platform_set_drvdata(pdev
, tegra
);
227 static int tegra_bpmp_thermal_remove(struct platform_device
*pdev
)
229 struct tegra_bpmp_thermal
*tegra
= platform_get_drvdata(pdev
);
231 tegra_bpmp_free_mrq(tegra
->bpmp
, MRQ_THERMAL
, tegra
);
236 static const struct of_device_id tegra_bpmp_thermal_of_match
[] = {
237 { .compatible
= "nvidia,tegra186-bpmp-thermal" },
240 MODULE_DEVICE_TABLE(of
, tegra_bpmp_thermal_of_match
);
242 static struct platform_driver tegra_bpmp_thermal_driver
= {
243 .probe
= tegra_bpmp_thermal_probe
,
244 .remove
= tegra_bpmp_thermal_remove
,
246 .name
= "tegra-bpmp-thermal",
247 .of_match_table
= tegra_bpmp_thermal_of_match
,
250 module_platform_driver(tegra_bpmp_thermal_driver
);
252 MODULE_AUTHOR("Mikko Perttunen <mperttunen@nvidia.com>");
253 MODULE_DESCRIPTION("NVIDIA Tegra BPMP thermal sensor driver");
254 MODULE_LICENSE("GPL v2");