cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tegra-bpmp-thermal.c (6362B)


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